Chore: Update grafana eslint config version and fix rule violations (#109666)

This commit is contained in:
Tom Ratcliffe
2025-08-21 11:59:41 +01:00
committed by GitHub
parent 6f940f855e
commit 429cb9d916
23 changed files with 71 additions and 13 deletions
+9 -1
View File
@@ -6,6 +6,7 @@ const jsxA11yPlugin = require('eslint-plugin-jsx-a11y');
const lodashPlugin = require('eslint-plugin-lodash');
const barrelPlugin = require('eslint-plugin-no-barrel-files');
const reactPlugin = require('eslint-plugin-react');
const hooksPlugin = require('eslint-plugin-react-hooks');
const testingLibraryPlugin = require('eslint-plugin-testing-library');
const grafanaConfig = require('@grafana/eslint-config/flat');
@@ -16,7 +17,7 @@ const grafanaI18nPlugin = require('@grafana/i18n/eslint-plugin');
// as we just want to pull in all of the necessary configuration but not run the rules
// (this should only be concerned with checking rules that we want to improve,
// so there's no need to try and run the rules that will be linted properly anyway)
const { rules, ...baseConfig } = grafanaConfig;
const { rules, ...baseConfig } = grafanaConfig.find((config) => config?.name === '@grafana/eslint-config/flat');
/**
* @type {Array<import('eslint').Linter.Config>}
@@ -53,6 +54,13 @@ module.exports = [
// @ts-ignore - not sure why but flat config is typed as a maybe?
...reactPlugin.configs.flat['jsx-runtime'],
},
// FIXME: Remove once eslint-config-grafana is updated to include correct plugin
{
name: 'react-hooks-plugin',
plugins: {
'react-hooks': hooksPlugin,
},
},
{
files: ['**/*.{ts,tsx,js}'],
...baseConfig,
+23 -4
View File
@@ -8,6 +8,8 @@ const jsxA11yPlugin = require('eslint-plugin-jsx-a11y');
const lodashPlugin = require('eslint-plugin-lodash');
const barrelPlugin = require('eslint-plugin-no-barrel-files');
const reactPlugin = require('eslint-plugin-react');
// FIXME: Remove once eslint-config-grafana is updated to include correct plugin
const hooksPlugin = require('eslint-plugin-react-hooks');
const testingLibraryPlugin = require('eslint-plugin-testing-library');
const unicornPlugin = require('eslint-plugin-unicorn');
@@ -58,13 +60,20 @@ module.exports = [
'public/build-swagger', // swagger build output
],
},
// FIXME: Remove once eslint-config-grafana is updated to include correct plugin
{
name: 'react-hooks-plugin',
plugins: {
'react-hooks': hooksPlugin,
},
},
// Conditionally run the betterer rules if enabled in dev's config
...(enableBettererRules ? bettererConfig : []),
grafanaConfig,
// FIXME: Remove filtering once eslint-config-grafana is updated to include correct plugin
...grafanaConfig.filter(Boolean),
{
name: 'react/jsx-runtime',
// @ts-ignore - not sure why but flat config is typed as a maybe?
...reactPlugin.configs.flat['jsx-runtime'],
name: 'react/jsx-runtime-rules',
rules: reactPlugin.configs.flat['jsx-runtime'].rules,
},
{
name: 'grafana/defaults',
@@ -165,6 +174,8 @@ module.exports = [
message: 'No bare anchor nodes containing only text. Use `TextLink` instead.',
},
],
// FIXME: Fix these in follow up PR
'react/no-unescaped-entities': 'off',
},
},
{
@@ -366,6 +377,14 @@ module.exports = [
'testing-library/no-container': 'off',
},
},
{
name: 'grafana/test-disables',
files: ['**/*.{spec,test}.{ts,tsx}'],
rules: {
'react/display-name': 'off',
'react/no-children-prop': 'off',
},
},
{
name: 'grafana/explore-traceview-overrides',
files: ['public/app/features/explore/TraceView/components/demo/**/*.{ts,tsx,js,jsx}'],
+1 -1
View File
@@ -89,7 +89,7 @@
"@crowdin/crowdin-api-client": "^1.42.0",
"@cypress/webpack-preprocessor": "6.0.4",
"@emotion/eslint-plugin": "11.12.0",
"@grafana/eslint-config": "8.0.0",
"@grafana/eslint-config": "8.1.0",
"@grafana/eslint-plugin": "link:./packages/grafana-eslint-rules",
"@grafana/plugin-e2e": "2.1.7",
"@grafana/test-utils": "workspace:*",
@@ -38,6 +38,7 @@ export const ValuePill = forwardRef<HTMLSpanElement, ValuePillProps>(
);
}
);
ValuePill.displayName = 'ValuePill';
const getValuePillStyles = (theme: GrafanaTheme2, disabled?: boolean) => ({
wrapper: css({
@@ -20,6 +20,7 @@ import { SparklineCell, getStyles as getSparklineCellStyles } from './SparklineC
const AutoCellRenderer = memo((props: TableCellRendererProps) => (
<AutoCell value={props.value} field={props.field} rowIdx={props.rowIdx} />
));
AutoCellRenderer.displayName = 'AutoCellRenderer';
function isCustomCellOptions(options: TableCellOptions): options is TableCustomCellOptions {
return options.type === TableCellDisplayMode.Custom;
@@ -56,16 +57,19 @@ const CELL_REGISTRY: Record<TableCellOptions['type'], CellRegistryEntry> = {
getStyles: mixinAutoCellStyles(getJsonCellStyles),
},
[TableCellDisplayMode.Actions]: {
// eslint-disable-next-line react/display-name
renderer: memo((props: TableCellRendererProps) => (
<ActionsCell field={props.field} rowIdx={props.rowIdx} getActions={props.getActions ?? (() => [])} />
)),
getStyles: getActionsCellStyles,
},
[TableCellDisplayMode.DataLinks]: {
// eslint-disable-next-line react/display-name
renderer: memo((props: TableCellRendererProps) => <DataLinksCell field={props.field} rowIdx={props.rowIdx} />),
getStyles: getDataLinksStyles,
},
[TableCellDisplayMode.Gauge]: {
// eslint-disable-next-line react/display-name
renderer: memo((props: TableCellRendererProps) => (
<BarGaugeCell
field={props.field}
@@ -78,6 +82,7 @@ const CELL_REGISTRY: Record<TableCellOptions['type'], CellRegistryEntry> = {
)),
},
[TableCellDisplayMode.Sparkline]: {
// eslint-disable-next-line react/display-name
renderer: memo((props: TableCellRendererProps) => (
<SparklineCell
value={props.value}
@@ -91,16 +96,19 @@ const CELL_REGISTRY: Record<TableCellOptions['type'], CellRegistryEntry> = {
getStyles: getSparklineCellStyles,
},
[TableCellDisplayMode.Geo]: {
// eslint-disable-next-line react/display-name
renderer: memo((props: TableCellRendererProps) => <GeoCell value={props.value} height={props.height} />),
getStyles: getGeoCellStyles,
},
[TableCellDisplayMode.Image]: {
// eslint-disable-next-line react/display-name
renderer: memo((props: TableCellRendererProps) => (
<ImageCell cellOptions={props.cellOptions} field={props.field} value={props.value} rowIdx={props.rowIdx} />
)),
getStyles: getImageStyles,
},
[TableCellDisplayMode.Pill]: {
// eslint-disable-next-line react/display-name
renderer: memo((props: TableCellRendererProps) => (
<PillCell rowIdx={props.rowIdx} field={props.field} theme={props.theme} />
)),
@@ -108,6 +116,7 @@ const CELL_REGISTRY: Record<TableCellOptions['type'], CellRegistryEntry> = {
testField: (field: Field) => field.type === FieldType.string,
},
[TableCellDisplayMode.Markdown]: {
// eslint-disable-next-line react/display-name
renderer: memo((props: TableCellRendererProps) => (
<MarkdownCell field={props.field} rowIdx={props.rowIdx} disableSanitizeHtml={props.disableSanitizeHtml} />
)),
@@ -115,6 +124,7 @@ const CELL_REGISTRY: Record<TableCellOptions['type'], CellRegistryEntry> = {
testField: (field: Field) => field.type === FieldType.string,
},
[TableCellDisplayMode.Custom]: {
// eslint-disable-next-line react/display-name
renderer: memo((props: TableCellRendererProps) => {
if (!isCustomCellOptions(props.cellOptions) || !props.cellOptions.cellComponent) {
return null; // nonsensical case, but better to typeguard it than throw.
@@ -30,3 +30,4 @@ export const MaybeWrapWithLink = memo(({ field, rowIdx, children }: MaybeWrapWit
// raw value
return children;
});
MaybeWrapWithLink.displayName = 'MaybeWrapWithLink';
@@ -787,6 +787,7 @@ export function TableNG(props: TableNGProps) {
*/
const renderRowFactory =
(fields: Field[], panelContext: PanelContext, expandedRows: Set<number>, enableSharedCrosshair: boolean) =>
// eslint-disable-next-line react/display-name
(key: React.Key, props: RenderRowProps<TableRow, TableSummaryRow>): React.ReactNode => {
const { row } = props;
const rowIdx = row.__index;
@@ -81,3 +81,4 @@ export const TableCellActions = memo(
</div>
)
);
TableCellActions.displayName = 'TableCellActions';
@@ -157,3 +157,4 @@ export const TableCellTooltip = memo(
);
}
);
TableCellTooltip.displayName = 'TableCellTooltip';
@@ -40,6 +40,7 @@ export const TopSearchBarCommandPaletteTrigger = React.memo(() => {
return <PretendTextInput onClick={onOpenSearch} />;
});
TopSearchBarCommandPaletteTrigger.displayName = 'TopSearchBarCommandPaletteTrigger';
interface PretendTextInputProps {
onClick: () => void;
@@ -104,3 +104,4 @@ export const DashboardPicker = forwardRef<HTMLElement, Props>(
);
}
);
DashboardPicker.displayName = 'DashboardPicker';
@@ -54,3 +54,4 @@ export const DeleteModal = React.memo(({ onConfirm, onDismiss, isOpen, folderNam
/>
);
});
DeleteModal.displayName = 'DeleteModal';
@@ -37,5 +37,6 @@ const SilenceGrafanaRuleDrawer = React.memo(
),
(prevProps, nextProps) => prevProps.ruleUid === nextProps.ruleUid
);
SilenceGrafanaRuleDrawer.displayName = 'SilenceGrafanaRuleDrawer';
export default SilenceGrafanaRuleDrawer;
@@ -152,6 +152,7 @@ const ListItem = forwardRef<HTMLDivElement, ListItemProps>(
);
}
);
ListItem.displayName = 'ListItem';
const getStyles = (theme: GrafanaTheme2) => ({
listItem: css({
@@ -470,6 +470,7 @@ const DataSourceLogo = forwardRef<HTMLImageElement, DataSourceLogoProps>(({ data
/>
);
});
DataSourceLogo.displayName = 'DataSourceLogo';
const dataSourceLogoStyles = (theme: GrafanaTheme2) => ({
logo: css({
@@ -100,3 +100,4 @@ export const ResourceEditFormSharedFields = memo<DashboardEditFormSharedFieldsPr
);
}
);
ResourceEditFormSharedFields.displayName = 'ResourceEditFormSharedFields';
@@ -24,6 +24,7 @@ export function AutoGridItemRenderer({ model }: SceneComponentProps<AutoGridItem
const Wrapper = useMemo(
() =>
// eslint-disable-next-line react/display-name
memo(
({
item,
@@ -165,6 +165,7 @@ export const OptionsPaneCategory = React.memo(
);
}
);
OptionsPaneCategory.displayName = 'OptionsPaneCategory';
const getStyles = (theme: GrafanaTheme2) => ({
box: css({
@@ -328,3 +328,4 @@ export const LogRows = memo(
);
}
);
LogRows.displayName = 'LogRows';
@@ -285,7 +285,12 @@ const OrganizeFieldsTransformerEditor = ({ options, input, onChange }: OrganizeF
<>
<div ref={provided.innerRef} className={styles.labelsDraggable} {...provided.droppableProps}>
{uiOrderByItems.map((item, idx) => (
<DraggableUIOrderByItem item={item} index={idx} onChangeSort={onChangeSort} />
<DraggableUIOrderByItem
item={item}
index={idx}
onChangeSort={onChangeSort}
key={`${item.name}-${item.type}`}
/>
))}
</div>
{provided.placeholder}
@@ -131,6 +131,7 @@ const makeRenderGroupBy = (
onDeleteGroupBy: (propertyName: string) => void,
templateVariableOptions: SelectableValue<string>
) => {
// eslint-disable-next-line react/display-name
return (
item: BuilderQueryEditorGroupByExpression,
onChangeItem: (updatedItem: BuilderQueryEditorGroupByExpression) => void,
@@ -155,7 +155,7 @@ export const TimeRegionEditor = ({ value, onChange }: Props) => {
description={
<>
{t('dashboard-settings.time-regions.advanced-description-use', 'Use ')}
<a href="https://crontab.run/" target="_blank">
<a href="https://crontab.run/" target="_blank" rel="noreferrer">
{t('dashboard-settings.time-regions.advanced-description-cron', 'Cron syntax')}
</a>
{t(
+5 -5
View File
@@ -3171,9 +3171,9 @@ __metadata:
languageName: unknown
linkType: soft
"@grafana/eslint-config@npm:8.0.0":
version: 8.0.0
resolution: "@grafana/eslint-config@npm:8.0.0"
"@grafana/eslint-config@npm:8.1.0":
version: 8.1.0
resolution: "@grafana/eslint-config@npm:8.1.0"
peerDependencies:
"@stylistic/eslint-plugin-ts": ">=2.9.0"
"@typescript-eslint/eslint-plugin": ">=6.18.0"
@@ -3184,7 +3184,7 @@ __metadata:
eslint-plugin-react: ">=7.33.0"
eslint-plugin-react-hooks: ">=4.6.0"
typescript: ">=5.2.0"
checksum: 10/b9a875d9e157eff1212cbd2648f18a1b04ebdd2bbea7d13ecb63d5b441fb3f550aa452eccdbf3ccadedd25996e6fb33cba615e3dfdd21ce9e00b505287a51dc4
checksum: 10/d82f169de300dbb2ca62f74a4cb80461ab43e1adb5885e59a02c6080d9cef93f9aaac584b58b91f360014edf52b3dd1e81aad97e67011edc99de54a49deba7f2
languageName: node
linkType: hard
@@ -18304,7 +18304,7 @@ __metadata:
"@grafana/azure-sdk": "npm:0.0.7"
"@grafana/data": "workspace:*"
"@grafana/e2e-selectors": "workspace:*"
"@grafana/eslint-config": "npm:8.0.0"
"@grafana/eslint-config": "npm:8.1.0"
"@grafana/eslint-plugin": "link:./packages/grafana-eslint-rules"
"@grafana/faro-core": "npm:^1.19.0"
"@grafana/faro-web-sdk": "npm:^1.19.0"