Chore: Unskip some a11y story checks and fix any associated issues (#108613)

* fix some a11y issues with the stories

* fix lockfile

* fix tests

* put aria-label on <Icon>

* add aria-describedby

* undo changes to VizLegendTable

* use useID for image id
This commit is contained in:
Ashley Harrison
2025-07-29 09:30:18 +01:00
committed by GitHub
parent fe906dfc1e
commit a2698dc3b5
43 changed files with 348 additions and 362 deletions
-84
View File
@@ -4229,36 +4229,12 @@ exports[`no skipping a11y tests in stories`] = {
"packages/grafana-alerting/src/grafana/contactPoints/components/ContactPointSelector/ContactPointSelector.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/AutoSaveField/AutoSaveField.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Button/Button.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Carousel/Carousel.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Cascader/Cascader.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/ColorPicker/ColorPicker.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/ColorPicker/ColorPickerInput.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Combobox/MultiCombobox.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/ConfirmButton/ConfirmButton.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/ContextMenu/ContextMenu.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/DateTimePickers/TimeOfDayPicker.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/FileDropzone/FileDropzone.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
@@ -4268,45 +4244,21 @@ exports[`no skipping a11y tests in stories`] = {
"packages/grafana-ui/src/components/Forms/Checkbox.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Forms/Field.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Forms/FieldArray.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Forms/FieldSet.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Forms/Form.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Forms/InlineField.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Forms/RadioButtonGroup/RadioButtonGroup.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/InlineToast/InlineToast.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Input/Input.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/InteractiveTable/InteractiveTable.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Layout/Grid/Grid.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Layout/Stack/Stack.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Link/TextLink.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/LoadingBar/LoadingBar.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Menu/Menu.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
@@ -4325,54 +4277,18 @@ exports[`no skipping a11y tests in stories`] = {
"packages/grafana-ui/src/components/ScrollContainer/ScrollContainer.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Segment/Segment.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Segment/SegmentAsync.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Segment/SegmentInput.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Select/Select.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Select/SelectPerf.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Slider/RangeSlider.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Slider/Slider.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/StatsPicker/StatsPicker.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Switch/Switch.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Table/Table.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/TableInputCSV/TableInputCSV.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Tags/TagList.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/Text/Text.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/ThemeDemos/ThemeDemo.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/ThemeDemos/Typography.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/ToolbarButton/ToolbarButton.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
"packages/grafana-ui/src/components/VizLayout/VizLayout.story.tsx:5381": [
[0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"]
],
@@ -186,7 +186,7 @@ class LightColors implements ThemeColorsBase<Partial<ThemeRichColor>> {
text = {
primary: `rgba(${this.blackBase}, 1)`,
secondary: `rgba(${this.blackBase}, 0.75)`,
disabled: `rgba(${this.blackBase}, 0.64)`,
disabled: `rgba(${this.blackBase}, 0.65)`,
link: this.primary.text,
maxContrast: palette.black,
};
@@ -1,5 +1,5 @@
import { StoryFn, Meta } from '@storybook/react';
import { useState } from 'react';
import { useId, useState } from 'react';
import { Combobox } from '../Combobox/Combobox';
import { Checkbox } from '../Forms/Checkbox';
@@ -35,8 +35,6 @@ const meta: Meta = {
'validationMessageHorizontalOverflow',
],
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
argTypes: {
saveErrorMessage: { control: 'text' },
@@ -76,10 +74,12 @@ const themeOptions = [
export const Basic: StoryFn = (args) => {
const [inputValue, setInputValue] = useState('');
const id = useId();
return (
<AutoSaveField onFinishChange={args.inputSuccessful ? getSuccess : getError} {...args}>
{(onChange) => (
<Input
id={id}
value={inputValue}
onChange={(e) => {
const value = e.currentTarget.value;
@@ -105,12 +105,19 @@ export const AllComponents: StoryFn = (args) => {
const [checkBoxValue, setCheckBoxValue] = useState(false);
const [textAreaValue, setTextAreaValue] = useState('');
const [switchValue, setSwitchValue] = useState(false);
const textId = useId();
const comboboxId = useId();
const radioButtonId = useId();
const checkBoxId = useId();
const textAreaId = useId();
const switchId = useId();
return (
<div>
<AutoSaveField onFinishChange={args.inputSuccessful ? getSuccess : getError} label="Text as a child" {...args}>
{(onChange) => (
<Input
id={textId}
value={inputTextValue}
onChange={(e) => {
const value = e.currentTarget.value;
@@ -123,6 +130,7 @@ export const AllComponents: StoryFn = (args) => {
<AutoSaveField onFinishChange={args.inputSuccessful ? getSuccess : getError} label="Combobox as child" {...args}>
{(onChange) => (
<Combobox
id={comboboxId}
options={themeOptions}
value={comoboboxValue}
onChange={(v) => {
@@ -139,6 +147,7 @@ export const AllComponents: StoryFn = (args) => {
>
{(onChange) => (
<RadioButtonGroup
id={radioButtonId}
options={themeOptions}
value={radioButtonValue}
onChange={(themeOption) => {
@@ -155,6 +164,7 @@ export const AllComponents: StoryFn = (args) => {
>
{(onChange) => (
<Checkbox
id={checkBoxId}
label="Checkbox test"
description="This is a checkbox input"
name="checkbox-test"
@@ -174,6 +184,7 @@ export const AllComponents: StoryFn = (args) => {
>
{(onChange) => (
<TextArea
id={textAreaId}
value={textAreaValue}
onChange={(e) => {
const value = e.currentTarget.value;
@@ -190,6 +201,7 @@ export const AllComponents: StoryFn = (args) => {
>
{(onChange) => (
<Switch
id={switchId}
label="Switch test"
name="switch-test"
value={switchValue}
@@ -18,8 +18,6 @@ export default {
docs: {
page: mdx,
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
argTypes: {
size: {
@@ -81,25 +79,25 @@ export const Examples: StoryFn<typeof Button> = () => {
<div>Inside ButtonGroup</div>
<ButtonGroup>
<Button icon="sync">Run query</Button>
<Button icon="angle-down" />
<Button icon="angle-down" aria-label="Expand menu" />
</ButtonGroup>
<ButtonGroup>
<Button variant="destructive" icon="sync">
Run query
</Button>
<Button variant="destructive" icon="angle-down" />
<Button variant="destructive" icon="angle-down" aria-label="Expand menu" />
</ButtonGroup>
<ButtonGroup>
<Button variant="success" icon="sync">
Run query
</Button>
<Button variant="success" icon="angle-down" />
<Button variant="success" icon="angle-down" aria-label="Expand menu" />
</ButtonGroup>
<ButtonGroup>
<Button variant="secondary" icon="sync">
Run query
</Button>
<Button variant="secondary" icon="angle-down" />
<Button variant="secondary" icon="angle-down" aria-label="Expand menu" />
</ButtonGroup>
</Stack>
<Card noMargin>
@@ -19,8 +19,6 @@ const meta: Meta<typeof Carousel> = {
component: Carousel,
parameters: {
docs: { page: mdx },
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
argTypes: {
images: {
@@ -24,9 +24,10 @@ describe('Carousel', () => {
render(<Carousel images={testImages} />);
testImages.forEach((image) => {
const imgElement = screen.getByAltText(image.name);
expect(imgElement).toBeInTheDocument();
expect(imgElement).toHaveAttribute('src', image.path);
const name = screen.getByText(image.name);
expect(name).toBeInTheDocument();
const imageElement = document.querySelector(`img[src="${image.path}"]`);
expect(imageElement).toBeInTheDocument();
});
});
@@ -140,7 +141,7 @@ describe('Carousel', () => {
render(<Carousel images={testImages} />);
const images = screen.getAllByRole('img');
const images = screen.getAllByRole('presentation');
images.forEach((img) => {
fireEvent.error(img);
});
@@ -2,7 +2,7 @@ import { css, cx } from '@emotion/css';
import { useDialog } from '@react-aria/dialog';
import { FocusScope } from '@react-aria/focus';
import { OverlayContainer, useOverlay } from '@react-aria/overlays';
import { useState, useEffect, useRef } from 'react';
import { useState, useEffect, useRef, useId } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { t } from '@grafana/i18n';
@@ -26,6 +26,7 @@ export const Carousel: React.FC<CarouselProps> = ({ images }) => {
const [selectedIndex, setSelectedIndex] = useState<number | null>(null);
const [imageErrors, setImageErrors] = useState<Record<string, boolean>>({});
const [validImages, setValidImages] = useState<CarouselImage[]>(images);
const id = useId();
const styles = useStyles2(getStyles);
const resetButtonStyles = useStyles2(clearButtonStyles);
@@ -100,17 +101,22 @@ export const Carousel: React.FC<CarouselProps> = ({ images }) => {
return (
<>
<div className={cx(styles.imageGrid)}>
{validImages.map((image, index) => (
<button
type="button"
key={image.path}
onClick={() => openPreview(index)}
className={cx(resetButtonStyles, styles.imageButton)}
>
<img src={image.path} alt={image.name} onError={() => handleImageError(image.path)} />
<p>{image.name}</p>
</button>
))}
{validImages.map((image, index) => {
const imageNameId = `${id}-carousel-image-${index}`;
return (
<button
aria-label={t('grafana-ui.carousel.aria-label-open-image', 'Open image preview')}
aria-describedby={imageNameId}
type="button"
key={image.path}
onClick={() => openPreview(index)}
className={cx(resetButtonStyles, styles.imageButton)}
>
<img src={image.path} alt="" onError={() => handleImageError(image.path)} />
<p id={imageNameId}>{image.name}</p>
</button>
);
})}
</div>
{selectedIndex !== null && (
@@ -1,5 +1,7 @@
import { StoryFn, Meta } from '@storybook/react';
import { useState } from 'react';
import { useId, useState } from 'react';
import { Field } from '../Forms/Field';
import { Cascader, CascaderOption } from './Cascader';
import mdx from './Cascader.mdx';
@@ -49,8 +51,6 @@ const meta: Meta<typeof Cascader> = {
'formatCreateLabel',
],
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
args: {
onSelect,
@@ -61,7 +61,14 @@ const meta: Meta<typeof Cascader> = {
},
};
const Template: StoryFn<typeof Cascader> = (args) => <Cascader {...args} />;
const Template: StoryFn<typeof Cascader> = (args) => {
const id = useId();
return (
<Field label="Cascader field">
<Cascader {...args} id={id} />
</Field>
);
};
export const Simple = Template.bind({});
Simple.args = {
@@ -93,10 +100,15 @@ export const WithOptionsStateUpdate = () => {
value: 'initial',
},
]);
const id = useId();
setTimeout(() => setOptions(options), 2000);
return <Cascader options={updatedOptions} onSelect={onSelect} />;
return (
<Field label="Cascader field with updated options">
<Cascader options={updatedOptions} onSelect={onSelect} id={id} />
</Field>
);
};
export default meta;
@@ -1,6 +1,9 @@
import { action } from '@storybook/addon-actions';
import { useArgs } from '@storybook/preview-api';
import { Meta, StoryFn } from '@storybook/react';
import { useId } from 'react';
import { Field } from '../Forms/Field';
import { ColorPickerInput } from './ColorPickerInput';
@@ -11,22 +14,24 @@ const meta: Meta<typeof ColorPickerInput> = {
controls: {
exclude: ['onChange', 'onColorChange'],
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
};
export const Basic: StoryFn<typeof ColorPickerInput> = ({ color }) => {
const [, updateArgs] = useArgs();
const id = useId();
return (
<div style={{ minHeight: '100dvh', display: 'grid', placeContent: 'center' }}>
<ColorPickerInput
value={color}
onChange={(color) => {
action('Color changed')(color);
updateArgs({ color });
}}
/>
<Field label="Select color">
<ColorPickerInput
id={id}
value={color}
onChange={(color) => {
action('Color changed')(color);
updateArgs({ color });
}}
/>
</Field>
</div>
);
};
@@ -21,8 +21,6 @@ const meta: Meta = {
controls: {
exclude: ['className', 'onClick', 'onCancel', 'onConfirm'],
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
args: {
buttonText: 'Edit',
@@ -27,7 +27,12 @@ export const DeleteButton = ({ size, disabled, onConfirm, 'aria-label': ariaLabe
onConfirm={onConfirm}
closeOnConfirm={closeOnConfirm}
>
<Button aria-label={ariaLabel} variant="destructive" icon="times" size={size || 'sm'} />
<Button
aria-label={ariaLabel ?? t('grafana-ui.confirm-button.aria-label-delete', 'Delete')}
variant="destructive"
icon="times"
size={size || 'sm'}
/>
</ConfirmButton>
);
};
@@ -3,6 +3,7 @@ import { StoryFn, Meta } from '@storybook/react';
import * as React from 'react';
import { IconButton } from '../IconButton/IconButton';
import { Text } from '../Text/Text';
import { ContextMenu, ContextMenuProps } from './ContextMenu';
import mdx from './ContextMenu.mdx';
@@ -19,8 +20,6 @@ const meta: Meta<typeof ContextMenu> = {
controls: {
exclude: ['renderMenuItems', 'renderHeader', 'onClose', 'children'],
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
args: {
x: 200,
@@ -31,7 +30,7 @@ const meta: Meta<typeof ContextMenu> = {
};
const renderHeader = (): React.ReactNode => {
return <h6>Menu</h6>;
return <Text variant="h6">Menu</Text>;
};
export const Basic: StoryFn<typeof ContextMenu> = (args: ContextMenuProps) => {
@@ -1,9 +1,12 @@
import { action } from '@storybook/addon-actions';
import { useArgs } from '@storybook/preview-api';
import { Meta, StoryFn } from '@storybook/react';
import { useId } from 'react';
import { dateTime } from '@grafana/data';
import { Field } from '../Forms/Field';
import { TimeOfDayPicker } from './TimeOfDayPicker';
const meta: Meta<typeof TimeOfDayPicker> = {
@@ -13,8 +16,6 @@ const meta: Meta<typeof TimeOfDayPicker> = {
controls: {
exclude: ['onChange'],
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
args: {
value: dateTime(Date.now()),
@@ -24,14 +25,18 @@ const meta: Meta<typeof TimeOfDayPicker> = {
export const Basic: StoryFn<typeof TimeOfDayPicker> = (args) => {
const [, updateArgs] = useArgs();
const id = useId();
return (
<TimeOfDayPicker
{...args}
onChange={(newValue?) => {
action('on selected')(newValue);
updateArgs({ value: newValue });
}}
/>
<Field label="Select a time">
<TimeOfDayPicker
{...args}
id={id}
onChange={(newValue?) => {
action('on selected')(newValue);
updateArgs({ value: newValue });
}}
/>
</Field>
);
};
@@ -26,6 +26,7 @@ interface BaseProps {
disabledSeconds?: () => number[];
placeholder?: string;
allowEmpty?: boolean;
id?: string;
}
interface AllowEmptyProps extends BaseProps {
@@ -52,6 +53,7 @@ export const TimeOfDayPicker = ({
disabledHours,
disabledMinutes,
disabledSeconds,
id,
placeholder,
// note: we can't destructure allowEmpty/onChange here
// in order to discriminate the types properly later in the onChange handler
@@ -62,6 +64,7 @@ export const TimeOfDayPicker = ({
return (
<RcPicker<Moment>
id={id}
generateConfig={generateConfig}
locale={locale}
allowClear={
@@ -1,5 +1,5 @@
import { Meta, StoryFn } from '@storybook/react';
import { useState, useCallback } from 'react';
import { useState, useCallback, useId } from 'react';
import * as React from 'react';
import { Input } from '../Input/Input';
@@ -26,18 +26,19 @@ const meta: Meta<typeof Field> = {
controls: {
exclude: ['children', 'className'],
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
};
export const Simple: StoryFn<typeof Field> = (args) => (
<div>
<Field {...args}>
<Input id="thisField" />
</Field>
</div>
);
export const Simple: StoryFn<typeof Field> = (args) => {
const id = useId();
return (
<div>
<Field {...args}>
<Input id={id} />
</Field>
</div>
);
};
Simple.args = {
label: 'Graphite API key',
@@ -50,6 +51,7 @@ Simple.args = {
};
export const HorizontalLayout: StoryFn<typeof Field> = (args) => {
const id = useId();
const [checked, setChecked] = useState(false);
const onChange = useCallback(
(e: React.FormEvent<HTMLInputElement>) => setChecked(e.currentTarget.checked),
@@ -58,7 +60,7 @@ export const HorizontalLayout: StoryFn<typeof Field> = (args) => {
return (
<div>
<Field {...args}>
<Switch checked={checked} onChange={onChange} />
<Switch checked={checked} onChange={onChange} id={id} />
</Field>
</div>
);
@@ -1,4 +1,5 @@
import { Meta, StoryFn } from '@storybook/react';
import { useId } from 'react';
import { Button } from '../Button/Button';
import { Input } from '../Input/Input';
@@ -21,8 +22,6 @@ const meta: Meta<typeof FieldSet> = {
controls: {
exclude: ['children'],
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
argTypes: {
label: { control: 'text' },
@@ -30,22 +29,26 @@ const meta: Meta<typeof FieldSet> = {
};
export const Basic: StoryFn<typeof FieldSet> = (args: Props) => {
const nameId = useId();
const emailId = useId();
const colorId = useId();
const fontSizeId = useId();
return (
<Form onSubmit={() => console.log('Submit')}>
{() => (
<>
<FieldSet {...args}>
<Field label="Name">
<Input name="name" />
<Input name="name" id={nameId} />
</Field>
<Field label="Email">
<Input name="email" />
<Input name="email" id={emailId} />
</Field>
<Field label="Color">
<Input name="color" />
<Input name="color" id={colorId} />
</Field>
<Field label="Font size">
<Input name="fontsize" />
<Input name="fontsize" id={fontSizeId} />
</Field>
</FieldSet>
<Button variant="primary">Save</Button>
@@ -1,4 +1,5 @@
import { StoryFn } from '@storybook/react';
import { useId } from 'react';
import { ValidateResult } from 'react-hook-form';
import { withStoryContainer } from '../../utils/storybook/withStoryContainer';
@@ -23,8 +24,6 @@ export default {
docs: {
page: mdx,
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
};
@@ -57,71 +56,82 @@ interface FormDTO {
};
}
const renderForm = (defaultValues?: FormDTO) => (
<Form
defaultValues={defaultValues}
onSubmit={(data: FormDTO) => {
console.log(data);
}}
>
{({ register, control, errors }) => {
console.log(errors);
return (
<>
<Legend>Edit user</Legend>
const renderForm = (defaultValues?: FormDTO) => {
const nameId = useId();
const emailId = useId();
const usernameId = useId();
const nestedPathId = useId();
const textId = useId();
const checkboxId = useId();
const switchId = useId();
const radioId = useId();
const selectId = useId();
return (
<Form
defaultValues={defaultValues}
onSubmit={(data: FormDTO) => {
console.log(data);
}}
>
{({ register, control, errors }) => {
console.log(errors);
return (
<>
<Legend>Edit user</Legend>
<Field label="Name" invalid={!!errors.name} error="Name is required">
<Input {...register('name', { required: true })} placeholder="Roger Waters" />
</Field>
<Field label="Name" invalid={!!errors.name} error="Name is required">
<Input {...register('name', { required: true })} placeholder="Roger Waters" id={nameId} />
</Field>
<Field label="Email" invalid={!!errors.email} error="E-mail is required">
<Input {...register('email', { required: true })} id="email" placeholder="roger.waters@grafana.com" />
</Field>
<Field label="Email" invalid={!!errors.email} error="E-mail is required">
<Input {...register('email', { required: true })} id={emailId} placeholder="roger.waters@grafana.com" />
</Field>
<Field label="Username">
<Input {...register('username')} placeholder="mr.waters" />
</Field>
<Field label="Nested object">
<Input {...register('nested.path')} placeholder="Nested path" />
</Field>
<Field label="Username">
<Input {...register('username')} placeholder="mr.waters" id={usernameId} />
</Field>
<Field label="Nested object">
<Input {...register('nested.path')} placeholder="Nested path" id={nestedPathId} />
</Field>
<Field label="Textarea" invalid={!!errors.text} error="Text is required">
<TextArea {...register('text', { required: true })} placeholder="Long text" />
</Field>
<Field label="Textarea" invalid={!!errors.text} error="Text is required">
<TextArea {...register('text', { required: true })} placeholder="Long text" id={textId} />
</Field>
<Field label="Checkbox" invalid={!!errors.checkbox} error="We need your consent">
<Checkbox {...register('checkbox', { required: true })} label="Do you consent?" />
</Field>
<Field label="Checkbox" invalid={!!errors.checkbox} error="We need your consent">
<Checkbox {...register('checkbox', { required: true })} label="Do you consent?" id={checkboxId} />
</Field>
<Field label="Switch">
<Switch name="switch" {...register} />
</Field>
<Field label="Switch">
<Switch name="switch" {...register} id={switchId} />
</Field>
<Field label="RadioButton">
<InputControl
name="radio"
control={control}
render={({ field }) => <RadioButtonGroup {...field} options={selectOptions} />}
/>
</Field>
<Field label="RadioButton" htmlFor={radioId}>
<InputControl
name="radio"
control={control}
render={({ field }) => <RadioButtonGroup {...field} options={selectOptions} id={radioId} />}
/>
</Field>
<Field label="Select" invalid={!!errors.select} error="Select is required">
<InputControl
name="select"
control={control}
rules={{
required: true,
}}
render={({ field }) => <Select {...field} options={selectOptions} />}
/>
</Field>
<Field label="Select" invalid={!!errors.select} error="Select is required" htmlFor={selectId}>
<InputControl
name="select"
control={control}
rules={{
required: true,
}}
render={({ field }) => <Select {...field} options={selectOptions} inputId={selectId} />}
/>
</Field>
<Button type="submit">Update</Button>
</>
);
}}
</Form>
);
<Button type="submit">Update</Button>
</>
);
}}
</Form>
);
};
export const Basic = () => {
return <>{renderForm()}</>;
@@ -1,5 +1,5 @@
import { Meta, StoryFn } from '@storybook/react';
import { useState } from 'react';
import { useId, useState } from 'react';
import { Combobox } from '../Combobox/Combobox';
import { Input } from '../Input/Input';
@@ -23,15 +23,14 @@ const meta: Meta<typeof InlineField> = {
controls: {
exclude: ['htmlFor', 'className', 'children'],
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
};
export const basic: StoryFn<typeof InlineField> = (args) => {
const id = useId();
return (
<InlineField {...args}>
<Input placeholder="Inline input" />
<Input placeholder="Inline input" id={id} />
</InlineField>
);
};
@@ -50,9 +49,10 @@ basic.args = {
};
export const withTooltip: StoryFn<typeof InlineField> = (args) => {
const id = useId();
return (
<InlineField {...args}>
<Input placeholder="Inline input" />
<Input placeholder="Inline input" id={id} />
</InlineField>
);
};
@@ -64,9 +64,10 @@ withTooltip.args = {
};
export const grow: StoryFn<typeof InlineField> = (args) => {
const id = useId();
return (
<InlineField {...args}>
<Input placeholder="Inline input" />
<Input placeholder="Inline input" id={id} />
</InlineField>
);
};
@@ -83,9 +84,10 @@ export const withCombobox: StoryFn<typeof InlineField> = (args) => {
{ value: 2, label: 'Two' },
];
const [selected, setSelected] = useState(1);
const id = useId();
return (
<InlineField {...args}>
<Combobox width={16} onChange={(v) => setSelected(v.value)} options={comboboxOptions} value={selected} />
<Combobox width={16} onChange={(v) => setSelected(v.value)} options={comboboxOptions} value={selected} id={id} />
</InlineField>
);
};
@@ -96,25 +98,29 @@ withCombobox.args = {
};
export const multiple: StoryFn<typeof InlineField> = () => {
const id1 = useId();
const id2 = useId();
const id3 = useId();
return (
<>
<InlineField label="Field 1">
<Input placeholder="Inline input" />
<Input placeholder="Inline input" id={id1} />
</InlineField>
<InlineField label="Field 2">
<Input placeholder="Inline input" />
<Input placeholder="Inline input" id={id2} />
</InlineField>
<InlineField label="Field 3">
<Input placeholder="Inline input" />
<Input placeholder="Inline input" id={id3} />
</InlineField>
</>
);
};
export const error: StoryFn<typeof InlineField> = (args) => {
const id = useId();
return (
<InlineField {...args}>
<Input placeholder="Inline input" />
<Input placeholder="Inline input" id={id} />
</InlineField>
);
};
@@ -1,7 +1,8 @@
import { Meta, StoryFn } from '@storybook/react';
import { useState } from 'react';
import { useId, useState } from 'react';
import { ClipboardButton } from '../ClipboardButton/ClipboardButton';
import { Field } from '../Forms/Field';
import { Input } from '../Input/Input';
import { InlineToast as InlineToastImpl, InlineToastProps } from './InlineToast';
@@ -14,8 +15,6 @@ const story: Meta = {
docs: {
page: mdx,
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
argTypes: {
referenceElement: {
@@ -30,13 +29,16 @@ export default story;
export const InlineToast: StoryFn<InlineToastProps> = (args) => {
const [el, setEl] = useState<null | HTMLInputElement>(null);
const id = useId();
return (
<div style={{ maxWidth: 500, width: `calc(100% - 100px)` }}>
<InlineToastImpl {...args} referenceElement={el}>
Saved
</InlineToastImpl>
<Input ref={setEl} />
<Field label="Input with InlineToast">
<Input ref={setEl} id={id} />
</Field>
</div>
);
};
@@ -1,5 +1,5 @@
import { StoryFn, Meta } from '@storybook/react';
import { useState } from 'react';
import { useId, useState } from 'react';
import { KeyValue } from '@grafana/data';
@@ -31,8 +31,6 @@ const meta: Meta = {
controls: {
exclude: ['prefix', 'suffix', 'addonBefore', 'addonAfter'],
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
args: {
type: 'text',
@@ -73,20 +71,24 @@ export const Simple: StoryFn = (args) => {
const addonBefore = <div style={{ display: 'flex', alignItems: 'center', padding: '5px' }}>Input</div>;
const prefix = parseAccessory(args.prefixVisible);
const suffix = parseAccessory(args.suffixVisible);
const id = useId();
return (
<Input
disabled={args.disabled}
width={args.width}
prefix={prefix}
invalid={args.invalid}
suffix={suffix}
loading={args.loading}
addonBefore={args.before && addonBefore}
addonAfter={args.after && addonAfter}
type={args.type}
placeholder={args.placeholder}
/>
<Field label="Simple input">
<Input
id={id}
disabled={args.disabled}
width={args.width}
prefix={prefix}
invalid={args.invalid}
suffix={suffix}
loading={args.loading}
addonBefore={args.before && addonBefore}
addonAfter={args.after && addonAfter}
type={args.type}
placeholder={args.placeholder}
/>
</Field>
);
};
Simple.args = {
@@ -98,11 +100,12 @@ Simple.args = {
export const WithFieldValidation: StoryFn = (args) => {
const [value, setValue] = useState('');
const id = useId();
return (
<div>
<Field invalid={value === ''} error={value === '' ? 'This input is required' : ''}>
<Input value={value} onChange={(e) => setValue(e.currentTarget.value)} {...args} />
<Field invalid={value === ''} error={value === '' ? 'This input is required' : ''} label="Input with validation">
<Input id={id} value={value} onChange={(e) => setValue(e.currentTarget.value)} {...args} />
</Field>
</div>
);
@@ -1,7 +1,7 @@
import { Meta, StoryFn } from '@storybook/react';
import { useTheme2 } from '../../../themes/ThemeContext';
import { SpacingTokenControl } from '../../../utils/storybook/themeStorybookControls';
import { Card } from '../../Card/Card';
import { Grid } from './Grid';
import mdx from './Grid.mdx';
@@ -17,8 +17,6 @@ const meta: Meta<typeof Grid> = {
docs: {
page: mdx,
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
args: {
gap: 1,
@@ -26,13 +24,12 @@ const meta: Meta<typeof Grid> = {
};
export const ColumnsNumber: StoryFn<typeof Grid> = (args) => {
const theme = useTheme2();
return (
<Grid {...args}>
{Array.from({ length: 9 }).map((_, i) => (
<div key={i} style={{ background: theme.colors.background.secondary, textAlign: 'center', ...dimensions[i] }}>
N# {i}
</div>
<Card key={i} style={dimensions[i]}>
<Card.Heading>N# {i}</Card.Heading>
</Card>
))}
</Grid>
);
@@ -56,13 +53,12 @@ ColumnsNumber.parameters = {
};
export const ColumnsMinWidth: StoryFn<typeof Grid> = (args) => {
const theme = useTheme2();
return (
<Grid {...args}>
{Array.from({ length: 9 }).map((_, i) => (
<div key={i} style={{ background: theme.colors.background.secondary, textAlign: 'center' }}>
N# {i}
</div>
<Card key={i}>
<Card.Heading>N# {i}</Card.Heading>
</Card>
))}
</Grid>
);
@@ -17,8 +17,6 @@ const meta: Meta<typeof LoadingBar> = {
docs: {
page: mdx,
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
};
@@ -27,7 +27,7 @@ export function LoadingBar({ width, delay = DEFAULT_ANIMATION_DELAY, ariaLabel =
return (
<div style={containerStyles}>
<div aria-label={ariaLabel} className={styles.bar} />
<div aria-label={ariaLabel} role="status" className={styles.bar} />
</div>
);
}
@@ -223,7 +223,7 @@ export const Examples = () => {
<Button size="sm" variant="secondary" key="A">
Breakdown
</Button>,
<Button size="sm" variant="secondary" icon="times" key="B" />,
<Button aria-label="Close" size="sm" variant="secondary" icon="times" key="B" />,
],
})}
{renderPanel('With radio button', {
@@ -12,7 +12,7 @@ import { SegmentSection } from './SegmentSection';
const AddButton = (
<span className="gf-form-label query-part">
<Icon name="plus-circle" />
<Icon aria-label="Add" name="plus-circle" />
</span>
);
@@ -62,10 +62,6 @@ export const ArrayOptions = () => {
const meta: Meta<typeof Segment> = {
title: 'Inputs/Segment',
component: Segment,
parameters: {
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
};
export const ArrayOptionsWithPrimitiveValue = () => {
@@ -13,7 +13,7 @@ import { SegmentSection } from './SegmentSection';
const AddButton = (
<span className="gf-form-label query-part">
<Icon name="plus" />
<Icon aria-label="Add" name="plus" />
</span>
);
@@ -63,10 +63,6 @@ export const ArrayOptions = () => {
const meta: Meta<typeof SegmentAsync> = {
title: 'Inputs/SegmentAsync',
component: SegmentAsync,
parameters: {
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
};
export const ArrayOptionsWithPrimitiveValue = () => {
@@ -32,10 +32,6 @@ export const BasicInput = () => {
const meta: Meta<typeof SegmentInput> = {
title: 'Inputs/SegmentInput',
component: SegmentInput,
parameters: {
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
};
export const BasicInputWithPlaceholder = () => {
@@ -98,6 +94,7 @@ export const InputWithAutoFocus = () => {
<InputComponent initialValue="test" key={i} />
))}
<button
aria-label="Add"
type="button"
className="gf-form-label query-part"
onClick={() => {
@@ -1,10 +1,8 @@
import { css } from '@emotion/css';
import { GrafanaTheme, GrafanaTheme2 } from '@grafana/data';
export const getSegmentStyles = (theme: GrafanaTheme | GrafanaTheme2) => {
const palette = 'v1' in theme ? theme.v1.palette : theme.palette;
import { GrafanaTheme2 } from '@grafana/data';
export const getSegmentStyles = (theme: GrafanaTheme2) => {
return {
segment: css({
cursor: 'pointer',
@@ -12,7 +10,7 @@ export const getSegmentStyles = (theme: GrafanaTheme | GrafanaTheme2) => {
}),
queryPlaceholder: css({
color: palette.gray2,
color: theme.colors.text.disabled,
}),
disabled: css({
@@ -1,10 +1,10 @@
import { Meta } from '@storybook/react';
import { useMemo } from 'react';
import { useId, useMemo } from 'react';
import ReactSelect, { createFilter } from 'react-select';
import { SelectableValue } from '@grafana/data';
import { Label } from '../Forms/Label';
import { Field } from '../Forms/Field';
import { Select, VirtualizedSelect } from './Select';
@@ -16,10 +16,6 @@ const meta: Meta = {
control: { type: 'number' },
},
},
parameters: {
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
};
export default meta;
@@ -37,6 +33,10 @@ function customFilter(opt: SelectableValue, searchQuery: string) {
}
export function PerformanceScenarios({ numberOfOptions }: { numberOfOptions: number }) {
const virtualId = useId();
const virtualIgnoreAccentsId = useId();
const normalId = useId();
const standardId = useId();
const options = useMemo(() => {
const opts: SelectableValue[] = [];
const ALPHABET = 'qwertyuiopasdfghjklzxcvbnm'.split('');
@@ -51,20 +51,29 @@ export function PerformanceScenarios({ numberOfOptions }: { numberOfOptions: num
return (
<div>
<Label>Virtual:</Label>
<VirtualizedSelect options={options} onChange={() => {}} />
<Field label="Virtual:">
<VirtualizedSelect inputId={virtualId} options={options} onChange={() => {}} />
</Field>
<br />
<Label>Virtual with ignoreAccents false:</Label>
<VirtualizedSelect filterOption={customFilter} options={options} onChange={() => {}} />
<Field label="Virtual with ignoreAccents false:">
<VirtualizedSelect
inputId={virtualIgnoreAccentsId}
filterOption={customFilter}
options={options}
onChange={() => {}}
/>
</Field>
<br />
<Label>Normal:</Label>
<Select options={options} onChange={() => {}} />
<Field label="Normal:">
<Select inputId={normalId} options={options} onChange={() => {}} />
</Field>
<br />
<Label>Standard react-select</Label>
<ReactSelect options={options} onChange={() => {}} />
<Field label="Standard react-select">
<ReactSelect inputId={standardId} options={options} onChange={() => {}} />
</Field>
<br />
<p>Rendered with {options.length.toLocaleString()} options</p>
@@ -2,6 +2,8 @@ import { action } from '@storybook/addon-actions';
import { Meta, StoryFn } from '@storybook/react';
import { PureComponent } from 'react';
import { Field } from '../Forms/Field';
import { Props, StatsPicker } from './StatsPicker';
interface State {
@@ -21,17 +23,20 @@ class WrapperWithState extends PureComponent<Props, State> {
const { stats } = this.state;
return (
<StatsPicker
placeholder={placeholder}
allowMultiple={allowMultiple}
stats={stats}
onChange={(stats: string[]) => {
action('Picked:')(stats);
this.setState({ stats });
}}
menuPlacement={menuPlacement}
width={width}
/>
<Field label="Pick stats">
<StatsPicker
inputId="stats-picker"
placeholder={placeholder}
allowMultiple={allowMultiple}
stats={stats}
onChange={(stats: string[]) => {
action('Picked:')(stats);
this.setState({ stats });
}}
menuPlacement={menuPlacement}
width={width}
/>
</Field>
);
}
}
@@ -43,8 +48,6 @@ const meta: Meta<typeof StatsPicker> = {
controls: {
exclude: ['onChange', 'stats', 'defaultStat', 'className'],
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
};
@@ -1,5 +1,5 @@
import { Meta, StoryFn } from '@storybook/react';
import { useState, useCallback } from 'react';
import { useState, useCallback, useId } from 'react';
import * as React from 'react';
import { Field } from '../Forms/Field';
@@ -16,8 +16,6 @@ const meta: Meta<typeof Switch> = {
docs: {
page: mdx,
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
args: {
disabled: false,
@@ -63,7 +61,12 @@ export const Uncontrolled: StoryFn<typeof Switch> = (args) => {
(e: React.FormEvent<HTMLInputElement>) => setChecked(e.currentTarget.checked),
[setChecked]
);
return <Switch value={checked} disabled={args.disabled} onChange={onChange} invalid={args.invalid} />;
const id = useId();
return (
<Field label="Uncontrolled switch" disabled={args.disabled} invalid={args.invalid}>
<Switch id={id} value={checked} onChange={onChange} />
</Field>
);
};
export default meta;
@@ -8,10 +8,6 @@ import { TableInputCSV } from './TableInputCSV';
const meta: Meta<typeof TableInputCSV> = {
title: 'Inputs/Deprecated/TableInputCSV',
component: TableInputCSV,
parameters: {
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
};
export const basic: StoryFn<typeof TableInputCSV> = () => {
@@ -118,8 +118,9 @@ const getStyles = stylesFactory((theme: GrafanaTheme2) => {
position: 'absolute',
bottom: '15px',
right: '15px',
border: '1px solid #222',
border: `1px solid ${theme.colors.success.border}`,
background: theme.colors.success.main,
color: theme.colors.success.contrastText,
padding: `1px ${theme.spacing(0.5)}`,
fontSize: '80%',
}),
@@ -14,8 +14,6 @@ const meta: Meta<typeof TagList> = {
controls: {
exclude: ['className', 'onClick', 'getAriaLabel'],
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
args: {
displayMax: 3,
@@ -49,10 +49,12 @@ const TagListComponent = memo(
</li>
))}
{displayMax && displayMax > 0 && numTags - displayMax > 0 && (
<span className={styles.moreTagsLabel}>
{'+ '}
{numTags - displayMax}
</span>
<li className={styles.li}>
<span className={styles.moreTagsLabel}>
{'+ '}
{numTags - displayMax}
</span>
</li>
)}
</ul>
);
@@ -13,8 +13,6 @@ const meta: Meta = {
docs: {
page: mdx,
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
argTypes: {
variant: {
@@ -11,8 +11,6 @@ const meta: Meta = {
showPanel: false,
},
docs: {},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
};
@@ -1,10 +1,9 @@
/* eslint-disable @grafana/i18n/no-untranslated-strings */
import { css, cx } from '@emotion/css';
import { useState } from 'react';
import { useId, useState } from 'react';
import * as React from 'react';
import tinycolor from 'tinycolor2';
import { GrafanaTheme2, ThemeRichColor, ThemeVizHue } from '@grafana/data';
import { colorManipulator, GrafanaTheme2, ThemeRichColor, ThemeVizHue } from '@grafana/data';
import { useTheme2 } from '../../themes/ThemeContext';
import { allButtonVariants, Button } from '../Button/Button';
@@ -65,6 +64,15 @@ export const ThemeDemo = () => {
const [boolValue, setBoolValue] = useState(false);
const [selectValue, setSelectValue] = useState('Item 2');
const t = useTheme2();
const inputId = useId();
const disabledInputId = useId();
const comboboxId = useId();
const radioId = useId();
const switchId = useId();
const switchTrueId = useId();
const switchDisabledId = useId();
const inlineId = useId();
const inlineDisabledId = useId();
const richColors = [
t.colors.primary,
@@ -169,36 +177,41 @@ export const ThemeDemo = () => {
<CollapsableSection label="Forms" isOpen={true}>
<DemoBox bg="primary">
<Field label="Input label" description="Field description">
<Input placeholder="Placeholder" />
<Input id={inputId} placeholder="Placeholder" />
</Field>
<Field label="Input disabled" disabled>
<Input placeholder="Placeholder" value="Disabled value" />
<Input id={disabledInputId} placeholder="Placeholder" value="Disabled value" />
</Field>
<Field label="Combobox">
<Combobox options={selectOptions} value={selectValue} onChange={(v) => setSelectValue(v?.value!)} />
<Combobox
id={comboboxId}
options={selectOptions}
value={selectValue}
onChange={(v) => setSelectValue(v?.value!)}
/>
</Field>
<Field label="Radio label">
<RadioButtonGroup options={radioOptions} value={radioValue} onChange={setRadioValue} />
<RadioButtonGroup id={radioId} options={radioOptions} value={radioValue} onChange={setRadioValue} />
</Field>
<Stack>
<Field label="Switch">
<Switch value={boolValue} onChange={(e) => setBoolValue(e.currentTarget.checked)} />
<Switch id={switchId} value={boolValue} onChange={(e) => setBoolValue(e.currentTarget.checked)} />
</Field>
<Field label="Switch true">
<Switch value={true} />
<Switch id={switchTrueId} value={true} />
</Field>
<Field label="Switch false disabled" disabled={true}>
<Switch value={false} />
<Switch id={switchDisabledId} value={false} />
</Field>
</Stack>
<Stack direction="column">
<div>Inline forms</div>
<InlineFieldRow>
<InlineField label="Label">
<Input placeholder="Placeholder" />
<Input id={inlineId} placeholder="Placeholder" />
</InlineField>
<InlineField label="Another Label" disabled>
<Input placeholder="Disabled" />
<Input id={inlineDisabledId} placeholder="Disabled" />
</InlineField>
</InlineFieldRow>
</Stack>
@@ -265,7 +278,7 @@ export function VizHuesDemo({ theme, color }: VizHuesDemoProps) {
className={css({
background: shade.color,
borderRadius: theme.shape.radius.default,
color: tinycolor(shade.color).isLight() ? 'black' : 'white',
color: colorManipulator.getContrastRatio('#FFFFFF', shade.color) >= 4.5 ? '#FFFFFF' : '#000000',
padding: theme.spacing(1),
})}
>
@@ -1,5 +1,5 @@
import { Meta, StoryFn } from '@storybook/react';
import { useState } from 'react';
import { useId, useState } from 'react';
import * as React from 'react';
import { Divider } from '../Divider/Divider';
@@ -9,10 +9,6 @@ import { Text } from '../Text/Text';
const meta: Meta = {
title: 'Developers/Typography',
parameters: {
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
};
const FONT_WEIGHTS = [/*100, 200, 300, */ 400, 500 /*600, 700, 800, 900*/];
@@ -20,6 +16,8 @@ const FONT_WEIGHTS = [/*100, 200, 300, */ 400, 500 /*600, 700, 800, 900*/];
export const TypographySamples: StoryFn = () => {
const [fontWeight, setFontWeight] = useState(400);
const [fontSize, setFontSize] = useState(30);
const fontWeightId = useId();
const fontSizeId = useId();
const handleFontWeightChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setFontWeight(Number(event.target.value));
@@ -33,6 +31,7 @@ export const TypographySamples: StoryFn = () => {
<div style={{ fontSynthesis: 'none' }}>
<Field label={`Font weight - ${fontWeight}`}>
<input
id={fontWeightId}
type="range"
min={100}
max={900}
@@ -45,6 +44,7 @@ export const TypographySamples: StoryFn = () => {
<Field label={`Font size - ${fontSize}`}>
<input
id={fontSizeId}
type="range"
min={8}
max={100}
@@ -18,8 +18,6 @@ const meta: Meta<typeof ToolbarButton> = {
controls: {
exclude: ['imgSrc', 'imgAlt', 'narrow'],
},
// TODO fix a11y issue in story and remove this
a11y: { test: 'off' },
},
args: {
variant: 'default',
@@ -132,12 +130,12 @@ export const Examples: StoryFn<typeof ToolbarButton> = (args) => {
<ToolbarButton variant="active" icon="clock-nine" tooltip="Time picker">
2020-10-02
</ToolbarButton>
<ToolbarButton variant="active" icon="search-minus" />
<ToolbarButton aria-label="Zoom out" variant="active" icon="search-minus" />
</ButtonGroup>
<br />
<ButtonGroup>
<ToolbarButton variant="canvas" icon="sync" />
<ToolbarButton variant="canvas" isOpen={false} narrow />
<ToolbarButton aria-label="Refresh" variant="canvas" icon="sync" />
<ToolbarButton aria-label="Open menu" variant="canvas" isOpen={false} narrow />
</ButtonGroup>
<br />
Inside button group
@@ -146,13 +144,13 @@ export const Examples: StoryFn<typeof ToolbarButton> = (args) => {
<ToolbarButton variant="primary" icon="sync">
Run query
</ToolbarButton>
<ToolbarButton isOpen={false} narrow variant="primary" />
<ToolbarButton aria-label="Open menu" isOpen={false} narrow variant="primary" />
</ButtonGroup>
<ButtonGroup>
<ToolbarButton variant="destructive" icon="sync">
Run query
</ToolbarButton>
<ToolbarButton isOpen={false} narrow variant="destructive" />
<ToolbarButton aria-label="Open menu" isOpen={false} narrow variant="destructive" />
</ButtonGroup>
</Stack>
</Stack>
@@ -1,7 +1,8 @@
import { css } from '@emotion/css';
import * as React from 'react';
import { useTheme2 } from '../../themes/ThemeContext';
import { Stack } from '../../components/Layout/Stack/Stack';
import { Text } from '../../components/Text/Text';
export interface Props {
name: string;
@@ -9,20 +10,17 @@ export interface Props {
}
export const StoryExample = ({ name, children }: Props) => {
const theme = useTheme2();
const style = css({
width: '100%',
padding: '16px',
});
const heading = css({
color: theme.colors.text.secondary,
marginBottom: '16px',
});
return (
<div className={style}>
<h5 className={heading}>{name}</h5>
{children}
<Stack gap={2} direction="column">
<Text variant="h5">{name}</Text>
{children}
</Stack>
</div>
);
};
+4
View File
@@ -8272,6 +8272,9 @@
"card": {
"option": "option"
},
"carousel": {
"aria-label-open-image": "Open image preview"
},
"cascader": {
"clear-button": "Clear selection"
},
@@ -8284,6 +8287,7 @@
"aria-label-selected-color": "{{colorLabel}} color"
},
"confirm-button": {
"aria-label-delete": "Delete",
"cancel": "Cancel",
"confirmText-delete": "Delete"
},
+1 -1
View File
@@ -55,7 +55,7 @@ $text-color-emphasis: #000000;
// Links
// -------------------------
$link-color: rgba(36, 41, 46, 1);
$link-color-disabled: rgba(36, 41, 46, 0.64);
$link-color-disabled: rgba(36, 41, 46, 0.65);
$link-hover-color: #000000;
// Typography