-
Notifications
You must be signed in to change notification settings - Fork 249
fix: Overflow Tabs (3558) included in collection system #3790
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,7 @@ import { | |
| isSelected, | ||
| useListItemSelect, | ||
| useOverflowListItemMeasure, | ||
| ListRenderItemContext, | ||
| } from '@workday/canvas-kit-react/collection'; | ||
| import {calc, createStencil, px2rem} from '@workday/canvas-kit-styling'; | ||
|
|
||
|
|
@@ -169,6 +170,36 @@ export const StyledTabItem = createComponent('button')<TabsItemProps>({ | |
| }, | ||
| }); | ||
|
|
||
| /** | ||
| * When the selected tab receives ArrowDown, move focus to its tab panel. | ||
| * Only applies to the selected tab; returning null from onKeyDown prevents the roving | ||
| * focus handler from also handling the key. Tabs.OverflowButton does not use this hook. | ||
| * Uses ListRenderItemContext for the item id since this hook runs before useListItemRegister | ||
| * merges in data-id. | ||
| */ | ||
| const useTabsItemFocusPanelOnArrowDown = createElemPropsHook(useTabsModel)( | ||
| ({state}, _, elemProps: {'data-id'?: string} = {}) => { | ||
| const {item} = React.useContext(ListRenderItemContext); | ||
| const name = elemProps['data-id'] || item?.id || ''; | ||
| const selected = !!name && isSelected(name, state); | ||
|
|
||
| return { | ||
| onKeyDown(event: React.KeyboardEvent<HTMLElement>) { | ||
| if (!selected || event.key !== 'ArrowDown') { | ||
| return; | ||
| } | ||
| event.preventDefault(); | ||
| const panelId = slugify(`tabpanel-${state.id}-${name}`); | ||
| const panel = document.getElementById(panelId); | ||
| if (panel) { | ||
| (panel as HTMLElement).focus(); | ||
| } | ||
| return null as unknown as void; // prevent roving focus from handling this key | ||
|
||
| }, | ||
| }; | ||
| } | ||
| ); | ||
|
|
||
| export const useTabsItem = composeHooks( | ||
| createElemPropsHook(useTabsModel)(({state}, _, elemProps: {'data-id'?: string} = {}) => { | ||
| const name = elemProps['data-id'] || ''; | ||
|
|
@@ -185,7 +216,8 @@ export const useTabsItem = composeHooks( | |
| useListItemSelect, | ||
| useOverflowListItemMeasure, | ||
| useListItemRovingFocus, | ||
| useListItemRegister | ||
| useListItemRegister, | ||
| useTabsItemFocusPanelOnArrowDown | ||
| ); | ||
|
|
||
| export const TabsItem = createSubcomponent('button')({ | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -7,15 +7,18 @@ import { | |||
| ExtractProps, | ||||
| useModalityType, | ||||
| useLocalRef, | ||||
| useMountLayout, | ||||
| Generic, | ||||
| } from '@workday/canvas-kit-react/common'; | ||||
| import {Flex, mergeStyles} from '@workday/canvas-kit-react/layout'; | ||||
| import { | ||||
| useOverflowListMeasure, | ||||
| useListRenderItems, | ||||
| useListResetCursorOnBlur, | ||||
| ListRenderItemContext, | ||||
| isCursor, | ||||
| } from '@workday/canvas-kit-react/collection'; | ||||
| import {orientationKeyMap} from '../../collection/lib/keyUtils'; | ||||
|
|
||||
| import {useTabsModel} from './useTabsModel'; | ||||
| import {useTabsModel, TABS_OVERFLOW_BUTTON_ID} from './useTabsModel'; | ||||
| import {createStencil, px2rem} from '@workday/canvas-kit-styling'; | ||||
| import {system} from '@workday/canvas-tokens-web'; | ||||
|
|
||||
|
|
@@ -102,6 +105,50 @@ export const useTabOverflowScroll = createElemPropsHook(useTabsModel)( | |||
| } | ||||
| ); | ||||
|
|
||||
| /** | ||||
| * Resets the tablist cursor on blur to the selected tab when visible, or to the first visible | ||||
| * item (e.g. the overflow button) when the selected tab is hidden. This ensures the roving | ||||
| * tabindex always lands on a focusable element when only the "More" button is visible. | ||||
| */ | ||||
| export const useTabsListResetCursorOnBlur = createElemPropsHook(useTabsModel)(({state, events}) => { | ||||
| const programmaticFocusRef = React.useRef(false); | ||||
| const requestAnimationFrameRef = React.useRef(0); | ||||
|
|
||||
| useMountLayout(() => { | ||||
| return () => { | ||||
| cancelAnimationFrame(requestAnimationFrameRef.current); | ||||
| }; | ||||
| }); | ||||
|
|
||||
| return { | ||||
| onKeyDown(event: React.KeyboardEvent) { | ||||
| if (Object.keys(orientationKeyMap[state.orientation]).indexOf(event.key) !== -1) { | ||||
| programmaticFocusRef.current = true; | ||||
| } | ||||
| }, | ||||
| onFocus() { | ||||
| programmaticFocusRef.current = false; | ||||
| }, | ||||
| onBlur() { | ||||
| if (!programmaticFocusRef.current) { | ||||
| requestAnimationFrameRef.current = requestAnimationFrame(() => { | ||||
| requestAnimationFrameRef.current = 0; | ||||
|
||||
| requestAnimationFrameRef.current = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The import path has been changed from
cypress/react18tocypress/react. This change is not mentioned in the PR description or breaking changes section. If this is intentional, it should be documented as it may affect how Cypress tests are run, especially if the project specifically requires React 18 support or if there are differences in how the mount function behaves between these imports.