From f59495c4169e4c039d06d30f0065cc4acf1c1b0d Mon Sep 17 00:00:00 2001 From: Tom Ratcliffe Date: Wed, 3 Sep 2025 13:31:09 +0100 Subject: [PATCH] Chore: Move a11y storybook check to eslint (#110425) --- .betterer.eslint.config.js | 5 ++ .betterer.results | 115 +++++++++++++++++-------------------- .betterer.ts | 22 ------- 3 files changed, 58 insertions(+), 84 deletions(-) diff --git a/.betterer.eslint.config.js b/.betterer.eslint.config.js index 9e213f97bed..e37de7feeaf 100644 --- a/.betterer.eslint.config.js +++ b/.betterer.eslint.config.js @@ -151,6 +151,11 @@ module.exports = [ // eslint-disable-next-line no-restricted-syntax message: 'gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.', }, + { + selector: + "Property[key.name='a11y'][value.type='ObjectExpression'] Property[key.name='test'][value.value='off']", + message: 'Skipping a11y tests is not allowed. Please fix the component or story instead.', + }, ], }, }, diff --git a/.betterer.results b/.betterer.results index 2d188d2fada..edea426c911 100644 --- a/.betterer.results +++ b/.betterer.results @@ -25,6 +25,9 @@ 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"] ], @@ -665,7 +668,8 @@ exports[`better eslint`] = { [0, 0, 0, "React Hook \\"useArgs\\" is called in function \\"render\\" 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"], [0, 0, 0, "React Hook \\"useArgs\\" is called in function \\"render\\" 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\\".", "2"], [0, 0, 0, "React Hook \\"useArgs\\" is called in function \\"render\\" 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\\".", "3"], - [0, 0, 0, "React Hook \\"useArgs\\" is called in function \\"render\\" 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\\".", "4"] + [0, 0, 0, "React Hook \\"useArgs\\" is called in function \\"render\\" 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\\".", "4"], + [0, 0, 0, "Skipping a11y tests is not allowed. Please fix the component or story instead.", "5"] ], "packages/grafana-ui/src/components/Combobox/MultiCombobox.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] @@ -710,6 +714,9 @@ exports[`better eslint`] = { [0, 0, 0, "React Hook \\"useCallback\\" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?", "1"], [0, 0, 0, "React Hook \\"useContext\\" is called conditionally. React Hooks must be called in the exact same order in every component render.", "2"] ], + "packages/grafana-ui/src/components/FileDropzone/FileDropzone.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/FormField/FormField.tsx:5381": [ [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "0"] ], @@ -718,6 +725,12 @@ exports[`better eslint`] = { [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "1"], [0, 0, 0, "gf-form usage has been deprecated. Use a component from @grafana/ui or custom CSS instead.", "2"] ], + "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"], @@ -781,25 +794,41 @@ exports[`better eslint`] = { [0, 0, 0, "Using localeCompare() can cause performance issues when sorting large datasets. Consider using Intl.Collator for better performance when sorting arrays, or add an eslint-disable comment if sorting a small, known dataset.", "0"] ], "packages/grafana-ui/src/components/InteractiveTable/InteractiveTable.story.tsx:5381": [ - [0, 0, 0, "Using localeCompare() can cause performance issues when sorting large datasets. Consider using Intl.Collator for better performance when sorting arrays, or add an eslint-disable comment if sorting a small, known dataset.", "0"], - [0, 0, 0, "Using localeCompare() can cause performance issues when sorting large datasets. Consider using Intl.Collator for better performance when sorting arrays, or add an eslint-disable comment if sorting a small, known dataset.", "1"] + [0, 0, 0, "Skipping a11y tests is not allowed. Please fix the component or story instead.", "0"], + [0, 0, 0, "Using localeCompare() can cause performance issues when sorting large datasets. Consider using Intl.Collator for better performance when sorting arrays, or add an eslint-disable comment if sorting a small, known dataset.", "1"], + [0, 0, 0, "Using localeCompare() can cause performance issues when sorting large datasets. Consider using Intl.Collator for better performance when sorting arrays, or add an eslint-disable comment if sorting a small, known dataset.", "2"] ], "packages/grafana-ui/src/components/JSONFormatter/json_explorer/json_explorer.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], + "packages/grafana-ui/src/components/Layout/Stack/Stack.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/MatchersUI/FieldValueMatcher.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], "packages/grafana-ui/src/components/MatchersUI/fieldMatchersUI.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], + "packages/grafana-ui/src/components/Menu/Menu.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/Modal/Modal.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/Modal/ModalsContext.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"], [0, 0, 0, "Unexpected any. Specify a different type.", "3"] ], + "packages/grafana-ui/src/components/PageLayout/PageToolbar.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/PanelChrome/PanelChrome.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/PanelChrome/PanelContext.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] @@ -807,6 +836,12 @@ exports[`better eslint`] = { "packages/grafana-ui/src/components/PanelChrome/index.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "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"] @@ -828,6 +863,9 @@ 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"], @@ -868,12 +906,21 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "14"], [0, 0, 0, "Unexpected any. Specify a different type.", "15"] ], + "packages/grafana-ui/src/components/Slider/RangeSlider.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/Slider/Slider.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/Table/Cells/TableCell.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], [0, 0, 0, "Do not use any type assertions.", "2"], [0, 0, 0, "Unexpected any. Specify a different type.", "3"] ], + "packages/grafana-ui/src/components/Table/Table.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/Table/TableCellInspector.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], @@ -942,6 +989,9 @@ exports[`better eslint`] = { "packages/grafana-ui/src/components/ValuePicker/ValuePicker.tsx:5381": [ [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] ], + "packages/grafana-ui/src/components/VizLegend/VizLegend.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/VizLegend/types.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] @@ -4516,62 +4566,3 @@ exports[`no undocumented stories`] = { ] }` }; - -exports[`no skipping a11y tests in stories`] = { - value: `{ - "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/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/FileDropzone/FileDropzone.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/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/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/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/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/Menu/Menu.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/Modal/Modal.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/PageLayout/PageToolbar.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/PanelChrome/PanelChrome.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/QueryField/QueryField.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/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/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/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/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/VizLegend/VizLegend.story.tsx:5381": [ - [0, 0, 0, "No skipping of a11y tests in stories. Please fix the component or story instead.", "5381"] - ] - }` -}; diff --git a/.betterer.ts b/.betterer.ts index 70220d23deb..0e3521e9c97 100644 --- a/.betterer.ts +++ b/.betterer.ts @@ -18,30 +18,8 @@ export default { .include('**/*.{ts,tsx}') .exclude(new RegExp(eslintPathsToIgnore.join('|'))), 'no undocumented stories': () => countUndocumentedStories().include('**/*.story.tsx'), - 'no skipping a11y tests in stories': () => countSkippedA11yTestStories().include('**/*.story.tsx'), }; -function countSkippedA11yTestStories() { - return new BettererFileTest(async (filePaths, fileTestResult) => { - await Promise.all( - filePaths.map(async (filePath) => { - // look for skipped a11y tests - const skipRegex = new RegExp("a11y: { test: 'off' }", 'gm'); - - const fileText = await fs.readFile(filePath, 'utf8'); - - const hasSkip = skipRegex.test(fileText); - if (hasSkip) { - // In this case the file contents don't matter: - const file = fileTestResult.addFile(filePath, ''); - // Add the issue to the first character of the file: - file.addIssue(0, 0, 'No skipping of a11y tests in stories. Please fix the component or story instead.'); - } - }) - ); - }); -} - function countUndocumentedStories() { return new BettererFileTest(async (filePaths, fileTestResult) => { await Promise.all(