Accessibility: Trap focus correctly in dashboard settings (#47149)

* Accessibility: Trap focus correctly in search + dashboard settings

* add accessible names for the overlays

* Undo changes to search

* missed a bit
This commit is contained in:
Ashley Harrison 2022-04-01 11:44:48 +01:00 committed by GitHub
parent e0457665f6
commit 9d3b701797
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,5 +1,8 @@
import React, { useCallback, useMemo } from 'react'; import React, { useCallback, useMemo, useRef } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { FocusScope } from '@react-aria/focus';
import { useDialog } from '@react-aria/dialog';
import { useOverlay } from '@react-aria/overlays';
import { css, cx } from '@emotion/css'; import { css, cx } from '@emotion/css';
import { Button, CustomScrollbar, Icon, IconName, PageToolbar, stylesFactory, useForceUpdate } from '@grafana/ui'; import { Button, CustomScrollbar, Icon, IconName, PageToolbar, stylesFactory, useForceUpdate } from '@grafana/ui';
import config from 'app/core/config'; import config from 'app/core/config';
@ -40,6 +43,14 @@ const MakeEditable = (props: { onMakeEditable: () => any }) => (
); );
export function DashboardSettings({ dashboard, editview }: Props) { export function DashboardSettings({ dashboard, editview }: Props) {
const ref = useRef<HTMLDivElement>(null);
const { overlayProps } = useOverlay({}, ref);
const { dialogProps } = useDialog(
{
'aria-label': 'Dashboard settings',
},
ref
);
const forceUpdate = useForceUpdate(); const forceUpdate = useForceUpdate();
const onMakeEditable = useCallback(() => { const onMakeEditable = useCallback(() => {
dashboard.editable = true; dashboard.editable = true;
@ -139,35 +150,37 @@ export function DashboardSettings({ dashboard, editview }: Props) {
const styles = getStyles(config.theme2); const styles = getStyles(config.theme2);
return ( return (
<div className="dashboard-settings"> <FocusScope contain autoFocus restoreFocus>
<PageToolbar title={`${dashboard.title} / Settings`} parent={folderTitle} onGoBack={onClose} /> <div className="dashboard-settings" ref={ref} {...overlayProps} {...dialogProps}>
<CustomScrollbar> <PageToolbar title={`${dashboard.title} / Settings`} parent={folderTitle} onGoBack={onClose} />
<div className={styles.scrollInner}> <CustomScrollbar>
<div className={styles.settingsWrapper}> <div className={styles.scrollInner}>
<aside className="dashboard-settings__aside"> <div className={styles.settingsWrapper}>
{pages.map((page) => ( <aside className="dashboard-settings__aside">
<Link {pages.map((page) => (
onClick={() => reportInteraction(`Dashboard settings navigation to ${page.id}`)} <Link
to={(loc) => locationUtil.getUrlForPartial(loc, { editview: page.id })} onClick={() => reportInteraction(`Dashboard settings navigation to ${page.id}`)}
className={cx('dashboard-settings__nav-item', { active: page.id === editview })} to={(loc) => locationUtil.getUrlForPartial(loc, { editview: page.id })}
key={page.id} className={cx('dashboard-settings__nav-item', { active: page.id === editview })}
> key={page.id}
<Icon name={page.icon} style={{ marginRight: '4px' }} /> >
{page.title} <Icon name={page.icon} style={{ marginRight: '4px' }} />
</Link> {page.title}
))} </Link>
<div className="dashboard-settings__aside-actions"> ))}
{canSave && <SaveDashboardButton dashboard={dashboard} onSaveSuccess={onPostSave} />} <div className="dashboard-settings__aside-actions">
{canSaveAs && ( {canSave && <SaveDashboardButton dashboard={dashboard} onSaveSuccess={onPostSave} />}
<SaveDashboardAsButton dashboard={dashboard} onSaveSuccess={onPostSave} variant="secondary" /> {canSaveAs && (
)} <SaveDashboardAsButton dashboard={dashboard} onSaveSuccess={onPostSave} variant="secondary" />
</div> )}
</aside> </div>
<div className={styles.settingsContent}>{currentPage.component}</div> </aside>
<div className={styles.settingsContent}>{currentPage.component}</div>
</div>
</div> </div>
</div> </CustomScrollbar>
</CustomScrollbar> </div>
</div> </FocusScope>
); );
} }