@dxos/react-ui-editor 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/index.mjs +82 -70
- package/dist/lib/index.mjs.map +1 -1
- package/dist/types/src/components/EditorMenuProvider/menu.d.ts +2 -0
- package/dist/types/src/components/EditorMenuProvider/menu.d.ts.map +1 -1
- package/dist/types/src/components/EditorMenuProvider/popover.d.ts +7 -1
- package/dist/types/src/components/EditorMenuProvider/popover.d.ts.map +1 -1
- package/dist/types/src/components/EditorMenuProvider/useEditorMenu.d.ts +2 -2
- package/dist/types/src/components/EditorMenuProvider/useEditorMenu.d.ts.map +1 -1
- package/dist/types/src/components/EditorMenuProvider/useEditorMenu.test.d.ts +2 -0
- package/dist/types/src/components/EditorMenuProvider/useEditorMenu.test.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
- package/dist/types/src/stories/Widgets.stories.d.ts +5 -0
- package/dist/types/src/stories/Widgets.stories.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +33 -33
- package/src/components/EditorMenuProvider/EditorMenuProvider.tsx +1 -1
- package/src/components/EditorMenuProvider/menu.ts +2 -0
- package/src/components/EditorMenuProvider/popover.ts +12 -3
- package/src/components/EditorMenuProvider/useEditorMenu.test.tsx +125 -0
- package/src/components/EditorMenuProvider/useEditorMenu.ts +79 -56
- package/src/hooks/useTextEditor.test.tsx +45 -4
- package/src/hooks/useTextEditor.ts +37 -9
- package/src/stories/Comments.stories.tsx +25 -21
- package/src/stories/Widgets.stories.tsx +59 -0
|
@@ -194,27 +194,31 @@ const CommentsList = ({
|
|
|
194
194
|
onValueChange={handleSelect}
|
|
195
195
|
items={resolved.map(({ comment }) => ({ value: comment.id, label: comment.id }))}
|
|
196
196
|
>
|
|
197
|
-
<Listbox.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
<Listbox.
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
197
|
+
<Listbox.Viewport>
|
|
198
|
+
<Listbox.Content aria-label='Comments' classNames='p-1'>
|
|
199
|
+
{resolved.map(({ comment, range }) => (
|
|
200
|
+
<Listbox.Item key={comment.id} id={comment.id} classNames='flex items-center gap-2'>
|
|
201
|
+
<Listbox.ItemText classNames='grow'>
|
|
202
|
+
{(range && view?.state.doc.sliceString(range.from, range.to)) || comment.cursor || comment.id}
|
|
203
|
+
</Listbox.ItemText>
|
|
204
|
+
<Listbox.ItemDescription>
|
|
205
|
+
{range ? `${range.from}–${range.to}` : comment.cursor}
|
|
206
|
+
</Listbox.ItemDescription>
|
|
207
|
+
<Button.Root
|
|
208
|
+
variant='ghost'
|
|
209
|
+
iconOnly
|
|
210
|
+
icon='ph--x--regular'
|
|
211
|
+
label='Delete comment'
|
|
212
|
+
onClick={(event) => {
|
|
213
|
+
// Keep the row from selecting when the delete button is pressed.
|
|
214
|
+
event.stopPropagation();
|
|
215
|
+
handleDelete(comment.id);
|
|
216
|
+
}}
|
|
217
|
+
/>
|
|
218
|
+
</Listbox.Item>
|
|
219
|
+
))}
|
|
220
|
+
</Listbox.Content>
|
|
221
|
+
</Listbox.Viewport>
|
|
218
222
|
</Listbox.Root>
|
|
219
223
|
</div>
|
|
220
224
|
);
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
12
12
|
import React, { Fragment, type PropsWithChildren, useEffect, useMemo, useRef, useState } from 'react';
|
|
13
13
|
import { createPortal } from 'react-dom';
|
|
14
|
+
import { expect, userEvent, waitFor } from 'storybook/test';
|
|
14
15
|
|
|
16
|
+
import { sleep } from '@dxos/async';
|
|
15
17
|
import { random } from '@dxos/random';
|
|
16
18
|
import * as Card from '@dxos/react-ui/Card';
|
|
17
19
|
import * as Hooks from '@dxos/react-ui/Hooks';
|
|
@@ -80,6 +82,7 @@ const PreviewCard = () => {
|
|
|
80
82
|
}
|
|
81
83
|
return (
|
|
82
84
|
<Popover.Content
|
|
85
|
+
data-testid='preview-card'
|
|
83
86
|
classNames={[
|
|
84
87
|
'origin-(--transform-origin)',
|
|
85
88
|
'data-[state=open]:animate-popover-in',
|
|
@@ -358,6 +361,62 @@ export const Preview: Story = {
|
|
|
358
361
|
},
|
|
359
362
|
};
|
|
360
363
|
|
|
364
|
+
/** Longer than the anchor's close grace period, so a card that survives it was deliberately kept open. */
|
|
365
|
+
const PAST_CLOSE_DELAY = 600;
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Regression: the pointer travels from the chip onto its hover card (e.g. to reach the card's menu). The card
|
|
369
|
+
* stays open while the pointer is over it, closes once the pointer leaves both, and stays pinned once used.
|
|
370
|
+
*/
|
|
371
|
+
export const PreviewHoverTest: Story = {
|
|
372
|
+
args: { ...Preview.args },
|
|
373
|
+
play: async ({ canvasElement }) => {
|
|
374
|
+
const anchor = await waitFor(
|
|
375
|
+
() => {
|
|
376
|
+
const anchor = canvasElement.querySelector<HTMLElement>('dx-anchor');
|
|
377
|
+
if (!anchor) {
|
|
378
|
+
throw new Error('Anchor chip not rendered.');
|
|
379
|
+
}
|
|
380
|
+
return anchor;
|
|
381
|
+
},
|
|
382
|
+
{ timeout: 10_000 },
|
|
383
|
+
);
|
|
384
|
+
const outside = canvasElement.querySelector<HTMLElement>('pre');
|
|
385
|
+
if (!outside) {
|
|
386
|
+
throw new Error('Source pane not rendered.');
|
|
387
|
+
}
|
|
388
|
+
const card = () => document.querySelector<HTMLElement>('[data-testid="preview-card"]');
|
|
389
|
+
const hoverOpen = async () => {
|
|
390
|
+
await userEvent.hover(anchor);
|
|
391
|
+
await waitFor(() => expect(card()).not.toBeNull(), { timeout: 5_000 });
|
|
392
|
+
const current = card();
|
|
393
|
+
if (!current) {
|
|
394
|
+
throw new Error('Preview card not open.');
|
|
395
|
+
}
|
|
396
|
+
return current;
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
// 1. Moving from the chip onto the card keeps it open past the close grace period.
|
|
400
|
+
const opened = await hoverOpen();
|
|
401
|
+
await userEvent.hover(opened);
|
|
402
|
+
await sleep(PAST_CLOSE_DELAY);
|
|
403
|
+
await expect(card()).not.toBeNull();
|
|
404
|
+
|
|
405
|
+
// 2. Leaving the card closes it after the grace period.
|
|
406
|
+
await userEvent.hover(outside);
|
|
407
|
+
await waitFor(() => expect(card()).toBeNull(), { timeout: 5_000 });
|
|
408
|
+
|
|
409
|
+
// 3. Pressing inside the card (e.g. opening its menu) pins it: leaving no longer closes it.
|
|
410
|
+
const pinned = await hoverOpen();
|
|
411
|
+
await userEvent.pointer({ keys: '[MouseLeft]', target: pinned });
|
|
412
|
+
await userEvent.hover(outside);
|
|
413
|
+
await sleep(PAST_CLOSE_DELAY);
|
|
414
|
+
await expect(card()).not.toBeNull();
|
|
415
|
+
await userEvent.keyboard('{Escape}');
|
|
416
|
+
await waitFor(() => expect(card()).toBeNull(), { timeout: 5_000 });
|
|
417
|
+
},
|
|
418
|
+
};
|
|
419
|
+
|
|
361
420
|
/**
|
|
362
421
|
* Anchors with `trigger='click'` only open the preview card on click.
|
|
363
422
|
*/
|