Search: Announce to screen reader when query returns no result (#87382)

* Add alert role to not-found variant

* Provide role prop and apply for two cases
This commit is contained in:
Tobias Skarhed
2024-05-22 14:15:56 +02:00
committed by GitHub
parent 38fdfe43f5
commit fb1acb45a2
4 changed files with 15 additions and 3 deletions
@@ -66,6 +66,8 @@ import { EmptyState } from '@grafana/ui';
<EmptyState variant="not-found" message="No playlists found" />; <EmptyState variant="not-found" message="No playlists found" />;
``` ```
If the empty state is rendered as you are typing in a search box, use the `role="alert"` prop to announce the empty state to screen readers. Verify that the use case makes sense, as this approach may be very disruptive if used the wrong way.
### `variant="completed"` ### `variant="completed"`
For when a user has completed all tasks on a page, such as reading all their notifications. For when a user has completed all tasks on a page, such as reading all their notifications.
@@ -1,5 +1,5 @@
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import React, { ReactNode } from 'react'; import React, { AriaRole, ReactNode } from 'react';
import SVG from 'react-inlinesvg'; import SVG from 'react-inlinesvg';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
@@ -31,6 +31,10 @@ interface Props {
* Which variant to use. Affects the default image shown. * Which variant to use. Affects the default image shown.
*/ */
variant: 'call-to-action' | 'not-found' | 'completed'; variant: 'call-to-action' | 'not-found' | 'completed';
/**
* Use to set `alert` when needed. See documentation for the use case
*/
role?: AriaRole;
} }
export const EmptyState = ({ export const EmptyState = ({
@@ -40,12 +44,13 @@ export const EmptyState = ({
message, message,
hideImage = false, hideImage = false,
variant, variant,
role,
}: React.PropsWithChildren<Props>) => { }: React.PropsWithChildren<Props>) => {
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
const imageToShow = image ?? getDefaultImageForVariant(variant); const imageToShow = image ?? getDefaultImageForVariant(variant);
return ( return (
<Box paddingY={4} display="flex" direction="column" alignItems="center"> <Box paddingY={4} display="flex" direction="column" alignItems="center" role={role}>
<div className={styles.container}> <div className={styles.container}>
{!hideImage && imageToShow} {!hideImage && imageToShow}
<Stack direction="column" alignItems="center"> <Stack direction="column" alignItems="center">
@@ -97,6 +97,7 @@ export function SearchView({ width, height, canSelect }: SearchViewProps) {
} }
message={t('browse-dashboards.no-results.text', 'No results found for your query')} message={t('browse-dashboards.no-results.text', 'No results found for your query')}
variant="not-found" variant="not-found"
role="alert"
/> />
</div> </div>
); );
@@ -121,7 +121,11 @@ const RenderResults = ({ isFetchingSearchResults, searchResults }: RenderResults
const showEmptyState = !isFetchingSearchResults && items.length === 0; const showEmptyState = !isFetchingSearchResults && items.length === 0;
return showEmptyState ? ( return showEmptyState ? (
<EmptyState variant="not-found" message={t('command-palette.empty-state.message', 'No results found')} /> <EmptyState
variant="not-found"
role="alert"
message={t('command-palette.empty-state.message', 'No results found')}
/>
) : ( ) : (
<KBarResults <KBarResults
items={items} items={items}