codeant-ai-for-open-source[bot] commented on code in PR #37167:
URL: https://github.com/apache/superset/pull/37167#discussion_r2694632438


##########
superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx:
##########
@@ -106,12 +117,22 @@ const DashboardWrapper: React.FC<Props> = ({ children }) 
=> {
 
   useEffect(() => {
     const monitor = dragDropManager.getMonitor();
+    const debouncedSetIsDragged = debounce(setIsDragged, FAST_DEBOUNCE);

Review Comment:
   **Suggestion:** Using `debounce(setIsDragged, FAST_DEBOUNCE)` without the 
`leading` option delays setting the drag state until events stop (debounce 
fires after the last call), which likely makes `isDragged` become true only 
after dragging stops; use debounce with `{ leading: true, trailing: false }` or 
use `throttle` so the state is applied immediately when dragging starts. [logic 
error]
   
   <details>
   <summary><b>Severity Level:</b> Critical 🚨</summary>
   
   ```mdx
   - ❌ Drag start feedback delayed in dashboard editor.
   - ⚠️ Drop-zone highlighting behaves incorrectly.
   ```
   </details>
   
   ```suggestion
       const debouncedSetIsDragged = debounce(
         setIsDragged,
         FAST_DEBOUNCE,
         { leading: true, trailing: false },
       );
   ```
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. Open a dashboard in edit mode so DashboardWrapper mounts; the relevant 
logic is in
   
superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx
   (useEffect subscribeToStateChange block around lines 117-131).
   
   2. Begin a drag operation on a dashboard component. The drag monitor's
   subscribeToStateChange callback (monitor.isDragging() check at ~lines 
121-129) calls
   `debouncedSetIsDragged(true)` when dragging starts.
   
   3. Because debounce was created without the `leading` option 
(`debounce(setIsDragged,
   FAST_DEBOUNCE)` at line 120), the call to setIsDragged(true) is scheduled 
after
   FAST_DEBOUNCE elapses (i.e., after events stop), so isDragged becomes true 
only after
   dragging stops or after the debounce delay.
   
   4. Observe that the UI feedback that depends on isDragged (class 
'dragdroppable--dragging'
   applied in the JSX return of DashboardWrapper) is not applied immediately at 
drag start —
   drop-zone highlighting is delayed or inverted. Using `{ leading: true, 
trailing: false }`
   or throttle makes the state flip immediately on drag start, reproducing the 
intended
   immediate highlighting behavior.
   ```
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx
   **Line:** 120:120
   **Comment:**
        *Logic Error: Using `debounce(setIsDragged, FAST_DEBOUNCE)` without the 
`leading` option delays setting the drag state until events stop (debounce 
fires after the last call), which likely makes `isDragged` become true only 
after dragging stops; use debounce with `{ leading: true, trailing: false }` or 
use `throttle` so the state is applied immediately when dragging starts.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/components/Accessibility/SkipLink.tsx:
##########
@@ -0,0 +1,68 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React from 'react';
+import { styled } from '@superset-ui/core';
+
+/**
+ * SkipLink - WCAG 2.4.1 Bypass Blocks
+ * Allows keyboard users to skip navigation and jump directly to main content.
+ * The link is visually hidden but becomes visible when focused.
+ */
+
+const StyledSkipLink = styled.a`
+  position: absolute;
+  top: -100px;
+  left: 0;
+  background: ${({ theme }) => theme.colors.primary.dark1};
+  color: ${({ theme }) => theme.colors.grayscale.light5};
+  padding: ${({ theme }) => theme.gridUnit * 3}px ${({ theme }) => 
theme.gridUnit * 4}px;
+  z-index: 10000;
+  text-decoration: none;
+  font-weight: ${({ theme }) => theme.typography.weights.bold};
+  font-size: ${({ theme }) => theme.typography.sizes.m}px;
+  border-radius: 0 0 ${({ theme }) => theme.borderRadius}px ${({ theme }) => 
theme.borderRadius}px;
+  transition: top 0.2s ease-in-out;
+
+  &:focus,
+  &:focus-visible {
+    top: 0 !important;
+    outline: 3px solid ${({ theme }) => theme.colors.primary.light1};
+    outline-offset: 2px;
+  }
+
+  &:hover {
+    background: ${({ theme }) => theme.colors.primary.base};
+  }
+`;
+
+interface SkipLinkProps {
+  targetId?: string;
+  children?: React.ReactNode;
+}
+
+const SkipLink: React.FC<SkipLinkProps> = ({
+  targetId = 'main-content',
+  children = 'Skip to main content',
+}) => (
+  <StyledSkipLink href={`#${targetId}`} className="a11y-skip-link">
+    {children}
+  </StyledSkipLink>
+);

Review Comment:
   **Suggestion:** Fragment navigation alone does not reliably move keyboard 
focus to the target element for assistive technologies; clicking the link 
should programmatically set focus to the target element (and temporarily make 
it focusable if needed) to ensure screen reader and keyboard users are moved to 
the main content. [possible bug]
   
   <details>
   <summary><b>Severity Level:</b> Critical 🚨</summary>
   
   ```mdx
   - ❌ Screen reader users not moved to main content
   - ❌ Keyboard users lose context after activating skip link
   - ⚠️ Affects dashboard navigation flow for assistive tech
   ```
   </details>
   
   ```suggestion
   }) => {
     const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
       e.preventDefault();
       const el = document.getElementById(targetId);
       if (el && el instanceof HTMLElement) {
         // Make sure the element is focusable, focus it, then clean up any 
temporary tabindex
         const hadTabIndex = el.hasAttribute('tabindex');
         if (!hadTabIndex) {
           el.setAttribute('tabindex', '-1');
         }
         el.focus();
         if (!hadTabIndex) {
           el.removeAttribute('tabindex');
         }
       } else {
         // Fallback to fragment navigation if target not present
         window.location.hash = `#${targetId}`;
       }
     };
   
     return (
       <StyledSkipLink href={`#${targetId}`} className="a11y-skip-link" 
onClick={handleClick}>
         {children}
       </StyledSkipLink>
     );
   };
   ```
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. Open any dashboard (PR testing instructions: "Open any dashboard"). The 
SkipLink
   component is declared at
   `superset-frontend/src/components/Accessibility/SkipLink.tsx:59-66`.
   
   2. Press Tab to reveal and focus the "Skip to main content" link (the link 
is generated at
   `SkipLink.tsx:63` with an href fragment `#main-content`).
   
   3. Activate the link with Enter or Space. Current code relies solely on 
fragment
   navigation (`href="#main-content"`) and does not programmatically set focus 
on the target
   element.
   
   4. Observe with a screen reader or accessibility inspector: fragment 
navigation changes
   the URL fragment but does not reliably move keyboard focus to the main 
content element.
   Because `SkipLink` lacks a click handler to call `element.focus()`, users of 
assistive
   tech may not be placed into the main content even though the page scrolled 
there.
   
   5. The improved code (adds onClick that finds 
`document.getElementById(targetId)` at
   runtime and focuses it) reproduces the desired fix and ensures the main 
content receives
   keyboard focus after activation.
   ```
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** superset-frontend/src/components/Accessibility/SkipLink.tsx
   **Line:** 62:66
   **Comment:**
        *Possible Bug: Fragment navigation alone does not reliably move 
keyboard focus to the target element for assistive technologies; clicking the 
link should programmatically set focus to the target element (and temporarily 
make it focusable if needed) to ensure screen reader and keyboard users are 
moved to the main content.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx:
##########
@@ -25,7 +25,7 @@ import { LayoutItem, RootState } from 'src/dashboard/types';
 import AnchorLink from 'src/dashboard/components/AnchorLink';
 import Chart from 'src/dashboard/containers/Chart';
 import DeleteComponentButton from 
'src/dashboard/components/DeleteComponentButton';
-import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
+import { Draggable } from 'src/dashboard/components/dnd/DragDroppable';

Review Comment:
   **Suggestion:** If `DragDroppable` exports a default `Draggable` component 
(common pattern), importing it as a named export (`{ Draggable }`) will make 
`Draggable` undefined at runtime causing React to throw "Element type is 
invalid" or "TypeError: Draggable is not a function" when rendering; switch to 
a default import to match the module's export shape or verify the module's 
named export. [type error]
   
   <details>
   <summary><b>Severity Level:</b> Critical 🚨</summary>
   
   ```mdx
   - ❌ Dashboard charts fail to render when ChartHolder mounts.
   - ⚠️ Console shows React element type invalid error.
   ```
   </details>
   
   ```suggestion
   import Draggable from 'src/dashboard/components/dnd/DragDroppable';
   ```
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. Ensure the frontend is running (start the dev server or build the 
frontend).
   
   2. Open any dashboard page so React mounts ChartHolder (component defined at
   superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx, 
full file
   65..333).
   
   3. ChartHolder imports Draggable from DragDroppable (import statement at
   ChartHolder.tsx:28).
   
   4. During render ChartHolder instantiates <Draggable ...> (render begins at
   ChartHolder.tsx:246..256). If DragDroppable actually uses a default export 
rather than a
   named export, the named import on line 28 will be undefined and React will 
throw at render
   time (console shows "Element type is invalid" or "TypeError: Draggable is 
not a
   function"), preventing charts from rendering.
   ```
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx
   **Line:** 28:28
   **Comment:**
        *Type Error: If `DragDroppable` exports a default `Draggable` component 
(common pattern), importing it as a named export (`{ Draggable }`) will make 
`Draggable` undefined at runtime causing React to throw "Element type is 
invalid" or "TypeError: Draggable is not a function" when rendering; switch to 
a default import to match the module's export shape or verify the module's 
named export.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/packages/superset-ui-core/src/query/api/v1/makeApi.ts:
##########
@@ -115,11 +115,11 @@ export default function makeApi<
         jsonPayload: undefined as JsonObject | undefined,
       };
       if (requestType === 'search') {
-        requestConfig.searchParams = payload as URLSearchParams;
+        requestConfig.searchParams = payload as unknown as URLSearchParams;

Review Comment:
   **Suggestion:** Unsafe cast: `payload as unknown as URLSearchParams` only 
changes TypeScript typing and does not convert the runtime value. If `payload` 
is a plain object, array, or primitive, this will set 
`requestConfig.searchParams` to a non-URLSearchParams value (or produce 
incorrect query encoding), causing runtime errors or incorrect requests. 
Convert or construct a proper `URLSearchParams` at runtime and guard for 
null/undefined. [type error]
   
   <details>
   <summary><b>Severity Level:</b> Critical 🚨</summary>
   
   ```mdx
   - ❌ GET APIs using makeApi may send invalid querystrings.
   - ❌ SupersetClient.request() may throw serializer errors.
   - ⚠️ Dashboard list and search endpoints affected.
   ```
   </details>
   
   ```suggestion
           if (payload == null) {
             requestConfig.searchParams = undefined;
           } else if (payload instanceof URLSearchParams) {
             requestConfig.searchParams = payload;
           } else if (typeof payload === 'string') {
             requestConfig.searchParams = new URLSearchParams(payload);
           } else if (typeof payload === 'object') {
             requestConfig.searchParams = new URLSearchParams(
               Object.entries(payload as Record<string, any>).flatMap(([k, v]) 
=>
                 Array.isArray(v) ? v.map(item => [k, String(item)]) : [[k, 
String(v)]],
               ),
             );
           } else {
             requestConfig.searchParams = new URLSearchParams(String(payload));
           }
   ```
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. Open the file 
`superset-frontend/packages/superset-ui-core/src/query/api/v1/makeApi.ts`
   and locate the `request` function around the request-type branching (see 
line 117: `if
   (requestType === 'search') {` and line 118 where `searchParams` is assigned).
   
   2. Create a small test harness inside the repository that imports the 
factory: e.g. in a
   new file `packages/superset-ui-core/src/query/api/v1/testMakeApi.ts` call:
   
      - import makeApi from '.../makeApi';
   
      - const getApi = makeApi({ endpoint: '/api/v1/test', method: 'GET', 
requestType:
      'search' });
   
      - await getApi({ foo: 'bar' } as any);
   
      This will exercise `request()` and hit the branch starting at 
`makeApi.ts:117`.
   
   3. Run the test harness (ts-node or via the app dev server) so `request()` 
calls
   `SupersetClient.request()` with the `requestConfig` produced at 
`makeApi.ts:116-125`.
   Observe the `searchParams` value passed to `SupersetClient.request()` is the 
original
   plain object (casted via `as unknown as URLSearchParams`) rather than a real
   `URLSearchParams` instance.
   
   4. Observe runtime failure modes: either incorrect querystring serialization 
or errors in
   `SupersetClient.request()` (when it expects URLSearchParams). This 
reproduces the issue
   concretely by exercising the GET/search branch in `makeApi.ts:117-118`.
   ```
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** 
superset-frontend/packages/superset-ui-core/src/query/api/v1/makeApi.ts
   **Line:** 118:118
   **Comment:**
        *Type Error: Unsafe cast: `payload as unknown as URLSearchParams` only 
changes TypeScript typing and does not convert the runtime value. If `payload` 
is a plain object, array, or primitive, this will set 
`requestConfig.searchParams` to a non-URLSearchParams value (or produce 
incorrect query encoding), causing runtime errors or incorrect requests. 
Convert or construct a proper `URLSearchParams` at runtime and guard for 
null/undefined.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



##########
superset-frontend/src/components/ListView/ListView.test.tsx:
##########
@@ -0,0 +1,74 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React from 'react';
+import { render, waitFor } from 'spec/helpers/testing-library';
+import ListView from './ListView';
+
+const mockedProps = {
+  title: 'Data Table',
+  columns: [
+    {
+      accessor: 'id',
+      Header: 'ID',
+      sortable: true,
+    },
+    {
+      accessor: 'age',
+      Header: 'Age',
+    },
+    {
+      accessor: 'name',
+      Header: 'Name',
+    },
+    {
+      accessor: 'time',
+      Header: 'Time',
+    },
+  ],
+  data: [
+    { id: 1, name: 'data 1', age: 10, time: '2020-11-18T07:53:45.354Z' },
+    { id: 2, name: 'data 2', age: 1, time: '2020-11-18T07:53:45.354Z' },
+  ],
+  count: 2,
+  pageSize: 1,
+  loading: false,
+  refreshData: jest.fn(),
+  addSuccessToast: jest.fn(),
+  addDangerToast: jest.fn(),
+};
+
+test('redirects to first page when page index is invalid', async () => {
+  const fetchData = jest.fn();
+  window.history.pushState({}, '', '/?pageIndex=9');
+  render(<ListView {...mockedProps} fetchData={fetchData} />, {
+    useRouter: true,
+    useQueryParams: true,
+  });
+  await waitFor(() => {
+    expect(window.location.search).toEqual('?pageIndex=0');
+    expect(fetchData).toBeCalledTimes(2);

Review Comment:
   **Suggestion:** Wrong Jest matcher used: `toBeCalledTimes` is not a valid 
Jest matcher and will throw at runtime; replace it with 
`toHaveBeenCalledTimes`. [type error]
   
   <details>
   <summary><b>Severity Level:</b> Critical 🚨</summary>
   
   ```mdx
   - ❌ Unit test file fails, blocking CI runs.
   - ⚠️ Test suite aborts before validating behavior.
   - ⚠️ Developers get noisy CI errors for ListView tests.
   ```
   </details>
   
   ```suggestion
       expect(fetchData).toHaveBeenCalledTimes(2);
   ```
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. Run the test suite that includes this file:
   superset-frontend/src/components/ListView/ListView.test.tsx. The test named 
"redirects to
   first page when page index is invalid" starts at the test declaration on 
line 56 in the PR
   hunk.
   
   2. The test sets up fetchData as a jest mock at line 57 (const fetchData = 
jest.fn();) and
   renders the ListView component (render(...) call on line 59). Jest executes 
the test
   function body.
   
   3. When execution reaches the assertion at line 65 
(expect(fetchData).toBeCalledTimes(2);)
   Jest attempts to call the non-existent matcher toBeCalledTimes and throws a 
runtime error
   (TypeError: expect(...).toBeCalledTimes is not a function), causing the test 
to fail
   immediately.
   
   4. Observed outcome: test run fails with a matcher error before verifying 
call counts or
   subsequent assertions on lines 66-71. Fixing to toHaveBeenCalledTimes 
resolves the runtime
   failure and allows the rest of the assertions to run.
   ```
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** superset-frontend/src/components/ListView/ListView.test.tsx
   **Line:** 65:65
   **Comment:**
        *Type Error: Wrong Jest matcher used: `toBeCalledTimes` is not a valid 
Jest matcher and will throw at runtime; replace it with `toHaveBeenCalledTimes`.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to