Chore: Fix some more a11y checks in stories (#110531)
fix more a11y checks in stories
This commit is contained in:
@@ -25,9 +25,6 @@ exports[`better eslint`] = {
|
||||
"e2e/utils/support/types.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"packages/grafana-alerting/src/grafana/contactPoints/components/ContactPointSelector/ContactPointSelector.story.tsx:5381": [
|
||||
[0, 0, 0, "Skipping a11y tests is not allowed. Please fix the component or story instead.", "0"]
|
||||
],
|
||||
"packages/grafana-alerting/src/grafana/notificationPolicies/utils.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
@@ -725,9 +722,6 @@ exports[`better eslint`] = {
|
||||
"packages/grafana-ui/src/components/Forms/Checkbox.story.tsx:5381": [
|
||||
[0, 0, 0, "Skipping a11y tests is not allowed. Please fix the component or story instead.", "0"]
|
||||
],
|
||||
"packages/grafana-ui/src/components/Forms/FieldArray.story.tsx:5381": [
|
||||
[0, 0, 0, "Skipping a11y tests is not allowed. Please fix the component or story instead.", "0"]
|
||||
],
|
||||
"packages/grafana-ui/src/components/Forms/Form.story.tsx:5381": [
|
||||
[0, 0, 0, "React Hook \\"useId\\" is called in function \\"renderForm\\" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word \\"use\\".", "0"],
|
||||
[0, 0, 0, "React Hook \\"useId\\" is called in function \\"renderForm\\" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word \\"use\\".", "1"],
|
||||
@@ -836,9 +830,6 @@ exports[`better eslint`] = {
|
||||
"packages/grafana-ui/src/components/QueryField/QueryField.story.tsx:5381": [
|
||||
[0, 0, 0, "Skipping a11y tests is not allowed. Please fix the component or story instead.", "0"]
|
||||
],
|
||||
"packages/grafana-ui/src/components/ScrollContainer/ScrollContainer.story.tsx:5381": [
|
||||
[0, 0, 0, "Skipping a11y tests is not allowed. Please fix the component or story instead.", "0"]
|
||||
],
|
||||
"packages/grafana-ui/src/components/SecretFormField/SecretFormField.tsx:5381": [
|
||||
[0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "0"],
|
||||
[0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "1"]
|
||||
@@ -860,9 +851,6 @@ exports[`better eslint`] = {
|
||||
"packages/grafana-ui/src/components/Segment/SegmentSelect.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"packages/grafana-ui/src/components/Select/Select.story.tsx:5381": [
|
||||
[0, 0, 0, "Skipping a11y tests is not allowed. Please fix the component or story instead.", "0"]
|
||||
],
|
||||
"packages/grafana-ui/src/components/Select/SelectBase.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
|
||||
+16
-4
@@ -1,8 +1,11 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import type { Meta, StoryFn, StoryObj } from '@storybook/react';
|
||||
import { useId } from 'react';
|
||||
|
||||
import { Field } from '@grafana/ui';
|
||||
|
||||
import { defaultDecorators } from '../../../../../tests/story-utils';
|
||||
|
||||
import { ContactPointSelector } from './ContactPointSelector';
|
||||
import { ContactPointSelector, ContactPointSelectorProps } from './ContactPointSelector';
|
||||
import mdx from './ContactPointSelector.mdx';
|
||||
import { simpleContactPointsListScenario, withErrorScenario } from './ContactPointSelector.test.scenario';
|
||||
|
||||
@@ -14,11 +17,18 @@ const meta: Meta<typeof ContactPointSelector> = {
|
||||
docs: {
|
||||
page: mdx,
|
||||
},
|
||||
// TODO fix a11y issue in story and remove this
|
||||
a11y: { test: 'off' },
|
||||
},
|
||||
};
|
||||
|
||||
const StoryRenderFn: StoryFn<ContactPointSelectorProps> = (args) => {
|
||||
const id = useId();
|
||||
return (
|
||||
<Field noMargin label="Select contact point">
|
||||
<ContactPointSelector {...args} id={id} />
|
||||
</Field>
|
||||
);
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof ContactPointSelector>;
|
||||
|
||||
@@ -28,6 +38,7 @@ export const Basic: Story = {
|
||||
handlers: simpleContactPointsListScenario,
|
||||
},
|
||||
},
|
||||
render: StoryRenderFn,
|
||||
};
|
||||
|
||||
export const WithError: Story = {
|
||||
@@ -36,4 +47,5 @@ export const WithError: Story = {
|
||||
handlers: withErrorScenario,
|
||||
},
|
||||
},
|
||||
render: StoryRenderFn,
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Button } from '../Button/Button';
|
||||
import { Input } from '../Input/Input';
|
||||
import { Stack } from '../Layout/Stack/Stack';
|
||||
|
||||
import { Field } from './Field';
|
||||
import { FieldArray } from './FieldArray';
|
||||
import mdx from './FieldArray.mdx';
|
||||
import { Form } from './Form';
|
||||
@@ -21,8 +22,6 @@ const meta: Meta = {
|
||||
controls: {
|
||||
exclude: ['name', 'keyName', 'control', 'shouldUnregister'],
|
||||
},
|
||||
// TODO fix a11y issue in story and remove this
|
||||
a11y: { test: 'off' },
|
||||
},
|
||||
argTypes: {
|
||||
containerWidth: { control: { type: 'range', min: 100, max: 500, step: 10 } },
|
||||
@@ -46,16 +45,22 @@ export const Simple: StoryFn = (args) => {
|
||||
<div style={{ marginBottom: '1rem' }}>
|
||||
{fields.map((field, index) => (
|
||||
<Stack key={field.id}>
|
||||
<Input
|
||||
key={field.id}
|
||||
{...register(`people.${index}.firstName` as const)}
|
||||
defaultValue={field.firstName}
|
||||
/>
|
||||
<Input
|
||||
key={field.id}
|
||||
{...register(`people.${index}.lastName` as const)}
|
||||
defaultValue={field.lastName}
|
||||
/>
|
||||
<Field noMargin label="First name">
|
||||
<Input
|
||||
key={field.id}
|
||||
{...register(`people.${index}.firstName` as const)}
|
||||
defaultValue={field.firstName}
|
||||
id={`${field.id}-first-name`}
|
||||
/>
|
||||
</Field>
|
||||
<Field noMargin label="Last name">
|
||||
<Input
|
||||
key={field.id}
|
||||
{...register(`people.${index}.lastName` as const)}
|
||||
defaultValue={field.lastName}
|
||||
id={`${field.id}-last-name`}
|
||||
/>
|
||||
</Field>
|
||||
</Stack>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -285,7 +285,6 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
gap: theme.spacing(1),
|
||||
marginLeft: theme.spacing(2),
|
||||
color: theme.colors.text.secondary,
|
||||
opacity: 0.7,
|
||||
}),
|
||||
description: css({
|
||||
...theme.typography.bodySmall,
|
||||
|
||||
@@ -14,8 +14,6 @@ const meta: Meta<typeof ScrollContainer> = {
|
||||
docs: {
|
||||
page: mdx,
|
||||
},
|
||||
// TODO fix a11y issue in story and remove this
|
||||
a11y: { test: 'off' },
|
||||
},
|
||||
argTypes: {},
|
||||
args: {
|
||||
|
||||
@@ -40,7 +40,10 @@ export const ScrollContainer = forwardRef<HTMLDivElement, PropsWithChildren<Prop
|
||||
|
||||
return (
|
||||
<Box {...boxProps} display="flex" direction="column" flex={1} position="relative">
|
||||
<div onScroll={onScroll} className={styles.scroller} ref={ref}>
|
||||
{/* scrollable containers need tabindex set so keyboard users can focus them to scroll */}
|
||||
{/* see https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/a7d1a12a6198d546c4a06477b385b4fde03b762e/docs/rules/no-noninteractive-tabindex.md#:~:text=If%20you%20know,scroll%20containers%22. */}
|
||||
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */}
|
||||
<div tabIndex={0} onScroll={onScroll} className={styles.scroller} ref={ref}>
|
||||
{showScrollIndicators ? <ScrollIndicators>{children}</ScrollIndicators> : children}
|
||||
</div>
|
||||
</Box>
|
||||
|
||||
@@ -2,12 +2,13 @@ import { auto } from '@popperjs/core';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { Meta, StoryFn } from '@storybook/react';
|
||||
import Chance from 'chance';
|
||||
import { useState } from 'react';
|
||||
import { useId, useState } from 'react';
|
||||
|
||||
import { SelectableValue, toIconName } from '@grafana/data';
|
||||
|
||||
import { getAvailableIcons } from '../../types/icon';
|
||||
import { Alert } from '../Alert/Alert';
|
||||
import { Field } from '../Forms/Field';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
|
||||
import { AsyncMultiSelect, AsyncSelect, MultiSelect, Select } from './Select';
|
||||
@@ -72,8 +73,6 @@ const meta: Meta = {
|
||||
'value',
|
||||
],
|
||||
},
|
||||
// TODO fix a11y issue in story and remove this
|
||||
a11y: { test: 'off' },
|
||||
},
|
||||
args: {
|
||||
width: 0,
|
||||
@@ -113,10 +112,12 @@ interface StoryProps extends Partial<SelectCommonProps<string>> {
|
||||
|
||||
export const Basic: StoryFn<StoryProps> = (args) => {
|
||||
const [value, setValue] = useState<SelectableValue<string>>();
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Field noMargin label="Select an option">
|
||||
<Select
|
||||
inputId={id}
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
@@ -125,16 +126,18 @@ export const Basic: StoryFn<StoryProps> = (args) => {
|
||||
}}
|
||||
{...args}
|
||||
/>
|
||||
</>
|
||||
</Field>
|
||||
);
|
||||
};
|
||||
|
||||
export const BasicVirtualizedList: StoryFn<StoryProps> = (args) => {
|
||||
const [value, setValue] = useState<SelectableValue<string>>();
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Field noMargin label="Select an option">
|
||||
<Select
|
||||
inputId={id}
|
||||
options={generateThousandsOfOptions()}
|
||||
virtualized
|
||||
value={value}
|
||||
@@ -144,7 +147,7 @@ export const BasicVirtualizedList: StoryFn<StoryProps> = (args) => {
|
||||
}}
|
||||
{...args}
|
||||
/>
|
||||
</>
|
||||
</Field>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -153,9 +156,11 @@ export const BasicVirtualizedList: StoryFn<StoryProps> = (args) => {
|
||||
*/
|
||||
export const BasicSelectPlainValue: StoryFn<StoryProps> = (args) => {
|
||||
const [value, setValue] = useState<string>();
|
||||
const id = useId();
|
||||
return (
|
||||
<>
|
||||
<Field noMargin label="Select an option">
|
||||
<Select
|
||||
inputId={id}
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
@@ -165,7 +170,7 @@ export const BasicSelectPlainValue: StoryFn<StoryProps> = (args) => {
|
||||
prefix={getPrefix(args.icon)}
|
||||
{...args}
|
||||
/>
|
||||
</>
|
||||
</Field>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -186,10 +191,12 @@ export const SelectWithOptionDescriptions: StoryFn = (args) => {
|
||||
imgUrl: 'https://placekitten.com/40/40',
|
||||
},
|
||||
];
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Field noMargin label="Select an option">
|
||||
<Select
|
||||
inputId={id}
|
||||
options={options}
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
@@ -199,7 +206,7 @@ export const SelectWithOptionDescriptions: StoryFn = (args) => {
|
||||
prefix={getPrefix(args.icon)}
|
||||
{...args}
|
||||
/>
|
||||
</>
|
||||
</Field>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -208,10 +215,12 @@ export const SelectWithOptionDescriptions: StoryFn = (args) => {
|
||||
*/
|
||||
export const MultiPlainValue: StoryFn = (args) => {
|
||||
const [value, setValue] = useState<string[]>();
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Field noMargin label="Select an option">
|
||||
<MultiSelect
|
||||
inputId={id}
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
@@ -220,16 +229,18 @@ export const MultiPlainValue: StoryFn = (args) => {
|
||||
prefix={getPrefix(args.icon)}
|
||||
{...args}
|
||||
/>
|
||||
</>
|
||||
</Field>
|
||||
);
|
||||
};
|
||||
|
||||
export const MultiSelectWithOptionGroups: StoryFn = (args) => {
|
||||
const [value, setValue] = useState<string[]>();
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Field noMargin label="Select an option">
|
||||
<MultiSelect
|
||||
inputId={id}
|
||||
options={[
|
||||
{ label: 'Foo', value: '1' },
|
||||
{
|
||||
@@ -262,16 +273,18 @@ export const MultiSelectWithOptionGroups: StoryFn = (args) => {
|
||||
prefix={getPrefix(args.icon)}
|
||||
{...args}
|
||||
/>
|
||||
</>
|
||||
</Field>
|
||||
);
|
||||
};
|
||||
|
||||
export const MultiSelectWithOptionGroupsVirtualized: StoryFn = (args) => {
|
||||
const [value, setValue] = useState<string[]>();
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Field noMargin label="Select an option">
|
||||
<MultiSelect
|
||||
inputId={id}
|
||||
options={manyGroupedOptions}
|
||||
virtualized
|
||||
value={value}
|
||||
@@ -282,25 +295,29 @@ export const MultiSelectWithOptionGroupsVirtualized: StoryFn = (args) => {
|
||||
prefix={getPrefix(args.icon)}
|
||||
{...args}
|
||||
/>
|
||||
</>
|
||||
</Field>
|
||||
);
|
||||
};
|
||||
|
||||
export const MultiSelectBasic: StoryFn = (args) => {
|
||||
const [value, setValue] = useState<Array<SelectableValue<string>>>([]);
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: '450px' }}>
|
||||
<MultiSelect
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
setValue(v);
|
||||
action('onChange')(v);
|
||||
}}
|
||||
prefix={getPrefix(args.icon)}
|
||||
{...args}
|
||||
/>
|
||||
<Field noMargin label="Select an option">
|
||||
<MultiSelect
|
||||
inputId={id}
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
setValue(v);
|
||||
action('onChange')(v);
|
||||
}}
|
||||
prefix={getPrefix(args.icon)}
|
||||
{...args}
|
||||
/>
|
||||
</Field>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -314,20 +331,24 @@ MultiSelectBasic.args = {
|
||||
|
||||
export const MultiSelectBasicWithSelectAll: StoryFn = (args) => {
|
||||
const [value, setValue] = useState<Array<SelectableValue<string>>>([]);
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: '450px' }}>
|
||||
<MultiSelect
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
toggleAllOptions={{ enabled: true }}
|
||||
onChange={(v) => {
|
||||
setValue(v);
|
||||
action('onChange')(v);
|
||||
}}
|
||||
prefix={getPrefix(args.icon)}
|
||||
{...args}
|
||||
/>
|
||||
<Field noMargin label="Select an option">
|
||||
<MultiSelect
|
||||
inputId={id}
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
toggleAllOptions={{ enabled: true }}
|
||||
onChange={(v) => {
|
||||
setValue(v);
|
||||
action('onChange')(v);
|
||||
}}
|
||||
prefix={getPrefix(args.icon)}
|
||||
{...args}
|
||||
/>
|
||||
</Field>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -341,19 +362,23 @@ MultiSelectBasicWithSelectAll.args = {
|
||||
|
||||
export const MultiSelectAsync: StoryFn = (args) => {
|
||||
const [value, setValue] = useState<Array<SelectableValue<string>>>();
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<AsyncMultiSelect
|
||||
loadOptions={loadAsyncOptions}
|
||||
defaultOptions
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
setValue(v);
|
||||
action('onChange')(v);
|
||||
}}
|
||||
prefix={getPrefix(args.icon)}
|
||||
{...args}
|
||||
/>
|
||||
<Field noMargin label="Select an option">
|
||||
<AsyncMultiSelect
|
||||
inputId={id}
|
||||
loadOptions={loadAsyncOptions}
|
||||
defaultOptions
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
setValue(v);
|
||||
action('onChange')(v);
|
||||
}}
|
||||
prefix={getPrefix(args.icon)}
|
||||
{...args}
|
||||
/>
|
||||
</Field>
|
||||
);
|
||||
};
|
||||
MultiSelectAsync.args = {
|
||||
@@ -362,29 +387,35 @@ MultiSelectAsync.args = {
|
||||
|
||||
export const BasicSelectAsync: StoryFn = (args) => {
|
||||
const [value, setValue] = useState<SelectableValue<string>>();
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<AsyncSelect
|
||||
loadOptions={loadAsyncOptions}
|
||||
defaultOptions
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
setValue(v);
|
||||
action('onChange')(v);
|
||||
}}
|
||||
prefix={getPrefix(args.icon)}
|
||||
{...args}
|
||||
/>
|
||||
<Field noMargin label="Select an option">
|
||||
<AsyncSelect
|
||||
inputId={id}
|
||||
loadOptions={loadAsyncOptions}
|
||||
defaultOptions
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
setValue(v);
|
||||
action('onChange')(v);
|
||||
}}
|
||||
prefix={getPrefix(args.icon)}
|
||||
{...args}
|
||||
/>
|
||||
</Field>
|
||||
);
|
||||
};
|
||||
|
||||
export const AutoMenuPlacement: StoryFn = (args) => {
|
||||
const [value, setValue] = useState<SelectableValue<string>>();
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={{ width: '100%', height: 'calc(95vh - 118px)', display: 'flex', alignItems: 'flex-end' }}>
|
||||
<div style={{ width: '100%', height: 'calc(95vh - 118px)', display: 'flex', alignItems: 'flex-end' }}>
|
||||
<Field noMargin label="Select an option">
|
||||
<Select
|
||||
inputId={id}
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
@@ -394,8 +425,8 @@ export const AutoMenuPlacement: StoryFn = (args) => {
|
||||
prefix={getPrefix(args.icon)}
|
||||
{...args}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
</Field>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
AutoMenuPlacement.args = {
|
||||
@@ -404,11 +435,13 @@ AutoMenuPlacement.args = {
|
||||
|
||||
export const WidthAuto: StoryFn = (args) => {
|
||||
const [value, setValue] = useState<SelectableValue<string>>();
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={{ width: '100%' }}>
|
||||
<div style={{ width: '100%' }}>
|
||||
<Field noMargin label="Select an option">
|
||||
<Select
|
||||
inputId={id}
|
||||
options={generateOptions()}
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
@@ -419,8 +452,8 @@ export const WidthAuto: StoryFn = (args) => {
|
||||
{...args}
|
||||
width="auto"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
</Field>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -428,9 +461,11 @@ export const CustomValueCreation: StoryFn = (args) => {
|
||||
const [value, setValue] = useState<SelectableValue<string>>();
|
||||
const [customOptions, setCustomOptions] = useState<Array<SelectableValue<string>>>([]);
|
||||
const options = generateOptions();
|
||||
const id = useId();
|
||||
return (
|
||||
<>
|
||||
<Field noMargin label="Select an option">
|
||||
<Select
|
||||
inputId={id}
|
||||
options={[...options, ...customOptions]}
|
||||
value={value}
|
||||
onChange={(v) => {
|
||||
@@ -447,7 +482,7 @@ export const CustomValueCreation: StoryFn = (args) => {
|
||||
prefix={getPrefix(args.icon)}
|
||||
{...args}
|
||||
/>
|
||||
</>
|
||||
</Field>
|
||||
);
|
||||
};
|
||||
CustomValueCreation.args = {
|
||||
|
||||
Reference in New Issue
Block a user