improve dialog accessability

This commit is contained in:
David Lechner
2022-03-17 11:08:31 -05:00
parent 46faff981d
commit c3c9a8fd7c
8 changed files with 68 additions and 27 deletions
+11 -20
View File
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2021-2022 The Pybricks Authors
import { getByLabelText, waitForElementToBeRemoved } from '@testing-library/react';
import { getByLabelText, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { testRender } from '../../test';
@@ -22,28 +22,19 @@ it('should manage license dialog open/close', async () => {
<AboutDialog isOpen={true} onClose={() => undefined} />,
);
expect(
dialog.queryByRole('dialog', { name: 'Open Source Software Licenses' }),
).toBeNull();
userEvent.click(dialog.getByText('Software Licenses'));
expect(
dialog.getByText(
`${process.env.REACT_APP_NAME} is built on open source software.`,
{
exact: false,
},
),
).toBeInTheDocument();
const licenseDialog = dialog.getByRole('dialog', {
name: 'Open Source Software Licenses',
});
expect(licenseDialog).toBeVisible();
const licenseDialog = document.querySelector(
'.pb-license-dialog',
) as HTMLDivElement;
userEvent.click(getByLabelText(licenseDialog, 'Close'));
await waitForElementToBeRemoved(() =>
dialog.queryByText(
`${process.env.REACT_APP_NAME} is built on open source software.`,
{
exact: false,
},
),
);
await waitFor(() => expect(licenseDialog).not.toBeVisible());
});
+2 -1
View File
@@ -34,7 +34,7 @@ const AboutDialog: React.VoidFunctionComponent<AboutDialogProps> = ({
return (
<Dialog
title={`Pybricks v${firmwareVersion} (${appName} v${appVersion})`}
title={i18n.translate(AboutStringId.Title, { appName })}
isOpen={isOpen}
onClose={onClose}
>
@@ -45,6 +45,7 @@ const AboutDialog: React.VoidFunctionComponent<AboutDialogProps> = ({
<p>
<strong>{i18n.translate(AboutStringId.Description)}</strong>
</p>
<p>{`v${firmwareVersion} (${appName} v${appVersion})`}</p>
<p>{pybricksCopyright}</p>
</div>
<div className={Classes.DIALOG_FOOTER}>
+1
View File
@@ -1,5 +1,6 @@
{
"about": {
"title": "About {appName}",
"description": "MicroPython for LEGO® Powered Up smart hubs.",
"licenseButton": { "label": "Software Licenses" },
"changelogButton": { "label": "Changelog" },
+2 -1
View File
@@ -1,9 +1,10 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2021 The Pybricks Authors
// Copyright (c) 2021-2022 The Pybricks Authors
// About dialog translation keys.
export enum AboutStringId {
Title = 'about.title',
Description = 'about.description',
LicenseButtonLabel = 'about.licenseButton.label',
ChangelogButtonLabel = 'about.changelogButton.label',
+1 -1
View File
@@ -57,7 +57,7 @@ describe('new file button', () => {
userEvent.click(button);
const dialog = explorer.getByRole('dialog');
const dialog = explorer.getByRole('dialog', { name: 'Create a new file' });
expect(dialog).toBeVisible();
userEvent.click(getByLabelText(dialog, 'Close'));
+4 -2
View File
@@ -108,11 +108,13 @@ describe('about dialog', () => {
<SettingsDrawer isOpen={true} onClose={() => undefined} />,
);
expect(settings.queryByRole('dialog')).toBeNull();
const appName = process.env.REACT_APP_NAME;
expect(settings.queryByRole('dialog', { name: `About ${appName}` })).toBeNull();
settings.getByText('About').click();
const dialog = settings.getByRole('dialog');
const dialog = settings.getByRole('dialog', { name: `About ${appName}` });
expect(dialog).toBeVisible();
+14 -2
View File
@@ -19,7 +19,7 @@ import {
} from '@blueprintjs/core';
import { Tooltip2 } from '@blueprintjs/popover2';
import { useI18n } from '@shopify/react-i18n';
import React, { useMemo, useState } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { useDispatch } from 'react-redux';
import { useTernaryDarkMode } from 'usehooks-ts';
import AboutDialog from '../about/AboutDialog';
@@ -98,12 +98,24 @@ const SettingsDrawer: React.VoidFunctionComponent<SettingsProps> = ({
useHotkeys(hotkeys);
// HACK: set additional attributes that are not supported via Drawer props
const handleDrawerOpening = useCallback<(node: HTMLElement) => void>((n) => {
n.setAttribute('role', 'dialog');
n.setAttribute('aria-modal', 'true');
n.setAttribute('aria-labelledby', 'settings-drawer-dialog-title');
}, []);
return (
<Drawer
isOpen={isOpen}
icon="cog"
size={DrawerSize.SMALL}
title={i18n.translate(SettingsStringId.Title)}
title={
<span id="settings-drawer-dialog-title">
{i18n.translate(SettingsStringId.Title)}
</span>
}
onOpening={handleDrawerOpening}
onClose={onClose}
// work around https://github.com/palantir/blueprint/issues/5169
shouldReturnFocusOnClose={false}
+33
View File
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2022 The Pybricks Authors
import { getByLabelText, waitFor } from '@testing-library/dom';
import React from 'react';
import { testRender } from '../../test';
import Toolbar from './Toolbar';
describe('settings button', () => {
it('should open settings drawer', async () => {
const [toolbar] = testRender(<Toolbar />);
const settingButton = toolbar.getByLabelText('Settings');
expect(
toolbar.queryByRole('dialog', {
name: 'Settings & Help',
}),
).toBeNull();
settingButton.click();
const settingsDrawer = toolbar.getByRole('dialog', {
name: 'Settings & Help',
});
expect(settingsDrawer).toBeVisible();
getByLabelText(settingsDrawer, 'Close').click();
await waitFor(() => expect(settingsDrawer).not.toBeVisible());
});
});