diff --git a/.changeset/stupid-seas-stare.md b/.changeset/stupid-seas-stare.md new file mode 100644 index 0000000000..f3d4f5e1f7 --- /dev/null +++ b/.changeset/stupid-seas-stare.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Fix accessibility issue on controlled select input on tab navigation + keyboard enter/space action. diff --git a/packages/core-components/src/components/Select/Select.test.tsx b/packages/core-components/src/components/Select/Select.test.tsx index e9828e70fd..71159150e4 100644 --- a/packages/core-components/src/components/Select/Select.test.tsx +++ b/packages/core-components/src/components/Select/Select.test.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { fireEvent, render } from '@testing-library/react'; +import { fireEvent, render, within } from '@testing-library/react'; import React from 'react'; import { SelectComponent as Select } from './Select'; @@ -45,7 +45,7 @@ describe('} - label={label} - tabIndex={0} - renderValue={s => - multiple && (value as any[]).length !== 0 ? ( - - {(s as string[]).map(selectedValue => ( - el.value === selectedValue)?.value} - label={ - items.find(el => el.value === selectedValue)?.label - } - clickable - onDelete={handleDelete(selectedValue)} - className={classes.chip} - /> - ))} - - ) : ( - - {(value as any[]).length === 0 - ? placeholder || '' - : items.find(el => el.value === s)?.label} - - ) - } - IconComponent={() => - !isOpen ? : - } - MenuProps={{ - anchorOrigin: { - vertical: 'bottom', - horizontal: 'left', - }, - transformOrigin: { - vertical: 'top', - horizontal: 'left', - }, - getContentAnchorEl: null, - }} - > - {placeholder && !multiple && ( - {placeholder} - )} - {native - ? items && - items.map(item => ( - - )) - : items && - items.map(item => ( - - {multiple && ( - - )} - {item.label} - + + {label} + - - + + ) : ( + + {(value as any[]).length === 0 + ? placeholder || '' + : items.find(el => el.value === s)?.label} + + ) + } + IconComponent={() => + !isOpen ? : + } + MenuProps={{ + anchorOrigin: { + vertical: 'bottom', + horizontal: 'left', + }, + transformOrigin: { + vertical: 'top', + horizontal: 'left', + }, + getContentAnchorEl: null, + }} + > + {placeholder && !multiple && ( + {placeholder} + )} + {native + ? items && + items.map(item => ( + + )) + : items && + items.map(item => ( + + {multiple && ( + + )} + {item.label} + + ))} + + ); } diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.test.tsx index 85d9d97c7f..242bed6311 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.test.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { render, waitFor, screen } from '@testing-library/react'; +import { render, waitFor, screen, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { CurveFilter } from './CurveFilter'; @@ -33,7 +33,9 @@ describe('', () => { expect(screen.getByText('Step Before')).toBeInTheDocument(); - await userEvent.click(screen.getByTestId('select')); + await userEvent.click( + within(screen.getByTestId('select')).getByRole('button'), + ); await userEvent.click(screen.getByText('Monotone X')); await waitFor(() => { diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/DirectionFilter.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/DirectionFilter.test.tsx index cb660b5a25..dc74218316 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/DirectionFilter.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/DirectionFilter.test.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { render, waitFor, screen } from '@testing-library/react'; +import { render, waitFor, screen, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { Direction } from '../EntityRelationsGraph'; @@ -37,7 +37,9 @@ describe('', () => { expect(screen.getByText('Right to left')).toBeInTheDocument(); - await userEvent.click(screen.getByTestId('select')); + await userEvent.click( + within(screen.getByTestId('select')).getByRole('button'), + ); await userEvent.click(screen.getByText('Top to bottom')); await waitFor(() => { diff --git a/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.test.tsx b/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.test.tsx index 04d8df3d16..2513f8dca9 100644 --- a/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.test.tsx @@ -19,7 +19,7 @@ import { Entity } from '@backstage/catalog-model'; import { ApiProvider } from '@backstage/core-app-api'; import { alertApiRef } from '@backstage/core-plugin-api'; import { renderWithEffects, TestApiRegistry } from '@backstage/test-utils'; -import { fireEvent, waitFor, screen } from '@testing-library/react'; +import { fireEvent, waitFor, screen, within } from '@testing-library/react'; import { capitalize } from 'lodash'; import { default as React } from 'react'; import { catalogApiRef } from '../../api'; @@ -87,7 +87,7 @@ describe('', () => { expect(screen.getByText('Kind')).toBeInTheDocument(); const input = screen.getByTestId('select'); - fireEvent.click(input); + fireEvent.mouseDown(within(input).getByRole('button')); await waitFor(() => screen.getByText('Domain')); @@ -115,7 +115,7 @@ describe('', () => { , ); const input = screen.getByTestId('select'); - fireEvent.click(input); + fireEvent.mouseDown(within(input).getByRole('button')); await waitFor(() => screen.getByText('Domain')); fireEvent.click(screen.getByText('Domain')); @@ -171,7 +171,7 @@ describe('', () => { ); const input = screen.getByTestId('select'); - fireEvent.click(input); + fireEvent.mouseDown(within(input).getByRole('button')); expect( screen.getByRole('option', { name: 'Component' }), @@ -196,7 +196,7 @@ describe('', () => { expect(screen.getByText('Frob')).toBeInTheDocument(); const input = screen.getByTestId('select'); - fireEvent.click(input); + fireEvent.mouseDown(within(input).getByRole('button')); expect(screen.getByRole('option', { name: 'Domain' })).toBeInTheDocument(); }); }); diff --git a/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.test.tsx b/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.test.tsx index d61a6c480f..3e69c35180 100644 --- a/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.test.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { fireEvent, waitFor, screen } from '@testing-library/react'; +import { fireEvent, waitFor, screen, within } from '@testing-library/react'; import { Entity } from '@backstage/catalog-model'; import { EntityTypePicker } from './EntityTypePicker'; import { MockEntityListContextProvider } from '../../testUtils/providers'; @@ -95,7 +95,7 @@ describe('', () => { expect(screen.getByText('Type')).toBeInTheDocument(); const input = screen.getByTestId('select'); - fireEvent.click(input); + fireEvent.mouseDown(within(input).getByRole('button')); await waitFor(() => screen.getByText('service')); @@ -119,7 +119,7 @@ describe('', () => { , ); const input = screen.getByTestId('select'); - fireEvent.click(input); + fireEvent.mouseDown(within(input).getByRole('button')); await waitFor(() => screen.getByText('service')); fireEvent.click(screen.getByText('service')); @@ -128,7 +128,7 @@ describe('', () => { type: new EntityTypeFilter(['service']), }); - fireEvent.click(input); + fireEvent.mouseDown(within(input).getByRole('button')); fireEvent.click(screen.getByText('all')); expect(updateFilters).toHaveBeenLastCalledWith({ type: undefined }); diff --git a/plugins/xcmetrics/src/components/BuildListFilter/BuildListFilter.test.tsx b/plugins/xcmetrics/src/components/BuildListFilter/BuildListFilter.test.tsx index 5062da4749..bc81b57882 100644 --- a/plugins/xcmetrics/src/components/BuildListFilter/BuildListFilter.test.tsx +++ b/plugins/xcmetrics/src/components/BuildListFilter/BuildListFilter.test.tsx @@ -18,7 +18,7 @@ import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; import userEvent from '@testing-library/user-event'; import { BuildListFilter } from './BuildListFilter'; import { BuildFilters, xcmetricsApiRef } from '../../api'; -import { RenderResult } from '@testing-library/react'; +import { RenderResult, within } from '@testing-library/react'; jest.mock('../../api/XcmetricsClient'); const client = require('../../api/XcmetricsClient'); @@ -50,13 +50,13 @@ const renderWithFiltersVisible = async ( const setStatusFilter = async (rendered: RenderResult, option: string) => { const statusSelect = rendered.getAllByTestId('select')[0]; - await userEvent.click(statusSelect); + await userEvent.click(within(statusSelect).getByRole('button')); await userEvent.click((await rendered.findAllByText(option))[0]); }; const setProjectFilter = async (rendered: RenderResult, option: string) => { const statusSelect = rendered.getAllByTestId('select')[1]; - await userEvent.click(statusSelect); + await userEvent.click(within(statusSelect).getByRole('button')); const options = await rendered.findAllByText(option); await userEvent.click(options[options.length - 1]); };