mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: Add lint rule for no-unreduced-motion
(#85862)
* add lint rule for no-unreduced-motion * update to satisfy types
This commit is contained in:
parent
00daca7f43
commit
5e74b6962b
@ -4,7 +4,7 @@ This package contains custom eslint rules for use within the Grafana codebase on
|
|||||||
|
|
||||||
## Rules
|
## Rules
|
||||||
|
|
||||||
### `@grafana/no-aria-label-selectors`
|
### `no-aria-label-selectors`
|
||||||
|
|
||||||
Require aria-label JSX properties to not include selectors from the `@grafana/e2e-selectors` package.
|
Require aria-label JSX properties to not include selectors from the `@grafana/e2e-selectors` package.
|
||||||
|
|
||||||
@ -12,12 +12,20 @@ Previously we hijacked the aria-label property to use as E2E selectors as an att
|
|||||||
|
|
||||||
Now, we prefer using data-testid for E2E selectors.
|
Now, we prefer using data-testid for E2E selectors.
|
||||||
|
|
||||||
### `@grafana/no-border-radius-literal`
|
### `no-border-radius-literal`
|
||||||
|
|
||||||
Check if border-radius theme tokens are used.
|
Check if border-radius theme tokens are used.
|
||||||
|
|
||||||
To improve the consistency across Grafana we encourage devs to use tokens instead of custom values. In this case, we want the `borderRadius` to use the appropriate token such as `theme.shape.radius.default`, `theme.shape.radius.pill` or `theme.shape.radius.circle`.
|
To improve the consistency across Grafana we encourage devs to use tokens instead of custom values. In this case, we want the `borderRadius` to use the appropriate token such as `theme.shape.radius.default`, `theme.shape.radius.pill` or `theme.shape.radius.circle`.
|
||||||
|
|
||||||
### `@grafana/theme-token-usage`
|
### `no-unreduced-motion`
|
||||||
|
|
||||||
|
Avoid direct use of `animation*` or `transition*` properties.
|
||||||
|
|
||||||
|
To account for users with motion sensitivities, these should always be wrapped in a [`prefers-reduced-motion`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion) media query.
|
||||||
|
|
||||||
|
`@grafana/ui` exposes a `handledReducedMotion` utility function that can be used to handle this.
|
||||||
|
|
||||||
|
### `theme-token-usage`
|
||||||
|
|
||||||
Used to find all instances of `theme` tokens being used in the codebase and emit the counts as metrics. Should **not** be used as an actual lint rule!
|
Used to find all instances of `theme` tokens being used in the codebase and emit the counts as metrics. Should **not** be used as an actual lint rule!
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
const noAriaLabelSelectors = require('./rules/no-aria-label-e2e-selectors.cjs');
|
const noAriaLabelSelectors = require('./rules/no-aria-label-e2e-selectors.cjs');
|
||||||
const noBorderRadiusLiteral = require('./rules/no-border-radius-literal.cjs');
|
const noBorderRadiusLiteral = require('./rules/no-border-radius-literal.cjs');
|
||||||
|
const noUnreducedMotion = require('./rules/no-unreduced-motion.cjs');
|
||||||
const themeTokenUsage = require('./rules/theme-token-usage.cjs');
|
const themeTokenUsage = require('./rules/theme-token-usage.cjs');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
rules: {
|
rules: {
|
||||||
|
'no-unreduced-motion': noUnreducedMotion,
|
||||||
'no-aria-label-selectors': noAriaLabelSelectors,
|
'no-aria-label-selectors': noAriaLabelSelectors,
|
||||||
'no-border-radius-literal': noBorderRadiusLiteral,
|
'no-border-radius-literal': noBorderRadiusLiteral,
|
||||||
'theme-token-usage': themeTokenUsage,
|
'theme-token-usage': themeTokenUsage,
|
||||||
|
@ -15,10 +15,7 @@ const { ESLintUtils } = require('@typescript-eslint/utils');
|
|||||||
|
|
||||||
const GRAFANA_E2E_PACKAGE_NAME = '@grafana/e2e-selectors';
|
const GRAFANA_E2E_PACKAGE_NAME = '@grafana/e2e-selectors';
|
||||||
|
|
||||||
const createRule = ESLintUtils.RuleCreator(
|
const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/grafana/grafana/blob/main/packages/grafana-eslint-rules/README.md#${name}`);
|
||||||
// TODO: find a proper url?
|
|
||||||
(name) => `https://github.com/grafana/grafana#${name}`
|
|
||||||
);
|
|
||||||
|
|
||||||
// A relative simple lint rule that will look of the `selectors` export from @grafana/e2e-selectors
|
// A relative simple lint rule that will look of the `selectors` export from @grafana/e2e-selectors
|
||||||
// is used in an aria-label
|
// is used in an aria-label
|
||||||
@ -69,7 +66,6 @@ const rule = createRule({
|
|||||||
meta: {
|
meta: {
|
||||||
docs: {
|
docs: {
|
||||||
description: 'aria-label should not contain e2e selectors',
|
description: 'aria-label should not contain e2e selectors',
|
||||||
// recommended: 'error',
|
|
||||||
},
|
},
|
||||||
messages: {
|
messages: {
|
||||||
useDataTestId: 'Use data-testid for E2E selectors instead of aria-label',
|
useDataTestId: 'Use data-testid for E2E selectors instead of aria-label',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
const { ESLintUtils, AST_NODE_TYPES } = require('@typescript-eslint/utils');
|
const { ESLintUtils, AST_NODE_TYPES } = require('@typescript-eslint/utils');
|
||||||
|
|
||||||
const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/grafana/grafana#${name}`);
|
const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/grafana/grafana/blob/main/packages/grafana-eslint-rules/README.md#${name}`);
|
||||||
|
|
||||||
const borderRadiusRule = createRule({
|
const borderRadiusRule = createRule({
|
||||||
create(context) {
|
create(context) {
|
||||||
|
65
packages/grafana-eslint-rules/rules/no-unreduced-motion.cjs
Normal file
65
packages/grafana-eslint-rules/rules/no-unreduced-motion.cjs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// @ts-check
|
||||||
|
const { ESLintUtils, AST_NODE_TYPES } = require('@typescript-eslint/utils');
|
||||||
|
|
||||||
|
const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/grafana/grafana/blob/main/packages/grafana-eslint-rules/README.md#${name}`);
|
||||||
|
|
||||||
|
const restrictedProperties = ['animation', 'transition'];
|
||||||
|
|
||||||
|
const isRestrictedProperty = (/** @type string */ propertyName) => {
|
||||||
|
return restrictedProperties.some((prop) => propertyName.startsWith(prop));
|
||||||
|
};
|
||||||
|
|
||||||
|
const rule = createRule({
|
||||||
|
create(context) {
|
||||||
|
return {
|
||||||
|
CallExpression(node) {
|
||||||
|
if (
|
||||||
|
node.callee.type === AST_NODE_TYPES.Identifier &&
|
||||||
|
node.callee.name === 'css'
|
||||||
|
) {
|
||||||
|
const cssObjects = node.arguments.flatMap((node) => {
|
||||||
|
switch (node.type) {
|
||||||
|
case AST_NODE_TYPES.ObjectExpression:
|
||||||
|
return [node];
|
||||||
|
case AST_NODE_TYPES.ArrayExpression:
|
||||||
|
return node.elements.filter(v => v?.type === AST_NODE_TYPES.ObjectExpression);
|
||||||
|
default:
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const cssObject of cssObjects) {
|
||||||
|
if (cssObject?.type === AST_NODE_TYPES.ObjectExpression) {
|
||||||
|
for (const property of cssObject.properties) {
|
||||||
|
if (
|
||||||
|
property.type === AST_NODE_TYPES.Property &&
|
||||||
|
property.key.type === AST_NODE_TYPES.Identifier &&
|
||||||
|
isRestrictedProperty(property.key.name)
|
||||||
|
) {
|
||||||
|
context.report({
|
||||||
|
node: property,
|
||||||
|
messageId: 'noUnreducedMotion',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
name: 'no-unreduced-motion',
|
||||||
|
meta: {
|
||||||
|
type: 'problem',
|
||||||
|
docs: {
|
||||||
|
description: 'Check if animation or transition properties are used directly.',
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
noUnreducedMotion: 'Avoid direct use of `animation*` or `transition*` properties. Use the `handleReducedMotion` utility function or wrap in a `prefers-reduced-motion` media query.',
|
||||||
|
},
|
||||||
|
schema: [],
|
||||||
|
},
|
||||||
|
defaultOptions: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = rule;
|
@ -1,7 +1,7 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
const { ESLintUtils, AST_NODE_TYPES } = require('@typescript-eslint/utils');
|
const { ESLintUtils, AST_NODE_TYPES } = require('@typescript-eslint/utils');
|
||||||
|
|
||||||
const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/grafana/grafana#${name}`);
|
const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/grafana/grafana/blob/main/packages/grafana-eslint-rules/README.md#${name}`);
|
||||||
|
|
||||||
const themeTokenUsage = createRule({
|
const themeTokenUsage = createRule({
|
||||||
create(context) {
|
create(context) {
|
||||||
|
Loading…
Reference in New Issue
Block a user