Skip to content

Conversation

@SonicScrewdriver
Copy link
Contributor

@SonicScrewdriver SonicScrewdriver commented Aug 6, 2025

Summary:

This [DRAFT] PR is currently a proof of concept. I'm testing the results upstream, but please feel free to leave any comments or concerns about my approach.

The main goals of this PR are to:

  1. Remove as many usages of findDOMNode as possible
  2. Clean up string refs wherever related to these usages of findDOMNode
  3. Improve any typing to be more specific, in an effort to slowly clean up our need for overrides

Changes Made

  • SimpleKeypadInput: Converted to forwardRef functional component with useImperativeHandle to expose DOM node directly
  • Table, Matrix, NumberLine widgets: Replaced string refs and findDOMNode calls with modern callback/object ref patterns
  • Type improvements: Updated input ref types and removed unnecessary type cast in NumericInput

Issue: LEMS-XXXX

Test plan:

  • Tests pass
  • Manual testing upstream
  • Upcoming QA pass

@SonicScrewdriver SonicScrewdriver self-assigned this Aug 6, 2025
@SonicScrewdriver SonicScrewdriver changed the title docs(changeset): Remove unused code and usages of findDOMNode [DRAFT - Testing Spiciness]: Remove unused code and usages of findDOMNode Aug 6, 2025
@github-actions github-actions bot added schema-change Attached to PRs when we detect Perseus Schema changes in it item-splitting-change and removed schema-change Attached to PRs when we detect Perseus Schema changes in it labels Aug 6, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Aug 6, 2025

🗄️ Schema Change: No Changes ✅

@github-actions
Copy link
Contributor

github-actions bot commented Aug 6, 2025

Size Change: -165 B (-0.03%)

Total Size: 496 kB

Filename Size Change
packages/perseus/dist/es/index.js 207 kB -165 B (-0.08%)
ℹ️ View Unchanged
Filename Size
packages/kas/dist/es/index.js 20.7 kB
packages/keypad-context/dist/es/index.js 1 kB
packages/kmath/dist/es/index.js 5.98 kB
packages/math-input/dist/es/index.js 98.6 kB
packages/math-input/dist/es/strings.js 1.61 kB
packages/perseus-core/dist/es/index.item-splitting.js 13.1 kB
packages/perseus-core/dist/es/index.js 21.7 kB
packages/perseus-editor/dist/es/index.js 94.5 kB
packages/perseus-linter/dist/es/index.js 7.07 kB
packages/perseus-score/dist/es/index.js 9.34 kB
packages/perseus-utils/dist/es/index.js 403 B
packages/perseus/dist/es/strings.js 7.56 kB
packages/pure-markdown/dist/es/index.js 1.39 kB
packages/simple-markdown/dist/es/index.js 6.71 kB

compressed-size-action

@github-actions
Copy link
Contributor

github-actions bot commented Aug 6, 2025

🛠️ Item Splitting: No Changes ✅

@github-actions
Copy link
Contributor

github-actions bot commented Aug 6, 2025

npm Snapshot: Published

Good news!! We've packaged up the latest commit from this PR (83156df) and published it to npm. You
can install it using the tag PR2769.

Example:

pnpm add @khanacademy/perseus@PR2769

If you are working in Khan Academy's frontend, you can run the below command.

./dev/tools/bump_perseus_version.ts -t PR2769

If you are working in Khan Academy's webapp, you can run the below command.

./dev/tools/bump_perseus_version.js -t PR2769

<SimpleKeypadInput
{...inputProps}
style={style}
scrollable={true}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nonsense property that doesn't exist, so I've removed it! We simply didn't catch it before as SimpleKeypadInput wasn't very specific about props

static contextType = PerseusI18nContext;
declare context: React.ContextType<typeof PerseusI18nContext>;

tickControlRef: HTMLInputElement | null = null;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's only ever one input in NumberLine, so I figured I'd simplify this accordingly.


// There's only one input path for the tick control, but the renderer
// expects this method to be implemented.
getInputPaths: () => ReadonlyArray<ReadonlyArray<string>> = () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since these methods are part of our our main renderer logic, I didn't want to mess with them too much.

I think improving this will require a more holistic and modern approach to our focus handling across Perseus (perhaps a context?).

@SonicScrewdriver SonicScrewdriver force-pushed the cleaning-the-perseus-finddom branch from 6010d1f to 10b363c Compare August 6, 2025 20:34
const inputID = getRefForPath(getInputPath(row, col));
const curInput = this.answerRefs[inputID];
// @ts-expect-error - TS2339 - Property 'getStringValue' does not exist on type 'ReactInstance'.
const curValueString = curInput.getStringValue();
Copy link
Contributor Author

@SonicScrewdriver SonicScrewdriver Aug 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't remove these ts-expect errors as the SimpleKeypadInput never supported these methods, although our TextInput does. (getStringValue, getSelectionStart, getSelectionEnd).

This means that none of this keyboard logic was ever working for our mobile users. However, they were also very unlikely to hit this as it requires hitting specific keys on their mobile device's keyboard.

@github-actions github-actions bot added item-splitting-change schema-change Attached to PRs when we detect Perseus Schema changes in it and removed schema-change Attached to PRs when we detect Perseus Schema changes in it item-splitting-change labels Aug 7, 2025
} else {
// @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'focus' does not exist on type 'Element | Text'.
ReactDOM.findDOMNode(inputComponent).focus();
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the section of code that was able to be cleaned up by converting SimpleKeypadInput into the forward ref functional component

@SonicScrewdriver SonicScrewdriver changed the title [DRAFT - Testing Spiciness]: Remove unused code and usages of findDOMNode [DRAFT]: Remove unused code and usages of findDOMNode Aug 7, 2025
ref="tick-ctrl"
ref={(ref) => {
this.tickControlRef = ref;
}}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooo I can simplify this

In my testing and investigations, I simply cannot find any case where this fallback is called.
if (interWidgetPath.length === 0) {
// @ts-expect-error - TS2345 - Argument of type 'Widget | null | undefined' is not assignable to parameter of type 'ReactInstance | null | undefined'.
return ReactDOM.findDOMNode(widget);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only ever seem to hit line 1536 when we are in a Graded Group, but the length is never zero. I've been unable to find any cases where we ever actually need this fallback. I tested every widget in storybook in both desktop/mobile views. The only place we ever satisfy this condition is when we're explicitly forcing the situation in tests.

Copy link
Contributor Author

@SonicScrewdriver SonicScrewdriver Aug 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm after further testing, I can't really seem to see any regressions from removing this entirely. All tests pass in both Perseus and in frontend, and I haven't noticed anything while testing every single widget. I'll make sure to get a full QA pass, but it'd be nice to greatly simplify all of this.

"![alttext](https://cdn.kastatic.org/ka-content-images/9cb2cf618c16501d01abf97036deb355d9393949.png)",
"![alttext](https://cdn.kastatic.org/ka-content-images/9cb2cf618c16501d01abf97036deb355d9393949.png)",
"![alttext](https://cdn.kastatic.org/ka-content-images/9cb2cf618c16501d01abf97036deb355d9393949.png)![alttext](https://cdn.kastatic.org/ka-content-images/9cb2cf618c16501d01abf97036deb355d9393949.png)",
],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted a story with images in a sorter, to see if there were any regressions. It seems to work identically!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants