import React from 'react';
import type { InteractionTaskArgs, PublicInteractionTask } from 'storybook-addon-performance';
import invariant from 'tiny-invariant';
import { DragDropContext, Draggable, Droppable } from '../src';
import Board from './pieces/board';
import { type RbdApi } from './pieces/types';
import { getColumn, getColumnItems, getColumnOrder, getItem } from './utils/board-utils';
import { userEvent, waitForAnimationFrame } from './utils/user-event';
const rbdApi: RbdApi = {
// @ts-expect-error
DragDropContext,
// @ts-expect-error
Draggable,
// @ts-expect-error
Droppable,
};
const boardMigration: {
(): React.JSX.Element;
story: {
name: string;
parameters: {
performance: {
interactions: PublicInteractionTask[];
};
};
};
} = (): React.JSX.Element => ;
async function waitUntilElementIsDraggable(element: HTMLElement): Promise {
return new Promise((resolve) => {
/**
* pdnd is not set up synchronously.
*
* We can tell when it is ready when an element becomes draggable.
*/
const observer = new MutationObserver(() => {
if (element.draggable) {
observer.disconnect();
resolve();
}
});
/**
* Only changes to the draggable attribute will trigger the above observer.
*/
observer.observe(element, { attributeFilter: ['draggable'] });
});
}
async function waitUntilDropTargetReady(element: HTMLElement): Promise {
return new Promise((resolve) => {
/**
* pdnd is not set up synchronously.
*
* We can tell when it is ready when an element becomes draggable.
*/
const observer = new MutationObserver(() => {
if (element.getAttribute('data-drop-target-for-element') === 'true') {
observer.disconnect();
resolve();
}
});
/**
* Only changes to the draggable attribute will trigger the above observer.
*/
observer.observe(element, {
attributeFilter: ['data-drop-target-for-element'],
});
});
}
const interactions: PublicInteractionTask[] = [
{
name: 'Pick up a card (mouse)',
description: 'Recording how long it takes to pick up a card',
run: async ({ container, controls }: InteractionTaskArgs): Promise => {
const itemA0 = getItem(container, 'A0');
await waitUntilElementIsDraggable(itemA0);
controls.time(async () => {
userEvent.nativePointer.lift({ element: itemA0, dragHandle: itemA0 });
});
await waitForAnimationFrame();
invariant(itemA0.getAttribute('data-is-dragging') === 'true', 'isDragging');
userEvent.nativePointer.cancel({ dragHandle: itemA0 });
},
},
{
name: 'Reorder a card (mouse)',
description: 'Recording how long it takes to reorder a card in the same list',
run: async ({ container, controls }: InteractionTaskArgs): Promise => {
const itemA0 = getItem(container, 'A0');
const itemA2 = getItem(container, 'A2');
await waitUntilElementIsDraggable(itemA0);
await controls.time(async () => {
await userEvent.nativePointer.dragAndDrop({
element: itemA0,
dragHandle: itemA0,
target: itemA2,
});
});
const columnItems = getColumnItems(container, 'A');
invariant(columnItems[0].getAttribute('data-testid') === 'item-A1');
invariant(columnItems[1].getAttribute('data-testid') === 'item-A0');
},
},
{
name: 'Move a card (mouse)',
description: 'Recording how long it takes to move a card to another list',
run: async ({ container, controls }: InteractionTaskArgs): Promise => {
const itemA0 = getItem(container, 'A0');
const itemB0 = getItem(container, 'B0');
await waitUntilElementIsDraggable(itemA0);
await controls.time(async () => {
await userEvent.nativePointer.dragAndDrop({
element: itemA0,
dragHandle: itemA0,
target: itemB0,
});
});
invariant(getColumnItems(container, 'A')[0].getAttribute('data-testid') === 'item-A1');
invariant(getColumnItems(container, 'B')[0].getAttribute('data-testid') === 'item-A0');
},
},
{
name: 'Reorder a column (mouse)',
description: 'Recording how long it takes to reorder a column',
run: async ({ container, controls }: InteractionTaskArgs): Promise => {
const columnA = getColumn(container, 'A');
const columnC = getColumn(container, 'C');
await Promise.all([
waitUntilElementIsDraggable(columnA.element),
waitUntilDropTargetReady(columnC.element),
]);
await controls.time(async () => {
await userEvent.nativePointer.dragAndDrop({
element: columnA.element,
dragHandle: columnA.dragHandle,
target: columnC.element,
});
});
const columnOrder = getColumnOrder(container);
invariant(columnOrder[0] === 'column-B');
},
},
];
boardMigration.story = {
name: 'Board (migration layer)',
parameters: {
performance: {
interactions,
},
},
};
export default boardMigration;