Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/@adobe/react-spectrum/src/steplist/StepListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export function StepListItem<T>(props: SpectrumStepListItemProps<T>): ReactNode
}

let markerId = useId();
let stateId = useId();
let labelId = useId();

return (
Expand All @@ -78,7 +79,7 @@ export function StepListItem<T>(props: SpectrumStepListItemProps<T>): ReactNode
<FocusRing within focusRingClass={classNames(styles, 'focus-ring')}>
<a
{...mergeProps(hoverProps, stepProps)}
aria-labelledby={`${markerId} ${labelId}`}
aria-labelledby={`${markerId} ${stateId} ${labelId}`}
ref={ref}
className={classNames(
styles,
Expand All @@ -91,7 +92,7 @@ export function StepListItem<T>(props: SpectrumStepListItemProps<T>): ReactNode
'is-selectable': state.isSelectable(key) && !isSelected
}
)}>
<VisuallyHidden {...stepStateProps}>{stepStateText}</VisuallyHidden>
<VisuallyHidden {...stepStateProps} id={stateId}>{stepStateText}</VisuallyHidden>
<div id={labelId} aria-hidden="true" className={classNames(styles, 'spectrum-Steplist-label')}>
{item.rendered}
</div>
Expand Down
32 changes: 32 additions & 0 deletions packages/@adobe/react-spectrum/test/steplist/StepList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<DOMRefValue<HTMLDivElement>>();
const container = renderComponent({ref});
Expand Down
2 changes: 1 addition & 1 deletion packages/@react-spectrum/s2/src/ComboBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'}>({
Expand Down
5 changes: 2 additions & 3 deletions packages/@react-spectrum/s2/src/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ export let menuitem = style<Omit<MenuItemRenderProps, 'hasSubmenu' | 'isOpen'> &
default: 'default',
isLink: 'pointer'
},
transition: 'default',
transition: 'transform',
forcedColorAdjust: 'none'
}, getAllowedOverrides());

Expand Down Expand Up @@ -296,8 +296,7 @@ export let description = style<{size: 'S' | 'M' | 'L' | 'XL', isFocused: boolean
forcedColors: {
default: 'inherit'
}
},
transition: 'default'
}
});

let value = style({
Expand Down
4 changes: 2 additions & 2 deletions packages/dev/s2-docs/pages/react-aria/Tabs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/react-aria-components/src/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
48 changes: 48 additions & 0 deletions packages/react-aria-components/test/Tabs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<Tabs>
<TabList aria-label="test">
<Tab id="a">A</Tab>
<Tab id="b">B</Tab>
</TabList>
<TabPanels data-testid="tabpanels">
<TabPanel id="a">A</TabPanel>
<TabPanel id="b">B</TabPanel>
</TabPanels>
</Tabs>
);

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(
<Tabs>
<TabList aria-label="test">
<Tab id="a">A</Tab>
<Tab id="b">B</Tab>
</TabList>
<TabPanels data-testid="tabpanels">
<TabPanel id="a">A</TabPanel>
<TabPanel id="b">B</TabPanel>
</TabPanels>
</Tabs>
);

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(
<Tabs>
Expand Down
Loading