Compare commits

...

15 Commits

Author SHA1 Message Date
Enzo Martellucci
dbe994d337 Merge branch 'master' into enxdev/fix/custom-tabs 2026-07-23 06:19:51 +02:00
Enzo Martellucci
00e4f90cac fix(dashboard): scope tab drag cursor and test the rule it emits
The cursor rule matched .editable-title anywhere under the tabs
container, so dashboard headers inside the active tab picked up the move
cursor while a tab was dragging. Scope it to .dragdroppable-tab, which
only wraps tab titles.

The previous test called @dnd-kit's own activator directly and passed
with the cursor rule removed, guarding nothing. Drive a real drag from
the tab title instead and assert the rule is emitted only once dragging
starts. jsdom implements no PointerEvent, so the sensor needs one to
activate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 06:17:51 +02:00
Enzo Martellucci
a9f3f9e0aa fix(dashboard): show tab drag cursor only while dragging 2026-07-22 23:44:11 +02:00
Enzo Martellucci
d9f42088ea Merge branch 'master' into enxdev/fix/custom-tabs 2026-07-22 22:54:01 +02:00
Enzo Martellucci
ebc3b3efa7 Merge branch 'master' into enxdev/fix/custom-tabs 2026-07-13 00:04:49 +02:00
Enzo Martellucci
76f33221ac Merge branch 'master' into enxdev/fix/custom-tabs 2026-05-18 09:35:59 +02:00
Enzo Martellucci
a3057488cf Merge branch 'master' into enxdev/fix/custom-tabs 2026-05-05 23:44:47 +02:00
Enzo Martellucci
a3c5d0356d Merge branch 'master' into enxdev/fix/custom-tabs 2026-04-27 10:32:48 +02:00
Enzo Martellucci
60ef1ed246 Merge branch 'master' into enxdev/fix/custom-tabs 2026-02-10 16:03:44 +01:00
Enzo Martellucci
624b5ec260 Merge branch 'master' into enxdev/fix/custom-tabs 2026-02-04 09:41:39 +01:00
Enzo Martellucci
c288f3ffd6 Merge branch 'master' into enxdev/fix/custom-tabs 2026-01-28 11:28:36 +01:00
Enzo Martellucci
e21fb5659a Merge branch 'master' into enxdev/fix/custom-tabs 2026-01-20 09:43:43 +01:00
Enzo Martellucci
f80c46005e Merge branch 'master' into enxdev/fix/custom-tabs 2026-01-13 16:29:02 +01:00
Enzo Martellucci
b5e76ccf35 lint 2026-01-02 13:00:18 +01:00
Enzo Martellucci
ad53a4c744 fix(dashboard): disable drag on input fields during tab reorder
Prevents drag activation when clicking on input/textarea elements,
allowing users to edit tab titles without accidentally triggering
drag-and-drop reordering
2026-01-02 12:02:41 +01:00
2 changed files with 72 additions and 1 deletions

View File

@@ -48,10 +48,50 @@ const mockProps: TabsRendererProps = {
tabBarPaddingLeft: 16,
};
// Mirrors the tab label markup of Tab.tsx: the title lives in a
// .dragdroppable-tab container and renders as a textarea via EditableTitle
const draggableTabProps: TabsRendererProps = {
...mockProps,
editMode: true,
tabItems: [
{
...mockTabItems[0],
label: (
<div className="dragdroppable-tab">
<span className="editable-title">
<textarea defaultValue="Tab 1" />
</span>
</div>
),
},
mockTabItems[1],
],
};
// jsdom implements no PointerEvent, so @dnd-kit's PointerSensor never activates
class MockPointerEvent extends MouseEvent {
isPrimary: boolean;
pointerId: number;
constructor(type: string, init: PointerEventInit = {}) {
super(type, init);
this.isPrimary = init.isPrimary ?? true;
this.pointerId = init.pointerId ?? 1;
}
}
// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks
describe('TabsRenderer', () => {
const { PointerEvent: OriginalPointerEvent } = globalThis;
beforeEach(() => {
jest.clearAllMocks();
globalThis.PointerEvent = MockPointerEvent as typeof PointerEvent;
});
afterEach(() => {
globalThis.PointerEvent = OriginalPointerEvent;
});
test('renders tabs container with correct test attributes', () => {
@@ -199,4 +239,29 @@ describe('TabsRenderer', () => {
expect(screen.getByText('Tab 1 Content')).toBeInTheDocument();
expect(screen.queryByText('Tab 2 Content')).not.toBeInTheDocument(); // Not active
});
test('drags from the tab title and shows the drag indicator only then', () => {
render(<TabsRenderer {...draggableTabProps} />);
const container = screen.getByTestId('dashboard-component-tabs');
const title = container.querySelector('textarea') as HTMLTextAreaElement;
// At rest the title keeps the text cursor it sets on itself
expect(container).not.toHaveStyleRule('cursor', 'move', {
target: '.dragdroppable-tab *',
});
// Pressing on the title and moving past the sensor's distance constraint
// has to start a drag: the title covers most of the tab, so a tab that
// cannot be dragged from there cannot really be dragged at all
fireEvent.pointerDown(title, { button: 0, isPrimary: true, clientX: 0 });
fireEvent.pointerMove(document, {
button: 0,
isPrimary: true,
clientX: 50,
});
expect(container).toHaveStyleRule('cursor', 'move', {
target: '.dragdroppable-tab *',
});
});
});

View File

@@ -72,10 +72,16 @@ const StyledTabsContainer = styled.div<{ isDragging?: boolean }>`
}
}
/* Hide ink-bar during drag */
${({ isDragging }) =>
isDragging &&
`
/* Show the drag indicator during drag, over the tab title textarea too.
The doubled parent outranks the title's own cursor; a single & loses. */
&& .dragdroppable-tab * {
cursor: move;
}
/* Hide ink-bar during drag */
.ant-tabs-card > .ant-tabs-nav .ant-tabs-ink-bar,
.ant-tabs > .ant-tabs-nav .ant-tabs-ink-bar {
display: none !important;