A11y/DashList: Make star button tab-navigable (#41479)

This commit is contained in:
kay delaney 2021-11-22 12:03:50 +00:00 committed by GitHub
parent 9122e7f647
commit 47a7477cff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 82 additions and 11 deletions

View File

@ -53,7 +53,7 @@ export const Alert = React.forwardRef<HTMLDivElement, Props>(
<div className={styles.icon}>
<Icon size="xl" name={getIconFromSeverity(severity) as IconName} />
</div>
<div className={styles.body}>
<div className={styles.body} role="alert">
<div className={styles.title}>{title}</div>
{children && <div className={styles.content}>{children}</div>}
</div>

View File

@ -1,9 +1,11 @@
import React, { useEffect, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { take } from 'lodash';
import { css, cx } from '@emotion/css';
import { InterpolateFunction, PanelProps } from '@grafana/data';
import { CustomScrollbar, Icon, useStyles2 } from '@grafana/ui';
import { GrafanaTheme2, InterpolateFunction, PanelProps } from '@grafana/data';
import { CustomScrollbar, stylesFactory, useStyles2 } from '@grafana/ui';
import { Icon, IconProps } from '@grafana/ui/src/components/Icon/Icon';
import { getFocusStyles } from '@grafana/ui/src/themes/mixins';
import { getBackendSrv } from 'app/core/services/backend_srv';
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
import impressionSrv from 'app/core/services/impression_srv';
@ -141,9 +143,14 @@ export function DashList(props: PanelProps<DashListOptions>) {
</a>
{dash.folderTitle && <div className={css.dashlistFolder}>{dash.folderTitle}</div>}
</div>
<span className={css.dashlistStar} onClick={(e) => toggleDashboardStar(e, dash)}>
<Icon name={dash.isStarred ? 'favorite' : 'star'} type={dash.isStarred ? 'mono' : 'default'} />
</span>
<IconToggle
aria-label={`Star dashboard "${dash.title}".`}
className={css.dashlistStar}
enabled={{ name: 'favorite', type: 'mono' }}
disabled={{ name: 'star', type: 'default' }}
checked={dash.isStarred}
onClick={(e) => toggleDashboardStar(e, dash)}
/>
</div>
</li>
))}
@ -154,3 +161,68 @@ export function DashList(props: PanelProps<DashListOptions>) {
</CustomScrollbar>
);
}
interface IconToggleProps extends Partial<IconProps> {
enabled: IconProps;
disabled: IconProps;
checked: boolean;
}
function IconToggle({
enabled,
disabled,
checked,
onClick,
className,
'aria-label': ariaLabel,
...otherProps
}: IconToggleProps) {
const toggleCheckbox = useCallback(
(e: React.MouseEvent<HTMLInputElement>) => {
e.preventDefault();
e.stopPropagation();
onClick?.(e);
},
[onClick]
);
const iconPropsOverride = checked ? enabled : disabled;
const iconProps = { ...otherProps, ...iconPropsOverride };
const styles = useStyles2(getCheckboxStyles);
return (
<label className={styles.wrapper}>
<input
type="checkbox"
checked={checked}
onClick={toggleCheckbox}
className={styles.checkBox}
aria-label={ariaLabel}
/>
<Icon className={cx(styles.icon, className)} {...iconProps} />
</label>
);
}
export const getCheckboxStyles = stylesFactory((theme: GrafanaTheme2) => {
return {
wrapper: css({
display: 'flex',
alignSelf: 'center',
cursor: 'pointer',
zIndex: 100,
}),
checkBox: css({
appearance: 'none',
'&:focus-visible + *': {
...getFocusStyles(theme),
borderRadius: theme.shape.borderRadius(1),
},
}),
icon: css({
marginBottom: 0,
verticalAlign: 'baseline',
display: 'flex',
}),
};
});

View File

@ -23,10 +23,9 @@ export const getStyles = (theme: GrafanaTheme2) => ({
`,
dashlistStar: css`
display: flex;
align-items: center;
align-self: center;
margin-right: 0px;
color: ${theme.colors.secondary.text};
cursor: pointer;
z-index: 1;
`,