Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 29 additions & 25 deletions superset-frontend/src/components/ListView/ListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ import TableCollection from '@superset-ui/core/components/TableCollection';
import BulkTagModal from 'src/features/tags/BulkTagModal';
import {
Button,
Tooltip,
Checkbox,
Icons,
EmptyState,
Loading,
Pagination,
type EmptyStateProps,
} from '@superset-ui/core/components';
import { Pagination } from 'antd';
import CardCollection from './CardCollection';
import FilterControls from './Filters';
import { CardSortSelect } from './CardSortSelect';
Expand Down Expand Up @@ -201,31 +202,34 @@ const ViewModeToggle = ({
setMode: (mode: 'table' | 'card') => void;
}) => (
<ViewModeContainer>
<div
role="button"
tabIndex={0}
onClick={e => {
e.currentTarget.blur();
setMode('card');
}}
className={cx('toggle-button', { active: mode === 'card' })}
>
<Icons.AppstoreOutlined iconSize="xl" />
</div>
<div
role="button"
tabIndex={0}
onClick={e => {
e.currentTarget.blur();
setMode('table');
}}
className={cx('toggle-button', { active: mode === 'table' })}
>
<Icons.UnorderedListOutlined iconSize="xl" />
</div>
<Tooltip title={t('Grid view')}>
<div
role="button"
tabIndex={0}
onClick={e => {
e.currentTarget.blur();
setMode('card');
}}
className={cx('toggle-button', { active: mode === 'card' })}
>
<Icons.AppstoreOutlined iconSize="xl" />
</div>
</Tooltip>
<Tooltip title={t('List view')}>
<div
role="button"
tabIndex={0}
Comment on lines +209 to +221
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Toggle buttons do not expose their pressed state to assistive technologies: the divs use role="button" and visually indicate active state via className, but they lack an aria-pressed attribute. Add aria-pressed tied to the active condition so screen readers can expose the toggle state. [possible bug]

Severity Level: Major ⚠️
- ❌ Screen readers don't announce toggle state.
- ⚠️ Affects ListView view-mode toggles accessibility.
Suggested change
onClick={e => {
e.currentTarget.blur();
setMode('card');
}}
className={cx('toggle-button', { active: mode === 'card' })}
>
<Icons.AppstoreOutlined iconSize="xl" />
</div>
</Tooltip>
<Tooltip title={t('List view')}>
<div
role="button"
tabIndex={0}
aria-pressed={mode === 'card'}
onClick={e => {
e.currentTarget.blur();
setMode('card');
}}
className={cx('toggle-button', { active: mode === 'card' })}
>
<Icons.AppstoreOutlined iconSize="xl" />
</div>
</Tooltip>
<Tooltip title={t('List view')}>
<div
role="button"
tabIndex={0}
aria-pressed={mode === 'table'}
Steps of Reproduction ✅
1. Run the app and load a page that shows the ListView view-mode toggle (ListView
component).

2. Open an accessibility inspector or screen reader (e.g., VoiceOver/NVDA) and navigate to
the toggle.

3. Inspect the toggle element at ListView.tsx:205-230: it has role="button" and visual
active class,

   but no aria-pressed attribute is present to convey state to assistive tech.

4. Observe assistive tech does not announce the pressed/toggled state; adding aria-pressed

   (aria-pressed={mode === 'card' / mode === 'table'}) exposes state to screen readers.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/src/components/ListView/ListView.tsx
**Line:** 209:221
**Comment:**
	*Possible Bug: Toggle buttons do not expose their pressed state to assistive technologies: the divs use role="button" and visually indicate active state via className, but they lack an `aria-pressed` attribute. Add `aria-pressed` tied to the active condition so screen readers can expose the toggle state.

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.

onClick={e => {
e.currentTarget.blur();
setMode('table');
}}
className={cx('toggle-button', { active: mode === 'table' })}
>
<Icons.UnorderedListOutlined iconSize="xl" />
</div>
</Tooltip>
</ViewModeContainer>
);

export interface ListViewProps<T extends object = any> {
columns: any[];
data: T[];
Expand Down Expand Up @@ -463,7 +467,7 @@ export function ListView<T extends object = any>({
current={pageIndex + 1}
pageSize={pageSize}
total={count}
onChange={page => {
onChange={(page: number) => {
gotoPage(page - 1);
}}
size="default"
Expand Down
Loading