You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by en...@apache.org on 2026/07/10 09:24:33 UTC
(superset) branch master updated: fix(CollectionControl): a11y drag handle and vertical axis lock (#39863)
This is an automated email from the ASF dual-hosted git repository.
EnxDev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 1631d52b650 fix(CollectionControl): a11y drag handle and vertical axis lock (#39863)
1631d52b650 is described below
commit 1631d52b65045fb7898e6468a6fe40af92d3837b
Author: Mehmet Salih Yavuz <sa...@proton.me>
AuthorDate: Fri Jul 10 12:24:25 2026 +0300
fix(CollectionControl): a11y drag handle and vertical axis lock (#39863)
Co-authored-by: Claude Opus 4.7 (1M context) <no...@anthropic.com>
Co-authored-by: Evan Rusackas <ev...@preset.io>
Co-authored-by: Joe Li <jo...@preset.io>
---
superset-frontend/package-lock.json | 15 ++++++++++
superset-frontend/package.json | 1 +
.../CollectionControl/CollectionControl.test.tsx | 8 +++++
.../controls/CollectionControl/index.tsx | 35 +++++++++++++++++-----
4 files changed, 52 insertions(+), 7 deletions(-)
diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json
index 8ffcb178c7c..91dc079a63b 100644
--- a/superset-frontend/package-lock.json
+++ b/superset-frontend/package-lock.json
@@ -24,6 +24,7 @@
"@deck.gl/mesh-layers": "~9.2.5",
"@deck.gl/react": "~9.2.5",
"@dnd-kit/core": "^6.3.1",
+ "@dnd-kit/modifiers": "^9.0.0",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@emotion/cache": "^11.4.0",
@@ -3037,6 +3038,20 @@
"react-dom": ">=16.8.0"
}
},
+ "node_modules/@dnd-kit/modifiers": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@dnd-kit/modifiers/-/modifiers-9.0.0.tgz",
+ "integrity": "sha512-ybiLc66qRGuZoC20wdSSG6pDXFikui/dCNGthxv4Ndy8ylErY0N3KVxY2bgo7AWwIbxDmXDg3ylAFmnrjcbVvw==",
+ "license": "MIT",
+ "dependencies": {
+ "@dnd-kit/utilities": "^3.2.2",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@dnd-kit/core": "^6.3.0",
+ "react": ">=16.8.0"
+ }
+ },
"node_modules/@dnd-kit/sortable": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz",
diff --git a/superset-frontend/package.json b/superset-frontend/package.json
index b3bec530839..8167496c282 100644
--- a/superset-frontend/package.json
+++ b/superset-frontend/package.json
@@ -107,6 +107,7 @@
"@deck.gl/mesh-layers": "~9.2.5",
"@deck.gl/react": "~9.2.5",
"@dnd-kit/core": "^6.3.1",
+ "@dnd-kit/modifiers": "^9.0.0",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@emotion/cache": "^11.4.0",
diff --git a/superset-frontend/src/explore/components/controls/CollectionControl/CollectionControl.test.tsx b/superset-frontend/src/explore/components/controls/CollectionControl/CollectionControl.test.tsx
index 1bd518e9511..09cca7d7f83 100644
--- a/superset-frontend/src/explore/components/controls/CollectionControl/CollectionControl.test.tsx
+++ b/superset-frontend/src/explore/components/controls/CollectionControl/CollectionControl.test.tsx
@@ -134,6 +134,14 @@ test('Should have SortableDragger icon', async () => {
expect(await screen.findByLabelText('Drag to reorder')).toBeVisible();
});
+test('Drag activator exposes aria-label="Drag to reorder"', async () => {
+ const props = createProps();
+ render(<CollectionControl {...props} />);
+ const dragActivator = await screen.findByLabelText('Drag to reorder');
+ expect(dragActivator).toHaveAttribute('aria-label', 'Drag to reorder');
+ expect(dragActivator.tagName).toBe('BUTTON');
+});
+
test('Should call Control component', async () => {
const props = createProps();
render(<CollectionControl {...props} />);
diff --git a/superset-frontend/src/explore/components/controls/CollectionControl/index.tsx b/superset-frontend/src/explore/components/controls/CollectionControl/index.tsx
index 1a0393577ad..97ad6976d46 100644
--- a/superset-frontend/src/explore/components/controls/CollectionControl/index.tsx
+++ b/superset-frontend/src/explore/components/controls/CollectionControl/index.tsx
@@ -17,7 +17,7 @@
* under the License.
*/
import React, { useCallback, useMemo, useRef } from 'react';
-import { IconTooltip, List } from '@superset-ui/core/components';
+import { Button, IconTooltip, List } from '@superset-ui/core/components';
import { nanoid } from 'nanoid';
import { t } from '@apache-superset/core/translation';
import { useTheme, type SupersetTheme } from '@apache-superset/core/theme';
@@ -27,14 +27,17 @@ import {
useSensor,
useSensors,
PointerSensor,
+ KeyboardSensor,
type DragEndEvent,
} from '@dnd-kit/core';
import {
SortableContext,
verticalListSortingStrategy,
useSortable,
+ sortableKeyboardCoordinates,
arrayMove,
} from '@dnd-kit/sortable';
+import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
import { CSS } from '@dnd-kit/utilities';
import { Icons } from '@superset-ui/core/components/Icons';
import {
@@ -68,8 +71,7 @@ export interface CollectionControlProps {
function DragHandle() {
return (
<Icons.MenuOutlined
- role="img"
- aria-label={t('Drag to reorder')}
+ aria-hidden
className="text-primary"
style={{ cursor: 'ns-resize' }}
/>
@@ -93,8 +95,14 @@ function SortableItem({
onChangeItem,
onRemoveItem,
}: SortableItemProps) {
- const { attributes, listeners, setNodeRef, transform, transition } =
- useSortable({ id });
+ const {
+ attributes,
+ listeners,
+ setNodeRef,
+ setActivatorNodeRef,
+ transform,
+ transition,
+ } = useSortable({ id });
const style = {
transform: CSS.Transform.toString(transform),
transition: transition ?? undefined,
@@ -116,8 +124,17 @@ function SortableItem({
paddingInline: theme.sizeUnit * 6,
})}
>
- <span {...attributes} {...listeners}>
- <DragHandle />
+ <span ref={setActivatorNodeRef} css={{ display: 'inline-flex' }}>
+ <Button
+ type="text"
+ aria-label={t('Drag to reorder')}
+ icon={<DragHandle />}
+ css={{
+ cursor: 'ns-resize',
+ }}
+ {...attributes}
+ {...listeners}
+ />
</span>
<div
css={(theme: SupersetTheme) => ({
@@ -183,6 +200,9 @@ function CollectionControl({
useSensor(PointerSensor, {
activationConstraint: { distance: 5 },
}),
+ useSensor(KeyboardSensor, {
+ coordinateGetter: sortableKeyboardCoordinates,
+ }),
);
// Two items can collide when keyAccessor returns falsy and the index
@@ -284,6 +304,7 @@ function CollectionControl({
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
+ modifiers={[restrictToVerticalAxis]}
onDragEnd={handleDragEnd}
>
<SortableContext items={itemIds} strategy={verticalListSortingStrategy}>