From 44e78a95dc1f0d2d4a16794feb590d05df0f2e0a Mon Sep 17 00:00:00 2001 From: Nisha Yerunkar Date: Tue, 26 Nov 2024 11:11:15 -0800 Subject: [PATCH] [Interactive Graph Editor] Stop cursor jumps in number input fields (#1912) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary: In order to stop cursor jumps in number fields, up I'm updating the ScrolllessNumberTextField component to have part of the implementation of EditableControlledInput, which makes sure that the input stays consistent until blurred. Issue: https://khanacademy.atlassian.net/browse/LEMS-2303 ## Test plan: - Go to http://localhost:6006/?path=/story/perseuseditor-widgets-interactive-graph--interactive-graph-segments - Scroll down to the start coords for the segments - Clear an input and type in ".5" - Confirm that it does NOT auto-update to 0.5 immediately - Confirm that it auto-updates to 0.5 after moving focus off the input - Type in "1.5" - Move the cursor right before the "." - Press backspace - Confirm that the cursor does not jump ## Before and after demos Before https://github.com/user-attachments/assets/66c0e69d-ed12-45a7-9527-26854a676ae7 After https://github.com/user-attachments/assets/a1e396cf-7b62-43d4-8e00-006b78e6c69a Author: nishasy Reviewers: benchristel, catandthemachines, anakaren-rojas Required Reviewers: Approved By: benchristel Checks: ✅ Publish Storybook to Chromatic (ubuntu-latest, 20.x), ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ Cypress (ubuntu-latest, 20.x), ✅ Check builds for changes in size (ubuntu-latest, 20.x), ✅ Lint, Typecheck, Format, and Test (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ gerald Pull Request URL: https://github.com/Khan/perseus/pull/1912 --- .changeset/neat-kiwis-relax.md | 5 +++ .../scrollless-number-text-field.test.tsx | 41 ++++++++++++++++++ .../scrollless-number-text-field.tsx | 42 ++++++++++++++++--- .../locked-figures/locked-point-settings.tsx | 2 + 4 files changed, 85 insertions(+), 5 deletions(-) create mode 100644 .changeset/neat-kiwis-relax.md diff --git a/.changeset/neat-kiwis-relax.md b/.changeset/neat-kiwis-relax.md new file mode 100644 index 0000000000..84fc5e2d02 --- /dev/null +++ b/.changeset/neat-kiwis-relax.md @@ -0,0 +1,5 @@ +--- +"@khanacademy/perseus-editor": patch +--- + +[Interactive Graph Editor] Stop cursor jumps in number input fields diff --git a/packages/perseus-editor/src/components/__tests__/scrollless-number-text-field.test.tsx b/packages/perseus-editor/src/components/__tests__/scrollless-number-text-field.test.tsx index 1bce852dff..cdacd9d082 100644 --- a/packages/perseus-editor/src/components/__tests__/scrollless-number-text-field.test.tsx +++ b/packages/perseus-editor/src/components/__tests__/scrollless-number-text-field.test.tsx @@ -53,4 +53,45 @@ describe("ScrolllessNumberTextField", () => { // Assert expect(onChange).not.toHaveBeenCalled(); }); + + test("calls onFocus on focus", async () => { + // Arrange + const onFocus = jest.fn(); + render( + {}} + onFocus={onFocus} + />, + ); + + // Act + // Tab to focus on input + await userEvent.tab(); + + // Assert + expect(onFocus).toHaveBeenCalled(); + }); + + test("calls onBlur on blur", async () => { + // Arrange + const onBlur = jest.fn(); + render( + {}} + onBlur={onBlur} + />, + ); + + // Tab to focus on input + await userEvent.tab(); + + // Act + // Tab to move focus away + await userEvent.tab(); + + // Assert + expect(onBlur).toHaveBeenCalled(); + }); }); diff --git a/packages/perseus-editor/src/components/scrollless-number-text-field.tsx b/packages/perseus-editor/src/components/scrollless-number-text-field.tsx index 53553c0e64..60f9078e0d 100644 --- a/packages/perseus-editor/src/components/scrollless-number-text-field.tsx +++ b/packages/perseus-editor/src/components/scrollless-number-text-field.tsx @@ -6,8 +6,14 @@ import type {PropsFor} from "@khanacademy/wonder-blocks-core"; /** * This is a custom text field of type="number" for use in Perseus Editors. * - * This makes it so that the text field's input number updates on scroll - * without scrolling the page. + * This component makes it so that + * 1. the text field's input number updates on scroll without + * scrolling the page. + * 2. the input is controlled as long as it does not have focus. + * While it is focused, it becomes editable and emits onChange + * events. This is useful to make sure that input behavior + * remains predictable, rather than possibly having the cursor + * jump around uenxpectedly. * * NOTE 1: Native HTML number inputs do not update the number value on scroll, * they only scroll the page. Inputs in React do NOT work this way (explanation @@ -16,10 +22,14 @@ import type {PropsFor} from "@khanacademy/wonder-blocks-core"; * the page to scroll. The behavior in this component is an improvement on * the React behavior, but it's the opposite of the native HTML behavior. * - * NOTE 2: Firefox seems to have a custom override for this. Even with this - * stopPropogation, Firefox matches the native HTML behavior. + * NOTE 2: Firefox seems to have a custom override for input scroll. Even + * with this stopPropogation, Firefox matches the native HTML behavior. */ const ScrolllessNumberTextField = (props: PropsFor) => { + const {value, onChange, ...restOfProps} = props; + const [focused, setFocused] = React.useState(false); + const [wipValue, setWipValue] = React.useState(""); + const inputRef = React.useRef(null); React.useEffect(() => { @@ -39,7 +49,29 @@ const ScrolllessNumberTextField = (props: PropsFor) => { }; }, [inputRef]); - return ; + return ( + { + setWipValue(newValue); + onChange(newValue); + }} + onFocus={(e) => { + setWipValue(value); + setFocused(true); + + props.onFocus?.(e); + }} + onBlur={(e) => { + setFocused(false); + + props.onBlur?.(e); + }} + ref={inputRef} + /> + ); }; export default ScrolllessNumberTextField; diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-point-settings.tsx b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-point-settings.tsx index 103a74c783..b82dc3f938 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-point-settings.tsx +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-point-settings.tsx @@ -98,6 +98,7 @@ const LockedPointSettings = (props: Props) => { onRemove, // defining point props showPoint, + error, expanded, onTogglePoint, onToggle, @@ -210,6 +211,7 @@ const LockedPointSettings = (props: Props) => { coord={coord} style={styles.spaceUnder} onChange={handleCoordChange} + error={!!error} /> {/* Toggle switch */}