From 7d64ca93bf75b3050722dc2e885b172c4c53b170 Mon Sep 17 00:00:00 2001 From: Cat Johnson Date: Wed, 26 Jun 2024 16:55:40 -0700 Subject: [PATCH 01/17] Attempting to break expression widget tabbing. --- .../src/components/input/math-input.tsx | 8 +++---- .../src/components/keypad/keypad.tsx | 1 - .../math-input/src/components/tabbar/item.tsx | 9 +------- .../src/components/tabbar/tabbar.tsx | 4 ++-- .../perseus/src/components/math-input.tsx | 1 - packages/perseus/src/perseus-markdown.tsx | 2 +- packages/perseus/src/widgets/expression.tsx | 21 +++++++++++-------- 7 files changed, 20 insertions(+), 26 deletions(-) diff --git a/packages/math-input/src/components/input/math-input.tsx b/packages/math-input/src/components/input/math-input.tsx index 2f136f32f8..09da57d9b5 100644 --- a/packages/math-input/src/components/input/math-input.tsx +++ b/packages/math-input/src/components/input/math-input.tsx @@ -684,12 +684,12 @@ class MathInput extends React.Component { // If we're already focused, but the keypad isn't active, activate it. if (this.state.focused && !keypadActive) { - setKeypadActive(true); + //setKeypadActive(true); } // Trigger a focus event, if we're not already focused. if (!this.state.focused) { - this.focus(setKeypadActive); + //this.focus(setKeypadActive); } // If the user clicked on the input using a mouse or tap gesture, @@ -697,7 +697,7 @@ class MathInput extends React.Component { // event will be triggered when the user types on the keyboard. // This is necessary to support Chromebooks as they use mobile user // agents, do not simulate touch events, and have physical keyboards. - this.inputRef?.focus(); + //this.inputRef?.focus(); }; handleTouchMove: (arg1: React.TouchEvent) => void = (e) => { @@ -998,7 +998,7 @@ class MathInput extends React.Component { this.inputRef = node; }} onFocus={() => { - this.focus(setKeypadActive); + //this.focus(setKeypadActive); }} onBlur={this.blur} onKeyUp={this.handleKeyUp} diff --git a/packages/math-input/src/components/keypad/keypad.tsx b/packages/math-input/src/components/keypad/keypad.tsx index 4aaf90f145..4e21539b2e 100644 --- a/packages/math-input/src/components/keypad/keypad.tsx +++ b/packages/math-input/src/components/keypad/keypad.tsx @@ -151,7 +151,6 @@ export default function Keypad(props: Props) { {selectedPage === "Fractions" && ( diff --git a/packages/math-input/src/components/tabbar/item.tsx b/packages/math-input/src/components/tabbar/item.tsx index d51bd63b95..4f13e20364 100644 --- a/packages/math-input/src/components/tabbar/item.tsx +++ b/packages/math-input/src/components/tabbar/item.tsx @@ -85,14 +85,7 @@ class TabbarItem extends React.Component { render(): React.ReactNode { const {onClick, itemType, itemState} = this.props; return ( - + {({hovered, focused, pressed}) => { const tintColor = imageTintColor( itemState, diff --git a/packages/math-input/src/components/tabbar/tabbar.tsx b/packages/math-input/src/components/tabbar/tabbar.tsx index 0d9a3cd2ac..9e5517ec28 100644 --- a/packages/math-input/src/components/tabbar/tabbar.tsx +++ b/packages/math-input/src/components/tabbar/tabbar.tsx @@ -33,8 +33,8 @@ function Tabbar(props: Props): React.ReactElement { const {items, onClickClose, selectedItem, onSelectItem, style} = props; return ( - - + + {items.map((item) => ( { render(): React.ReactNode { let className = classNames({ "perseus-math-input": true, - // mathquill usually adds these itself but react removes them when // updating the component. "mq-editable-field": true, diff --git a/packages/perseus/src/perseus-markdown.tsx b/packages/perseus/src/perseus-markdown.tsx index 47be9731b1..8d5cf3d57d 100644 --- a/packages/perseus/src/perseus-markdown.tsx +++ b/packages/perseus/src/perseus-markdown.tsx @@ -63,7 +63,7 @@ const rules = { return table; } - return React.cloneElement(table, {tabIndex: 0}); + return React.cloneElement(table); }, }, // This is pretty much horrible, but we have a regex here to capture an diff --git a/packages/perseus/src/widgets/expression.tsx b/packages/perseus/src/widgets/expression.tsx index 8318ae7097..cf4d4f433c 100644 --- a/packages/perseus/src/widgets/expression.tsx +++ b/packages/perseus/src/widgets/expression.tsx @@ -539,7 +539,9 @@ export class Expression extends React.Component { render(): | React.ReactNode | React.ReactElement> { + console.log("render Expression"); if (this.props.apiOptions.customKeypad) { + console.log("custom keypad"); return ( { // this.props.keypadElement should always be set // when apiOptions.customKeypad is set, but how // to convince TypeScript of this? - this.props.keypadElement?.configure( - this.props.keypadConfiguration, - () => { - if (this._isMounted) { - this._handleFocus(); - } - }, - ); + // this.props.keypadElement?.configure( + // this.props.keypadConfiguration, + // () => { + // if (this._isMounted) { + // this._handleFocus(); + // } + // }, + // ); }} onBlur={this._handleBlur} /> @@ -572,6 +574,7 @@ export class Expression extends React.Component { const {ERROR_MESSAGE, ERROR_TITLE} = this.context.strings; + console.log("render MathInput"); return (
{ onChange={this.changeAndTrack} convertDotToTimes={this.props.times} buttonSets={this.props.buttonSets} - onFocus={this._handleFocus} + //onFocus={this._handleFocus} onBlur={this._handleBlur} hasError={this.state.showErrorStyle} extraKeys={this.props.keypadConfiguration?.extraKeys} From 00d0639dc487ad06bac3609344715e29bd2ea771 Mon Sep 17 00:00:00 2001 From: Cat Johnson Date: Thu, 27 Jun 2024 17:29:46 -0700 Subject: [PATCH 02/17] Undoing testing code. --- .../src/components/input/math-input.tsx | 8 +++---- .../math-input/src/components/tabbar/item.tsx | 9 +++++++- .../src/components/tabbar/tabbar.tsx | 4 ++-- .../perseus/src/components/math-input.tsx | 1 + packages/perseus/src/perseus-markdown.tsx | 2 +- packages/perseus/src/widgets/expression.tsx | 21 ++++++++----------- 6 files changed, 25 insertions(+), 20 deletions(-) diff --git a/packages/math-input/src/components/input/math-input.tsx b/packages/math-input/src/components/input/math-input.tsx index 09da57d9b5..2f136f32f8 100644 --- a/packages/math-input/src/components/input/math-input.tsx +++ b/packages/math-input/src/components/input/math-input.tsx @@ -684,12 +684,12 @@ class MathInput extends React.Component { // If we're already focused, but the keypad isn't active, activate it. if (this.state.focused && !keypadActive) { - //setKeypadActive(true); + setKeypadActive(true); } // Trigger a focus event, if we're not already focused. if (!this.state.focused) { - //this.focus(setKeypadActive); + this.focus(setKeypadActive); } // If the user clicked on the input using a mouse or tap gesture, @@ -697,7 +697,7 @@ class MathInput extends React.Component { // event will be triggered when the user types on the keyboard. // This is necessary to support Chromebooks as they use mobile user // agents, do not simulate touch events, and have physical keyboards. - //this.inputRef?.focus(); + this.inputRef?.focus(); }; handleTouchMove: (arg1: React.TouchEvent) => void = (e) => { @@ -998,7 +998,7 @@ class MathInput extends React.Component { this.inputRef = node; }} onFocus={() => { - //this.focus(setKeypadActive); + this.focus(setKeypadActive); }} onBlur={this.blur} onKeyUp={this.handleKeyUp} diff --git a/packages/math-input/src/components/tabbar/item.tsx b/packages/math-input/src/components/tabbar/item.tsx index 4f13e20364..d51bd63b95 100644 --- a/packages/math-input/src/components/tabbar/item.tsx +++ b/packages/math-input/src/components/tabbar/item.tsx @@ -85,7 +85,14 @@ class TabbarItem extends React.Component { render(): React.ReactNode { const {onClick, itemType, itemState} = this.props; return ( - + {({hovered, focused, pressed}) => { const tintColor = imageTintColor( itemState, diff --git a/packages/math-input/src/components/tabbar/tabbar.tsx b/packages/math-input/src/components/tabbar/tabbar.tsx index 9e5517ec28..0d9a3cd2ac 100644 --- a/packages/math-input/src/components/tabbar/tabbar.tsx +++ b/packages/math-input/src/components/tabbar/tabbar.tsx @@ -33,8 +33,8 @@ function Tabbar(props: Props): React.ReactElement { const {items, onClickClose, selectedItem, onSelectItem, style} = props; return ( - - + + {items.map((item) => ( { render(): React.ReactNode { let className = classNames({ "perseus-math-input": true, + // mathquill usually adds these itself but react removes them when // updating the component. "mq-editable-field": true, diff --git a/packages/perseus/src/perseus-markdown.tsx b/packages/perseus/src/perseus-markdown.tsx index 8d5cf3d57d..47be9731b1 100644 --- a/packages/perseus/src/perseus-markdown.tsx +++ b/packages/perseus/src/perseus-markdown.tsx @@ -63,7 +63,7 @@ const rules = { return table; } - return React.cloneElement(table); + return React.cloneElement(table, {tabIndex: 0}); }, }, // This is pretty much horrible, but we have a regex here to capture an diff --git a/packages/perseus/src/widgets/expression.tsx b/packages/perseus/src/widgets/expression.tsx index cf4d4f433c..8318ae7097 100644 --- a/packages/perseus/src/widgets/expression.tsx +++ b/packages/perseus/src/widgets/expression.tsx @@ -539,9 +539,7 @@ export class Expression extends React.Component { render(): | React.ReactNode | React.ReactElement> { - console.log("render Expression"); if (this.props.apiOptions.customKeypad) { - console.log("custom keypad"); return ( { // this.props.keypadElement should always be set // when apiOptions.customKeypad is set, but how // to convince TypeScript of this? - // this.props.keypadElement?.configure( - // this.props.keypadConfiguration, - // () => { - // if (this._isMounted) { - // this._handleFocus(); - // } - // }, - // ); + this.props.keypadElement?.configure( + this.props.keypadConfiguration, + () => { + if (this._isMounted) { + this._handleFocus(); + } + }, + ); }} onBlur={this._handleBlur} /> @@ -574,7 +572,6 @@ export class Expression extends React.Component { const {ERROR_MESSAGE, ERROR_TITLE} = this.context.strings; - console.log("render MathInput"); return (
{ onChange={this.changeAndTrack} convertDotToTimes={this.props.times} buttonSets={this.props.buttonSets} - //onFocus={this._handleFocus} + onFocus={this._handleFocus} onBlur={this._handleBlur} hasError={this.state.showErrorStyle} extraKeys={this.props.keypadConfiguration?.extraKeys} From 4f6f02e92382540d7026cf401b09d3a1c4937ec5 Mon Sep 17 00:00:00 2001 From: Cat Johnson Date: Fri, 28 Jun 2024 12:03:34 -0700 Subject: [PATCH 03/17] Proof of concept of the arrowkey navigation of the expressions tabbar. --- .../math-input/src/components/tabbar/item.tsx | 177 +++++++++++++----- .../src/components/tabbar/tabbar.tsx | 46 ++++- 2 files changed, 170 insertions(+), 53 deletions(-) diff --git a/packages/math-input/src/components/tabbar/item.tsx b/packages/math-input/src/components/tabbar/item.tsx index d51bd63b95..3e8c362967 100644 --- a/packages/math-input/src/components/tabbar/item.tsx +++ b/packages/math-input/src/components/tabbar/item.tsx @@ -7,6 +7,7 @@ import * as React from "react"; import IconAsset from "./icons"; import type {KeypadPageType} from "../../types"; +import {useCallback, useEffect, useRef} from "react"; const styles = StyleSheet.create({ base: { @@ -75,70 +76,146 @@ function imageTintColor( return color.offBlack64; } export type ItemState = "active" | "inactive" | "disabled"; -type Props = { +type ArrowKeyTabItemProps = { onClick: () => void; itemState: ItemState; itemType: KeypadPageType; + focus: boolean; + setFocus: React.Dispatch>; + index: number; }; -class TabbarItem extends React.Component { - render(): React.ReactNode { - const {onClick, itemType, itemState} = this.props; - return ( - - {({hovered, focused, pressed}) => { - const tintColor = imageTintColor( - itemState, - hovered, - focused, - pressed, - ); +type TabItemProps = { + onClick: () => void; + itemState: ItemState; + itemType: KeypadPageType; +}; + +export function ArrowKeyTabbarItem( + props: ArrowKeyTabItemProps, +): React.ReactElement { + const {onClick, itemType, itemState, focus, setFocus, index} = props; + + const ref = useRef(null); - return ( + useEffect(() => { + if (focus) { + // Move element into view when it is focused + // @ts-expect-error - TS2339 - Property 'current' does not exist on type 'never'. + ref.current?.focus(); + } + }, [focus, ref.current]); + + return ( + + {({hovered, focused, pressed}) => { + const tintColor = imageTintColor( + itemState, + hovered, + focused, + pressed, + ); + + return ( + + + + {itemState === "active" && ( - - - {itemState === "active" && ( - - )} + /> + )} + + ); + }} + + ); +} + +function TabbarItem(props: TabItemProps): React.ReactElement { + const {onClick, itemType, itemState} = props; + + return ( + + {({hovered, focused, pressed}) => { + const tintColor = imageTintColor( + itemState, + hovered, + focused, + pressed, + ); + + return ( + + + - ); - }} - - ); - } + {itemState === "active" && ( + + )} + + ); + }} + + ); } export const TabbarItemForTesting = TabbarItem; diff --git a/packages/math-input/src/components/tabbar/tabbar.tsx b/packages/math-input/src/components/tabbar/tabbar.tsx index 0d9a3cd2ac..952cc2cc27 100644 --- a/packages/math-input/src/components/tabbar/tabbar.tsx +++ b/packages/math-input/src/components/tabbar/tabbar.tsx @@ -2,10 +2,11 @@ import {View} from "@khanacademy/wonder-blocks-core"; import {StyleSheet} from "aphrodite"; import * as React from "react"; -import TabbarItem from "./item"; +import TabbarItem, {ArrowKeyTabbarItem} from "./item"; import type {KeypadPageType} from "../../types"; import type {StyleType} from "@khanacademy/wonder-blocks-core"; +import {useCallback, useEffect, useState} from "react"; const styles = StyleSheet.create({ tabbar: { @@ -31,17 +32,25 @@ type Props = { function Tabbar(props: Props): React.ReactElement { const {items, onClickClose, selectedItem, onSelectItem, style} = props; + const [focus, setFocus] = useArrowKeyFocus(items.length); return ( - {items.map((item) => ( - ( + + > + } onClick={() => { onSelectItem(item); }} @@ -62,4 +71,35 @@ function Tabbar(props: Props): React.ReactElement { ); } +// Custom focus hook to handle arrow key navigation for the TabBar for each ArrowKeyTabItem. +function useArrowKeyFocus(size) { + const [currentFocus, setCurrentFocus] = useState(0); + + const handleKeyDown = useCallback( + (e) => { + if (e.keyCode === 39) { + // Right arrow + e.preventDefault(); + setCurrentFocus( + currentFocus === size - 1 ? size - 1 : currentFocus + 1, + ); + } else if (e.keyCode === 37) { + // Left arrow + e.preventDefault(); + setCurrentFocus(currentFocus === 0 ? 0 : currentFocus - 1); + } + }, + [size, currentFocus, setCurrentFocus], + ); + + useEffect(() => { + document.addEventListener("keydown", handleKeyDown, false); + return () => { + document.removeEventListener("keydown", handleKeyDown, false); + }; + }, [handleKeyDown]); + + return [currentFocus, setCurrentFocus]; +} + export default Tabbar; From 684adae745e712c75ad2ab3e4ad012ccb47846fc Mon Sep 17 00:00:00 2001 From: Cat Johnson Date: Mon, 1 Jul 2024 16:26:19 -0700 Subject: [PATCH 04/17] Making a few changes. --- .../math-input/src/components/tabbar/item.tsx | 21 +++---- .../src/components/tabbar/tabbar.tsx | 55 ++++++++++--------- 2 files changed, 39 insertions(+), 37 deletions(-) diff --git a/packages/math-input/src/components/tabbar/item.tsx b/packages/math-input/src/components/tabbar/item.tsx index 3e8c362967..2abbad70a6 100644 --- a/packages/math-input/src/components/tabbar/item.tsx +++ b/packages/math-input/src/components/tabbar/item.tsx @@ -7,7 +7,7 @@ import * as React from "react"; import IconAsset from "./icons"; import type {KeypadPageType} from "../../types"; -import {useCallback, useEffect, useRef} from "react"; +import {useEffect, useRef} from "react"; const styles = StyleSheet.create({ base: { @@ -81,8 +81,6 @@ type ArrowKeyTabItemProps = { itemState: ItemState; itemType: KeypadPageType; focus: boolean; - setFocus: React.Dispatch>; - index: number; }; type TabItemProps = { @@ -94,17 +92,16 @@ type TabItemProps = { export function ArrowKeyTabbarItem( props: ArrowKeyTabItemProps, ): React.ReactElement { - const {onClick, itemType, itemState, focus, setFocus, index} = props; + const {onClick, itemType, itemState, focus} = props; - const ref = useRef(null); + const tabRef = useRef(null); useEffect(() => { - if (focus) { + if (focus && tabRef?.current) { // Move element into view when it is focused - // @ts-expect-error - TS2339 - Property 'current' does not exist on type 'never'. - ref.current?.focus(); + tabRef?.current.focus(); } - }, [focus, ref.current]); + }, [focus, tabRef.current]); return ( {({hovered, focused, pressed}) => { const tintColor = imageTintColor( @@ -172,7 +169,7 @@ function TabbarItem(props: TabItemProps): React.ReactElement { style={styles.clickable} aria-selected={itemState === "active"} tabIndex={0} - role="tab" + role="button" > {({hovered, focused, pressed}) => { const tintColor = imageTintColor( diff --git a/packages/math-input/src/components/tabbar/tabbar.tsx b/packages/math-input/src/components/tabbar/tabbar.tsx index 952cc2cc27..b3c170400d 100644 --- a/packages/math-input/src/components/tabbar/tabbar.tsx +++ b/packages/math-input/src/components/tabbar/tabbar.tsx @@ -6,7 +6,7 @@ import TabbarItem, {ArrowKeyTabbarItem} from "./item"; import type {KeypadPageType} from "../../types"; import type {StyleType} from "@khanacademy/wonder-blocks-core"; -import {useCallback, useEffect, useState} from "react"; +import {useEffect, useRef, useState} from "react"; const styles = StyleSheet.create({ tabbar: { @@ -32,6 +32,7 @@ type Props = { function Tabbar(props: Props): React.ReactElement { const {items, onClickClose, selectedItem, onSelectItem, style} = props; + const [focus, setFocus] = useArrowKeyFocus(items.length); return ( @@ -44,13 +45,7 @@ function Tabbar(props: Props): React.ReactElement { item === selectedItem ? "active" : "inactive" } itemType={item} - index={index} focus={focus === index} - setFocus={ - setFocus as React.Dispatch< - React.SetStateAction - > - } onClick={() => { onSelectItem(item); }} @@ -72,32 +67,42 @@ function Tabbar(props: Props): React.ReactElement { } // Custom focus hook to handle arrow key navigation for the TabBar for each ArrowKeyTabItem. -function useArrowKeyFocus(size) { +function useArrowKeyFocus(size: number) { const [currentFocus, setCurrentFocus] = useState(0); - const handleKeyDown = useCallback( - (e) => { - if (e.keyCode === 39) { - // Right arrow - e.preventDefault(); - setCurrentFocus( - currentFocus === size - 1 ? size - 1 : currentFocus + 1, - ); - } else if (e.keyCode === 37) { - // Left arrow + useEffect(() => { + function handleKeyDown(e) { + let arrowEvent = false; + + // Extra condition to ensure you can't arrow key navigate outside a + // tabbar component. Would love to know of a more effective notation of this + // As there are concerns around whether this will cause issues + // where multiple keypads are open.... + if (document.activeElement?.role === "tab") { + if (e.keyCode === 39) { + // Right arrow + e.preventDefault(); + setCurrentFocus( + currentFocus === size - 1 ? size - 1 : currentFocus + 1, + ); + arrowEvent = true; + } else if (e.keyCode === 37) { + // Left arrow + e.preventDefault(); + setCurrentFocus(currentFocus === 0 ? 0 : currentFocus - 1); + arrowEvent = true; + } + } + if (arrowEvent) { + e.stopPropagation(); e.preventDefault(); - setCurrentFocus(currentFocus === 0 ? 0 : currentFocus - 1); } - }, - [size, currentFocus, setCurrentFocus], - ); - - useEffect(() => { + } document.addEventListener("keydown", handleKeyDown, false); return () => { document.removeEventListener("keydown", handleKeyDown, false); }; - }, [handleKeyDown]); + }); return [currentFocus, setCurrentFocus]; } From 7d3de427adef79c9f972aea5c283f78baa14ce67 Mon Sep 17 00:00:00 2001 From: Cat Johnson Date: Tue, 2 Jul 2024 14:27:43 -0700 Subject: [PATCH 05/17] Fixing arrow-key logic and consolidating components. --- .../math-input/src/components/tabbar/item.tsx | 79 ++----------------- .../src/components/tabbar/tabbar.tsx | 77 +++++++----------- 2 files changed, 37 insertions(+), 119 deletions(-) diff --git a/packages/math-input/src/components/tabbar/item.tsx b/packages/math-input/src/components/tabbar/item.tsx index 2abbad70a6..c9dd6c6ecc 100644 --- a/packages/math-input/src/components/tabbar/item.tsx +++ b/packages/math-input/src/components/tabbar/item.tsx @@ -76,28 +76,20 @@ function imageTintColor( return color.offBlack64; } export type ItemState = "active" | "inactive" | "disabled"; -type ArrowKeyTabItemProps = { - onClick: () => void; - itemState: ItemState; - itemType: KeypadPageType; - focus: boolean; -}; - type TabItemProps = { onClick: () => void; itemState: ItemState; itemType: KeypadPageType; + focus?: boolean; + role: "tab" | "button"; }; -export function ArrowKeyTabbarItem( - props: ArrowKeyTabItemProps, -): React.ReactElement { - const {onClick, itemType, itemState, focus} = props; - +function TabbarItem(props: TabItemProps): React.ReactElement { + const {onClick, itemType, itemState, focus, role} = props; const tabRef = useRef(null); useEffect(() => { - if (focus && tabRef?.current) { + if (role === "tab" && focus && tabRef?.current) { // Move element into view when it is focused tabRef?.current.focus(); } @@ -110,8 +102,8 @@ export function ArrowKeyTabbarItem( aria-label={itemType} style={styles.clickable} aria-selected={itemState === "active"} - tabIndex={focus ? 0 : -1} - role="tab" + tabIndex={role === "button" ? 0 : focus ? 0 : -1} + role={role} ref={tabRef} > {({hovered, focused, pressed}) => { @@ -158,63 +150,6 @@ export function ArrowKeyTabbarItem( ); } -function TabbarItem(props: TabItemProps): React.ReactElement { - const {onClick, itemType, itemState} = props; - - return ( - - {({hovered, focused, pressed}) => { - const tintColor = imageTintColor( - itemState, - hovered, - focused, - pressed, - ); - - return ( - - - - - {itemState === "active" && ( - - )} - - ); - }} - - ); -} - export const TabbarItemForTesting = TabbarItem; export default TabbarItem; diff --git a/packages/math-input/src/components/tabbar/tabbar.tsx b/packages/math-input/src/components/tabbar/tabbar.tsx index b3c170400d..577521adf8 100644 --- a/packages/math-input/src/components/tabbar/tabbar.tsx +++ b/packages/math-input/src/components/tabbar/tabbar.tsx @@ -2,11 +2,11 @@ import {View} from "@khanacademy/wonder-blocks-core"; import {StyleSheet} from "aphrodite"; import * as React from "react"; -import TabbarItem, {ArrowKeyTabbarItem} from "./item"; +import TabbarItem from "./item"; import type {KeypadPageType} from "../../types"; import type {StyleType} from "@khanacademy/wonder-blocks-core"; -import {useEffect, useRef, useState} from "react"; +import {useRef, useState} from "react"; const styles = StyleSheet.create({ tabbar: { @@ -33,13 +33,36 @@ type Props = { function Tabbar(props: Props): React.ReactElement { const {items, onClickClose, selectedItem, onSelectItem, style} = props; - const [focus, setFocus] = useArrowKeyFocus(items.length); + const selectedIndex = items.findIndex((item: KeypadPageType) => { + return item === selectedItem; + }); + + const [focus, setFocus] = useState( + selectedIndex === -1 ? 0 : selectedIndex, + ); + + // Custom function to handle arrow key navigation for the TabBar for each TabItem. + const useArrowKeyFocus = (e) => { + if (e.keyCode === 39) { + // Right arrow + setFocus(focus === items.length - 1 ? items.length - 1 : focus + 1); + } else if (e.keyCode === 37) { + // Left arrow + setFocus(focus === 0 ? 0 : focus - 1); + } + console.log(focus); + }; return ( - - + + {items.map((item, index) => ( - {onClickClose && ( { - function handleKeyDown(e) { - let arrowEvent = false; - - // Extra condition to ensure you can't arrow key navigate outside a - // tabbar component. Would love to know of a more effective notation of this - // As there are concerns around whether this will cause issues - // where multiple keypads are open.... - if (document.activeElement?.role === "tab") { - if (e.keyCode === 39) { - // Right arrow - e.preventDefault(); - setCurrentFocus( - currentFocus === size - 1 ? size - 1 : currentFocus + 1, - ); - arrowEvent = true; - } else if (e.keyCode === 37) { - // Left arrow - e.preventDefault(); - setCurrentFocus(currentFocus === 0 ? 0 : currentFocus - 1); - arrowEvent = true; - } - } - if (arrowEvent) { - e.stopPropagation(); - e.preventDefault(); - } - } - document.addEventListener("keydown", handleKeyDown, false); - return () => { - document.removeEventListener("keydown", handleKeyDown, false); - }; - }); - - return [currentFocus, setCurrentFocus]; -} - export default Tabbar; From 2c6a4c01edc3be2182cff970d1d28b6a0efe6bf1 Mon Sep 17 00:00:00 2001 From: Cat Johnson Date: Tue, 2 Jul 2024 14:50:51 -0700 Subject: [PATCH 06/17] Fixing linting issues and updating tests. --- .../__snapshots__/keypad.test.tsx.snap | 22 ++++++++++++------- .../__snapshots__/mobile-keypad.test.tsx.snap | 6 ++--- .../keypad/__tests__/keypad.test.tsx | 4 ++-- .../keypad/__tests__/mobile-keypad.test.tsx | 2 +- .../tabbar/__tests__/tabbar.test.tsx | 8 ++++--- .../math-input/src/components/tabbar/item.tsx | 4 ++-- .../src/components/tabbar/tabbar.tsx | 3 +-- .../__tests__/expression-mobile.test.tsx | 2 +- 8 files changed, 29 insertions(+), 22 deletions(-) diff --git a/packages/math-input/src/components/keypad/__tests__/__snapshots__/keypad.test.tsx.snap b/packages/math-input/src/components/keypad/__tests__/__snapshots__/keypad.test.tsx.snap index f261ded6fa..9d2bd1fa52 100644 --- a/packages/math-input/src/components/keypad/__tests__/__snapshots__/keypad.test.tsx.snap +++ b/packages/math-input/src/components/keypad/__tests__/__snapshots__/keypad.test.tsx.snap @@ -10,21 +10,22 @@ exports[`keypad should snapshot expanded: first render 1`] = ` >