diff --git a/packages/@adobe/react-spectrum/src/steplist/StepListItem.tsx b/packages/@adobe/react-spectrum/src/steplist/StepListItem.tsx index 057b6044b65..e10c14c888d 100644 --- a/packages/@adobe/react-spectrum/src/steplist/StepListItem.tsx +++ b/packages/@adobe/react-spectrum/src/steplist/StepListItem.tsx @@ -65,6 +65,7 @@ export function StepListItem(props: SpectrumStepListItemProps): ReactNode } let markerId = useId(); + let stateId = useId(); let labelId = useId(); return ( @@ -78,7 +79,7 @@ export function StepListItem(props: SpectrumStepListItemProps): ReactNode (props: SpectrumStepListItemProps): ReactNode 'is-selectable': state.isSelectable(key) && !isSelected } )}> - {stepStateText} + {stepStateText} diff --git a/packages/@adobe/react-spectrum/test/steplist/StepList.test.tsx b/packages/@adobe/react-spectrum/test/steplist/StepList.test.tsx index c32ab7aabb7..0a7657c5283 100644 --- a/packages/@adobe/react-spectrum/test/steplist/StepList.test.tsx +++ b/packages/@adobe/react-spectrum/test/steplist/StepList.test.tsx @@ -75,6 +75,38 @@ describe('StepList', function () { expect(stepList).toHaveAttribute('id', 'steplist-id'); }); + it('includes step state text in the accessible name via aria-labelledby', function () { + const tree = renderComponent({defaultLastCompletedStep: 'step-two', defaultSelectedKey: 'step-three', onSelectionChange}); + const stepListItems = tree.getAllByRole('link'); + + // Each link should have an aria-labelledby referencing marker, state, and label IDs + for (let link of stepListItems) { + let labelledby = link.getAttribute('aria-labelledby'); + expect(labelledby).toBeTruthy(); + let ids = labelledby!.split(' '); + expect(ids).toHaveLength(3); + for (let id of ids) { + expect(document.getElementById(id)).toBeTruthy(); + } + } + + // Verify the step state text is included in referenced elements + let currentStep = stepListItems[2]; + let currentIds = currentStep.getAttribute('aria-labelledby')!.split(' '); + let stateEl = document.getElementById(currentIds[1]); + expect(stateEl!.textContent).toContain('Current'); + + let completedStep = stepListItems[0]; + let completedIds = completedStep.getAttribute('aria-labelledby')!.split(' '); + let completedStateEl = document.getElementById(completedIds[1]); + expect(completedStateEl!.textContent).toContain('Completed'); + + let notCompletedStep = stepListItems[3]; + let notCompletedIds = notCompletedStep.getAttribute('aria-labelledby')!.split(' '); + let notCompletedStateEl = document.getElementById(notCompletedIds[1]); + expect(notCompletedStateEl!.textContent).toContain('Not'); + }); + it('attaches a user provided ref', function () { const ref = React.createRef>(); const container = renderComponent({ref}); diff --git a/packages/@react-spectrum/s2/src/ComboBox.tsx b/packages/@react-spectrum/s2/src/ComboBox.tsx index 1c8eac6c5ec..d58621974b1 100644 --- a/packages/@react-spectrum/s2/src/ComboBox.tsx +++ b/packages/@react-spectrum/s2/src/ComboBox.tsx @@ -268,7 +268,7 @@ export let listboxItem = style({ default: 'default', isLink: 'pointer' }, - transition: 'default' + transition: 'transform' }, getAllowedOverrides()); export let listboxHeader = style<{size?: 'S' | 'M' | 'L' | 'XL'}>({ diff --git a/packages/@react-spectrum/s2/src/Menu.tsx b/packages/@react-spectrum/s2/src/Menu.tsx index 26478028846..998c3a7c104 100644 --- a/packages/@react-spectrum/s2/src/Menu.tsx +++ b/packages/@react-spectrum/s2/src/Menu.tsx @@ -200,7 +200,7 @@ export let menuitem = style & default: 'default', isLink: 'pointer' }, - transition: 'default', + transition: 'transform', forcedColorAdjust: 'none' }, getAllowedOverrides()); @@ -296,8 +296,7 @@ export let description = style<{size: 'S' | 'M' | 'L' | 'XL', isFocused: boolean forcedColors: { default: 'inherit' } - }, - transition: 'default' + } }); let value = style({ diff --git a/packages/dev/s2-docs/pages/react-aria/Tabs.mdx b/packages/dev/s2-docs/pages/react-aria/Tabs.mdx index d74e9be4fdc..8783fae31bc 100644 --- a/packages/dev/s2-docs/pages/react-aria/Tabs.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Tabs.mdx @@ -290,8 +290,8 @@ function Example() { links={docs.links} showDescription cssVariables={{ - '--tab-panel-width': 'The width of the active tab panel in pixels. Useful for animations.', - '--tab-panel-height': 'The height of the active tab panel in pixels. Useful for animations.' + '--tab-panel-width': 'The width of the active tab panel in pixels. Useful for animations. Set when width, inline-size, or all is used in the CSS transition.', + '--tab-panel-height': 'The height of the active tab panel in pixels. Useful for animations. Set when height, block-size, or all is used in the CSS transition.' }} /> ### TabPanel diff --git a/packages/react-aria-components/src/Tabs.tsx b/packages/react-aria-components/src/Tabs.tsx index b6428283bc5..33dc60b72a9 100644 --- a/packages/react-aria-components/src/Tabs.tsx +++ b/packages/react-aria-components/src/Tabs.tsx @@ -364,7 +364,7 @@ export const TabPanels = /*#__PURE__*/ createHideableComponent(function TabPanel } if (hasTransition.current == null) { - hasTransition.current = /width|height|all/.test(window.getComputedStyle(el).transition); + hasTransition.current = /width|height|block-size|inline-size|all/.test(window.getComputedStyle(el).transition); } if (hasTransition.current && selectedKeyRef.current != null && selectedKeyRef.current !== state.selectedKey) { diff --git a/packages/react-aria-components/test/Tabs.test.js b/packages/react-aria-components/test/Tabs.test.js index 20f255979ca..7982487d543 100644 --- a/packages/react-aria-components/test/Tabs.test.js +++ b/packages/react-aria-components/test/Tabs.test.js @@ -717,6 +717,54 @@ describe('Tabs', () => { expect(tabPanels).toHaveStyle({width: '100px'}); }); + it('should detect block-size in transition for TabPanels', async () => { + let originalGetComputedStyle = window.getComputedStyle; + window.getComputedStyle = (el) => ({...originalGetComputedStyle(el), transition: 'block-size 400ms ease'}); + + let {getByTestId} = render( + + + A + B + + + A + B + + + ); + + let tabs = document.querySelectorAll('[role="tab"]'); + await user.click(tabs[1]); + + expect(getByTestId('tabpanels').style.getPropertyValue('--tab-panel-height')).not.toBe(''); + window.getComputedStyle = originalGetComputedStyle; + }); + + it('should detect inline-size in transition for TabPanels', async () => { + let originalGetComputedStyle = window.getComputedStyle; + window.getComputedStyle = (el) => ({...originalGetComputedStyle(el), transition: 'inline-size 400ms ease'}); + + let {getByTestId} = render( + + + A + B + + + A + B + + + ); + + let tabs = document.querySelectorAll('[role="tab"]'); + await user.click(tabs[1]); + + expect(getByTestId('tabpanels').style.getPropertyValue('--tab-panel-width')).not.toBe(''); + window.getComputedStyle = originalGetComputedStyle; + }); + it('supports tooltips', async function () { let {getByRole, getAllByRole} = render(