This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a commit to branch chore/lint-cleanup-tech-debt-2
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 2c36133ed1268a0fa36d1641248a44ab68313ac2
Author: Evan Rusackas <[email protected]>
AuthorDate: Wed Feb 11 00:10:40 2026 -0800

    chore(lint): enforce more strict eslint/oxlint rules (batch 2)
    
    Upgrade more lint rules from warn to error:
    - unicorn/no-useless-length-check
    - no-constant-binary-expression
    - no-unsafe-optional-chaining
    
    Add new rules:
    - storybook/prefer-pascal-case
    - react-you-might-not-need-an-effect/no-reset-all-state-on-prop-change
    - react-you-might-not-need-an-effect/no-chain-state-updates
    - react-you-might-not-need-an-effect/no-event-handler
    - react-you-might-not-need-an-effect/no-derived-state
    
    Fix violations:
    - state.ts: Remove redundant array length checks (every() handles empty 
arrays)
    - buildQuery.ts: Move nullish coalescing inside Number() call
    - Modal.tsx: Remove unnecessary optional chaining on 
document.documentElement
    - Layout.test.tsx: Extract boolean to variable to avoid constant expression
    - transformProps.test.ts: Use non-null assertion instead of optional 
chaining
    
    Co-Authored-By: Claude Opus 4.5 <[email protected]>
---
 superset-frontend/.eslintrc.js                           | 10 ++++++++++
 superset-frontend/oxlint.json                            |  4 +++-
 .../src/components/Layout/Layout.test.tsx                |  6 ++++--
 .../superset-ui-core/src/components/Modal/Modal.tsx      |  2 +-
 .../plugins/plugin-chart-ag-grid-table/src/buildQuery.ts |  2 +-
 .../test/BigNumber/transformProps.test.ts                | 16 ++++++++++++----
 .../plugins/plugin-chart-table/src/buildQuery.ts         |  2 +-
 .../src/dashboard/components/nativeFilters/state.ts      | 10 ++++------
 8 files changed, 36 insertions(+), 16 deletions(-)

diff --git a/superset-frontend/.eslintrc.js b/superset-frontend/.eslintrc.js
index 5f4b50ff9d2..53a5b91670b 100644
--- a/superset-frontend/.eslintrc.js
+++ b/superset-frontend/.eslintrc.js
@@ -135,6 +135,7 @@ module.exports = {
     'icons',
     'i18n-strings',
     'react-prefer-function-component',
+    'react-you-might-not-need-an-effect',
     'prettier',
   ],
   rules: {
@@ -238,6 +239,15 @@ module.exports = {
     // Lodash
     'lodash/import-scope': [2, 'member'],
 
+    // React effect best practices
+    'react-you-might-not-need-an-effect/no-reset-all-state-on-prop-change': 
'error',
+    'react-you-might-not-need-an-effect/no-chain-state-updates': 'error',
+    'react-you-might-not-need-an-effect/no-event-handler': 'error',
+    'react-you-might-not-need-an-effect/no-derived-state': 'error',
+
+    // Storybook
+    'storybook/prefer-pascal-case': 'error',
+
     // File progress
     'file-progress/activate': 1,
 
diff --git a/superset-frontend/oxlint.json b/superset-frontend/oxlint.json
index 81b7523dce5..980587090e4 100644
--- a/superset-frontend/oxlint.json
+++ b/superset-frontend/oxlint.json
@@ -33,7 +33,8 @@
     "no-unused-vars": "off",
     "no-undef": "error",
     "no-prototype-builtins": "off",
-    "no-unsafe-optional-chaining": "off",
+    "no-unsafe-optional-chaining": "error",
+    "no-constant-binary-expression": "error",
     "no-import-assign": "off",
     "no-promise-executor-return": "off",
 
@@ -250,6 +251,7 @@
     ],
 
     // === Unicorn rules (bonus coverage) ===
+    "unicorn/no-useless-length-check": "error",
     "unicorn/filename-case": "off",
     "unicorn/prevent-abbreviations": "off",
     "unicorn/no-null": "off",
diff --git 
a/superset-frontend/packages/superset-ui-core/src/components/Layout/Layout.test.tsx
 
b/superset-frontend/packages/superset-ui-core/src/components/Layout/Layout.test.tsx
index 88a70ff9e6e..12784689afa 100644
--- 
a/superset-frontend/packages/superset-ui-core/src/components/Layout/Layout.test.tsx
+++ 
b/superset-frontend/packages/superset-ui-core/src/components/Layout/Layout.test.tsx
@@ -47,9 +47,10 @@ describe('Layout Component', () => {
   });
 
   test('hides Header when headerVisible is false', () => {
+    const headerVisible = false;
     render(
       <Layout>
-        {false && <Layout.Header>Header</Layout.Header>}
+        {headerVisible && <Layout.Header>Header</Layout.Header>}
         <Layout.Content>Content Area</Layout.Content>
         <Layout.Footer>Ant Design Layout Footer</Layout.Footer>
       </Layout>,
@@ -59,11 +60,12 @@ describe('Layout Component', () => {
   });
 
   test('hides Footer when footerVisible is false', () => {
+    const footerVisible = false;
     render(
       <Layout>
         <Layout.Header>Header</Layout.Header>
         <Layout.Content>Content Area</Layout.Content>
-        {false && <Layout.Footer>Ant Design Layout Footer</Layout.Footer>}
+        {footerVisible && <Layout.Footer>Ant Design Layout 
Footer</Layout.Footer>}
       </Layout>,
     );
 
diff --git 
a/superset-frontend/packages/superset-ui-core/src/components/Modal/Modal.tsx 
b/superset-frontend/packages/superset-ui-core/src/components/Modal/Modal.tsx
index 021341808e0..173ab6f83a2 100644
--- a/superset-frontend/packages/superset-ui-core/src/components/Modal/Modal.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/components/Modal/Modal.tsx
@@ -280,7 +280,7 @@ const CustomModal = ({
   const shouldShowMask = !(resizable || draggable);
 
   const onDragStart = (_: DraggableEvent, uiData: DraggableData) => {
-    const { clientWidth, clientHeight } = window?.document?.documentElement;
+    const { clientWidth, clientHeight } = document.documentElement;
     const targetRect = draggableRef?.current?.getBoundingClientRect();
 
     if (targetRect) {
diff --git 
a/superset-frontend/plugins/plugin-chart-ag-grid-table/src/buildQuery.ts 
b/superset-frontend/plugins/plugin-chart-ag-grid-table/src/buildQuery.ts
index 9212936686b..8122c86f825 100644
--- a/superset-frontend/plugins/plugin-chart-ag-grid-table/src/buildQuery.ts
+++ b/superset-frontend/plugins/plugin-chart-ag-grid-table/src/buildQuery.ts
@@ -606,7 +606,7 @@ const buildQuery: BuildQuery<TableChartFormData> = (
         {
           ...queryObject,
           time_offsets: [],
-          row_limit: Number(formData?.row_limit) ?? 0,
+          row_limit: Number(formData?.row_limit ?? 0),
           row_offset: 0,
           post_processing: [],
           is_rowcount: true,
diff --git 
a/superset-frontend/plugins/plugin-chart-echarts/test/BigNumber/transformProps.test.ts
 
b/superset-frontend/plugins/plugin-chart-echarts/test/BigNumber/transformProps.test.ts
index 3c681f281b0..66933c4f813 100644
--- 
a/superset-frontend/plugins/plugin-chart-echarts/test/BigNumber/transformProps.test.ts
+++ 
b/superset-frontend/plugins/plugin-chart-echarts/test/BigNumber/transformProps.test.ts
@@ -196,7 +196,9 @@ describe('BigNumberWithTrendline', () => {
           showXAxis: true,
         },
       });
-      expect((transformed.echartOptions?.xAxis as any).show).toBe(true);
+      expect((transformed.echartOptions!.xAxis as { show: boolean 
}).show).toBe(
+        true,
+      );
     });
 
     test('should not show X axis when showXAxis is false', () => {
@@ -207,7 +209,9 @@ describe('BigNumberWithTrendline', () => {
           showXAxis: false,
         },
       });
-      expect((transformed.echartOptions?.xAxis as any).show).toBe(false);
+      expect((transformed.echartOptions!.xAxis as { show: boolean 
}).show).toBe(
+        false,
+      );
     });
 
     test('should show Y axis when showYAxis is true', () => {
@@ -218,7 +222,9 @@ describe('BigNumberWithTrendline', () => {
           showYAxis: true,
         },
       });
-      expect((transformed.echartOptions?.yAxis as any).show).toBe(true);
+      expect((transformed.echartOptions!.yAxis as { show: boolean 
}).show).toBe(
+        true,
+      );
     });
 
     test('should not show Y axis when showYAxis is false', () => {
@@ -229,7 +235,9 @@ describe('BigNumberWithTrendline', () => {
           showYAxis: false,
         },
       });
-      expect((transformed.echartOptions?.yAxis as any).show).toBe(false);
+      expect((transformed.echartOptions!.yAxis as { show: boolean 
}).show).toBe(
+        false,
+      );
     });
   });
 
diff --git a/superset-frontend/plugins/plugin-chart-table/src/buildQuery.ts 
b/superset-frontend/plugins/plugin-chart-table/src/buildQuery.ts
index 7b3a952d891..c23a8a49c56 100644
--- a/superset-frontend/plugins/plugin-chart-table/src/buildQuery.ts
+++ b/superset-frontend/plugins/plugin-chart-table/src/buildQuery.ts
@@ -347,7 +347,7 @@ const buildQuery: BuildQuery<TableChartFormData> = (
         {
           ...queryObject,
           time_offsets: [],
-          row_limit: Number(formData?.row_limit) ?? 0,
+          row_limit: Number(formData?.row_limit ?? 0),
           row_offset: 0,
           post_processing: [],
           is_rowcount: true,
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/state.ts 
b/superset-frontend/src/dashboard/components/nativeFilters/state.ts
index f95c1bae4ef..ecbb56e47a0 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/state.ts
+++ b/superset-frontend/src/dashboard/components/nativeFilters/state.ts
@@ -210,10 +210,9 @@ export function useIsFilterInScope() {
       if (hasChartsInScope) {
         isChartInScope = filter.chartsInScope!.some((chartId: number) => {
           const tabParents = selectChartTabParents(chartId);
+          // Note: every() returns true for empty arrays, so length check is 
unnecessary
           return (
-            !tabParents ||
-            tabParents.length === 0 ||
-            tabParents.every(tab => activeTabs.includes(tab))
+            !tabParents || tabParents.every(tab => activeTabs.includes(tab))
           );
         });
       }
@@ -276,10 +275,9 @@ export function useIsCustomizationInScope() {
         customization.chartsInScope.length > 0 &&
         customization.chartsInScope.some((chartId: number) => {
           const tabParents = selectChartTabParents(chartId);
+          // Note: every() returns true for empty arrays, so length check is 
unnecessary
           return (
-            !tabParents ||
-            tabParents.length === 0 ||
-            tabParents.every(tab => activeTabs.includes(tab))
+            !tabParents || tabParents.every(tab => activeTabs.includes(tab))
           );
         });
 

Reply via email to