@portabletext/plugin-emoji-picker 0.0.15 → 1.0.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/LICENSE +21 -0
- package/README.md +96 -1
- package/dist/index.cjs +61 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +61 -81
- package/dist/index.js.map +1 -1
- package/package.json +30 -30
- package/src/emoji-picker-machine.tsx +84 -99
- package/src/emoji-picker.feature +50 -1
- package/src/emoji-picker.test.tsx +42 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portabletext/plugin-emoji-picker",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Easily configure an Emoji Picker for the Portable Text Editor",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"portabletext",
|
|
@@ -36,53 +36,53 @@
|
|
|
36
36
|
"dist",
|
|
37
37
|
"src"
|
|
38
38
|
],
|
|
39
|
-
"scripts": {
|
|
40
|
-
"build": "pkg-utils build --strict --check --clean",
|
|
41
|
-
"check:lint": "biome lint .",
|
|
42
|
-
"check:react-compiler": "eslint --cache .",
|
|
43
|
-
"check:types": "tsc",
|
|
44
|
-
"check:types:watch": "tsc --watch",
|
|
45
|
-
"clean": "del .turbo && del lib && del node_modules",
|
|
46
|
-
"dev": "pkg-utils watch",
|
|
47
|
-
"lint:fix": "biome lint --write .",
|
|
48
|
-
"prepublishOnly": "turbo run build",
|
|
49
|
-
"test": "vitest --run",
|
|
50
|
-
"test:browser": "vitest --run --project browser",
|
|
51
|
-
"test:browser:chromium": "vitest run --project \"browser (chromium)\"",
|
|
52
|
-
"test:browser:chromium:watch": "vitest watch --project \"browser (chromium)\"",
|
|
53
|
-
"test:browser:firefox": "vitest run --project \"browser (firefox)\"",
|
|
54
|
-
"test:browser:firefox:watch": "vitest watch --project \"browser (firefox)\"",
|
|
55
|
-
"test:browser:webkit": "vitest run --project \"browser (webkit)\"",
|
|
56
|
-
"test:browser:webkit:watch": "vitest watch --project \"browser (webkit)\""
|
|
57
|
-
},
|
|
58
39
|
"dependencies": {
|
|
59
|
-
"@portabletext/keyboard-shortcuts": "workspace:^",
|
|
60
|
-
"@portabletext/plugin-input-rule": "workspace:~",
|
|
61
40
|
"@xstate/react": "^6.0.0",
|
|
62
41
|
"react-compiler-runtime": "1.0.0",
|
|
63
|
-
"xstate": "^5.23.0"
|
|
42
|
+
"xstate": "^5.23.0",
|
|
43
|
+
"@portabletext/keyboard-shortcuts": "^1.1.1",
|
|
44
|
+
"@portabletext/plugin-input-rule": "~0.4.0"
|
|
64
45
|
},
|
|
65
46
|
"devDependencies": {
|
|
66
|
-
"@portabletext/editor": "workspace:*",
|
|
67
|
-
"@portabletext/schema": "workspace:*",
|
|
68
47
|
"@sanity/pkg-utils": "^8.1.4",
|
|
69
48
|
"@types/react": "^19.2.0",
|
|
70
49
|
"@vitejs/plugin-react": "^5.0.4",
|
|
50
|
+
"@vitest/browser": "^3.2.4",
|
|
71
51
|
"babel-plugin-react-compiler": "1.0.0",
|
|
72
52
|
"eslint": "^9.38.0",
|
|
73
53
|
"eslint-formatter-gha": "^1.6.0",
|
|
74
54
|
"eslint-plugin-react-hooks": "7.0.0",
|
|
75
|
-
"racejar": "workspace:*",
|
|
76
55
|
"react": "^19.2.0",
|
|
77
|
-
"typescript": "
|
|
56
|
+
"typescript": "5.9.3",
|
|
78
57
|
"typescript-eslint": "^8.46.1",
|
|
79
|
-
"vitest": "^3.2.4"
|
|
58
|
+
"vitest": "^3.2.4",
|
|
59
|
+
"@portabletext/editor": "2.15.5",
|
|
60
|
+
"@portabletext/schema": "1.2.0",
|
|
61
|
+
"racejar": "1.3.2"
|
|
80
62
|
},
|
|
81
63
|
"peerDependencies": {
|
|
82
|
-
"@portabletext/editor": "
|
|
64
|
+
"@portabletext/editor": "^2.15.5",
|
|
83
65
|
"react": "^19.1.1"
|
|
84
66
|
},
|
|
85
67
|
"publishConfig": {
|
|
86
68
|
"access": "public"
|
|
69
|
+
},
|
|
70
|
+
"scripts": {
|
|
71
|
+
"build": "pkg-utils build --strict --check --clean",
|
|
72
|
+
"check:lint": "biome lint .",
|
|
73
|
+
"check:react-compiler": "eslint --cache .",
|
|
74
|
+
"check:types": "tsc",
|
|
75
|
+
"check:types:watch": "tsc --watch",
|
|
76
|
+
"clean": "del .turbo && del lib && del node_modules",
|
|
77
|
+
"dev": "pkg-utils watch",
|
|
78
|
+
"lint:fix": "biome lint --write .",
|
|
79
|
+
"test": "vitest --run",
|
|
80
|
+
"test:browser": "vitest --run --project browser",
|
|
81
|
+
"test:browser:chromium": "vitest run --project \"browser (chromium)\"",
|
|
82
|
+
"test:browser:chromium:watch": "vitest watch --project \"browser (chromium)\"",
|
|
83
|
+
"test:browser:firefox": "vitest run --project \"browser (firefox)\"",
|
|
84
|
+
"test:browser:firefox:watch": "vitest watch --project \"browser (firefox)\"",
|
|
85
|
+
"test:browser:webkit": "vitest run --project \"browser (webkit)\"",
|
|
86
|
+
"test:browser:webkit:watch": "vitest watch --project \"browser (webkit)\""
|
|
87
87
|
}
|
|
88
|
-
}
|
|
88
|
+
}
|
|
@@ -192,15 +192,8 @@ type EmojiPickerContext<TEmojiMatch = EmojiMatch> = {
|
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
type EmojiPickerEvent =
|
|
195
|
-
|
|
|
196
|
-
|
|
197
|
-
keyword: string
|
|
198
|
-
keywordAnchor: {
|
|
199
|
-
point: EditorSelectionPoint
|
|
200
|
-
blockOffset: BlockOffset
|
|
201
|
-
}
|
|
202
|
-
keywordFocus: BlockOffset
|
|
203
|
-
}
|
|
195
|
+
| TriggerFoundEvent
|
|
196
|
+
| PartialKeywordFoundEvent
|
|
204
197
|
| KeywordFoundEvent
|
|
205
198
|
| {
|
|
206
199
|
type: 'selection changed'
|
|
@@ -236,7 +229,7 @@ type EmojiPickerEvent =
|
|
|
236
229
|
type: 'insert selected match'
|
|
237
230
|
}
|
|
238
231
|
|
|
239
|
-
const
|
|
232
|
+
const triggerListenerCallback: CallbackLogicFunction<
|
|
240
233
|
AnyEventObject,
|
|
241
234
|
EmojiPickerEvent,
|
|
242
235
|
{editor: Editor}
|
|
@@ -268,10 +261,7 @@ const colonListenerCallback: CallbackLogicFunction<
|
|
|
268
261
|
actions: [
|
|
269
262
|
({event}) => [
|
|
270
263
|
effect(() => {
|
|
271
|
-
sendBack(
|
|
272
|
-
...event,
|
|
273
|
-
type: 'colon inserted',
|
|
274
|
-
})
|
|
264
|
+
sendBack(event)
|
|
275
265
|
}),
|
|
276
266
|
],
|
|
277
267
|
],
|
|
@@ -283,10 +273,7 @@ const colonListenerCallback: CallbackLogicFunction<
|
|
|
283
273
|
actions: [
|
|
284
274
|
({event}) => [
|
|
285
275
|
effect(() => {
|
|
286
|
-
sendBack(
|
|
287
|
-
...event,
|
|
288
|
-
type: 'colon inserted',
|
|
289
|
-
})
|
|
276
|
+
sendBack(event)
|
|
290
277
|
}),
|
|
291
278
|
],
|
|
292
279
|
],
|
|
@@ -363,76 +350,34 @@ const arrowListenerCallback: CallbackLogicFunction<
|
|
|
363
350
|
}
|
|
364
351
|
|
|
365
352
|
const emojiInsertListener: CallbackLogicFunction<
|
|
366
|
-
|
|
353
|
+
AnyEventObject,
|
|
367
354
|
EmojiPickerEvent,
|
|
368
355
|
{context: EmojiPickerContext}
|
|
369
|
-
> = ({sendBack, input
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
type: 'delete.text',
|
|
391
|
-
at: {anchor: event.anchor, focus: event.focus},
|
|
392
|
-
}),
|
|
393
|
-
raise({
|
|
394
|
-
type: 'insert.text',
|
|
395
|
-
text: event.emoji,
|
|
396
|
-
}),
|
|
397
|
-
],
|
|
398
|
-
],
|
|
399
|
-
}),
|
|
400
|
-
}),
|
|
401
|
-
input.context.editor.registerBehavior({
|
|
402
|
-
behavior: defineBehavior({
|
|
403
|
-
on: 'insert.text',
|
|
404
|
-
guard: ({event}) => {
|
|
405
|
-
if (event.text !== ':') {
|
|
406
|
-
return false
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
const anchor = context.keywordAnchor?.blockOffset
|
|
410
|
-
const focus = context.keywordFocus
|
|
411
|
-
const match = context.matches[context.selectedIndex]
|
|
412
|
-
|
|
413
|
-
return match && match.type === 'exact' && anchor && focus
|
|
414
|
-
? {anchor, focus, emoji: match.emoji}
|
|
415
|
-
: false
|
|
416
|
-
},
|
|
417
|
-
actions: [
|
|
418
|
-
(_, {anchor, focus, emoji}) => [
|
|
419
|
-
raise({
|
|
420
|
-
type: 'custom.insert emoji',
|
|
421
|
-
emoji,
|
|
422
|
-
anchor,
|
|
423
|
-
focus,
|
|
424
|
-
}),
|
|
425
|
-
],
|
|
356
|
+
> = ({sendBack, input}) => {
|
|
357
|
+
return input.context.editor.registerBehavior({
|
|
358
|
+
behavior: defineBehavior<{
|
|
359
|
+
emoji: string
|
|
360
|
+
anchor: BlockOffset
|
|
361
|
+
focus: BlockOffset
|
|
362
|
+
}>({
|
|
363
|
+
on: 'custom.insert emoji',
|
|
364
|
+
actions: [
|
|
365
|
+
({event}) => [
|
|
366
|
+
effect(() => {
|
|
367
|
+
sendBack({type: 'dismiss'})
|
|
368
|
+
}),
|
|
369
|
+
raise({
|
|
370
|
+
type: 'delete.text',
|
|
371
|
+
at: {anchor: event.anchor, focus: event.focus},
|
|
372
|
+
}),
|
|
373
|
+
raise({
|
|
374
|
+
type: 'insert.text',
|
|
375
|
+
text: event.emoji,
|
|
376
|
+
}),
|
|
426
377
|
],
|
|
427
|
-
|
|
378
|
+
],
|
|
428
379
|
}),
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
return () => {
|
|
432
|
-
for (const unregister of unregisterBehaviors) {
|
|
433
|
-
unregister()
|
|
434
|
-
}
|
|
435
|
-
}
|
|
380
|
+
})
|
|
436
381
|
}
|
|
437
382
|
|
|
438
383
|
const submitListenerCallback: CallbackLogicFunction<
|
|
@@ -493,6 +438,31 @@ const submitListenerCallback: CallbackLogicFunction<
|
|
|
493
438
|
],
|
|
494
439
|
}),
|
|
495
440
|
}),
|
|
441
|
+
input.context.editor.registerBehavior({
|
|
442
|
+
behavior: defineInputRuleBehavior({
|
|
443
|
+
rules: [keywordRule],
|
|
444
|
+
}),
|
|
445
|
+
}),
|
|
446
|
+
input.context.editor.registerBehavior({
|
|
447
|
+
behavior: defineBehavior<
|
|
448
|
+
KeywordFoundEvent,
|
|
449
|
+
KeywordFoundEvent['type'],
|
|
450
|
+
{
|
|
451
|
+
anchor: BlockOffset
|
|
452
|
+
focus: BlockOffset
|
|
453
|
+
emoji: string
|
|
454
|
+
}
|
|
455
|
+
>({
|
|
456
|
+
on: 'custom.keyword found',
|
|
457
|
+
actions: [
|
|
458
|
+
({event}) => [
|
|
459
|
+
effect(() => {
|
|
460
|
+
sendBack(event)
|
|
461
|
+
}),
|
|
462
|
+
],
|
|
463
|
+
],
|
|
464
|
+
}),
|
|
465
|
+
}),
|
|
496
466
|
]
|
|
497
467
|
|
|
498
468
|
return () => {
|
|
@@ -603,7 +573,7 @@ export const emojiPickerMachine = setup({
|
|
|
603
573
|
'emoji insert listener': fromCallback(emojiInsertListener),
|
|
604
574
|
'submit listener': fromCallback(submitListenerCallback),
|
|
605
575
|
'arrow listener': fromCallback(arrowListenerCallback),
|
|
606
|
-
'
|
|
576
|
+
'trigger listener': fromCallback(triggerListenerCallback),
|
|
607
577
|
'escape listener': fromCallback(escapeListenerCallback),
|
|
608
578
|
'selection listener': fromCallback(selectionListenerCallback),
|
|
609
579
|
'text change listener': fromCallback(textChangeListener),
|
|
@@ -612,7 +582,8 @@ export const emojiPickerMachine = setup({
|
|
|
612
582
|
'init keyword': assign({
|
|
613
583
|
keyword: ({context, event}) => {
|
|
614
584
|
if (
|
|
615
|
-
event.type !== '
|
|
585
|
+
event.type !== 'custom.trigger found' &&
|
|
586
|
+
event.type !== 'custom.partial keyword found' &&
|
|
616
587
|
event.type !== 'custom.keyword found'
|
|
617
588
|
) {
|
|
618
589
|
return context.keyword
|
|
@@ -624,7 +595,8 @@ export const emojiPickerMachine = setup({
|
|
|
624
595
|
'set keyword anchor': assign({
|
|
625
596
|
keywordAnchor: ({context, event}) => {
|
|
626
597
|
if (
|
|
627
|
-
event.type !== '
|
|
598
|
+
event.type !== 'custom.trigger found' &&
|
|
599
|
+
event.type !== 'custom.partial keyword found' &&
|
|
628
600
|
event.type !== 'custom.keyword found'
|
|
629
601
|
) {
|
|
630
602
|
return context.keywordAnchor
|
|
@@ -636,7 +608,8 @@ export const emojiPickerMachine = setup({
|
|
|
636
608
|
'set keyword focus': assign({
|
|
637
609
|
keywordFocus: ({context, event}) => {
|
|
638
610
|
if (
|
|
639
|
-
event.type !== '
|
|
611
|
+
event.type !== 'custom.trigger found' &&
|
|
612
|
+
event.type !== 'custom.partial keyword found' &&
|
|
640
613
|
event.type !== 'custom.keyword found'
|
|
641
614
|
) {
|
|
642
615
|
return context.keywordFocus
|
|
@@ -753,13 +726,17 @@ export const emojiPickerMachine = setup({
|
|
|
753
726
|
context,
|
|
754
727
|
}),
|
|
755
728
|
),
|
|
756
|
-
'insert selected match': ({context}) => {
|
|
729
|
+
'insert selected match': ({context, event}) => {
|
|
757
730
|
const match = context.matches[context.selectedIndex]
|
|
758
731
|
|
|
759
732
|
if (!match || !context.keywordAnchor || !context.keywordFocus) {
|
|
760
733
|
return
|
|
761
734
|
}
|
|
762
735
|
|
|
736
|
+
if (event.type === 'custom.keyword found' && match.type !== 'exact') {
|
|
737
|
+
return
|
|
738
|
+
}
|
|
739
|
+
|
|
763
740
|
context.editor.send({
|
|
764
741
|
type: 'custom.insert emoji',
|
|
765
742
|
emoji: match.emoji,
|
|
@@ -876,11 +853,15 @@ export const emojiPickerMachine = setup({
|
|
|
876
853
|
idle: {
|
|
877
854
|
entry: ['reset'],
|
|
878
855
|
invoke: {
|
|
879
|
-
src: '
|
|
856
|
+
src: 'trigger listener',
|
|
880
857
|
input: ({context}) => ({editor: context.editor}),
|
|
881
858
|
},
|
|
882
859
|
on: {
|
|
883
|
-
'
|
|
860
|
+
'custom.trigger found': {
|
|
861
|
+
target: 'searching',
|
|
862
|
+
actions: ['set keyword anchor', 'set keyword focus', 'init keyword'],
|
|
863
|
+
},
|
|
864
|
+
'custom.partial keyword found': {
|
|
884
865
|
target: 'searching',
|
|
885
866
|
actions: ['set keyword anchor', 'set keyword focus', 'init keyword'],
|
|
886
867
|
},
|
|
@@ -892,6 +873,8 @@ export const emojiPickerMachine = setup({
|
|
|
892
873
|
'update matches',
|
|
893
874
|
'insert selected match',
|
|
894
875
|
],
|
|
876
|
+
target: 'idle',
|
|
877
|
+
reenter: true,
|
|
895
878
|
},
|
|
896
879
|
},
|
|
897
880
|
},
|
|
@@ -916,6 +899,15 @@ export const emojiPickerMachine = setup({
|
|
|
916
899
|
},
|
|
917
900
|
],
|
|
918
901
|
on: {
|
|
902
|
+
'custom.keyword found': {
|
|
903
|
+
actions: [
|
|
904
|
+
'set keyword anchor',
|
|
905
|
+
'set keyword focus',
|
|
906
|
+
'init keyword',
|
|
907
|
+
'update matches',
|
|
908
|
+
'insert selected match',
|
|
909
|
+
],
|
|
910
|
+
},
|
|
919
911
|
'insert.text': [
|
|
920
912
|
{
|
|
921
913
|
guard: 'unexpected text insertion',
|
|
@@ -944,7 +936,6 @@ export const emojiPickerMachine = setup({
|
|
|
944
936
|
'update keyword',
|
|
945
937
|
'update matches',
|
|
946
938
|
'reset selected index',
|
|
947
|
-
'update emoji insert listener context',
|
|
948
939
|
'update submit listener context',
|
|
949
940
|
],
|
|
950
941
|
},
|
|
@@ -980,23 +971,17 @@ export const emojiPickerMachine = setup({
|
|
|
980
971
|
'navigate down': {
|
|
981
972
|
actions: [
|
|
982
973
|
'increment selected index',
|
|
983
|
-
'update emoji insert listener context',
|
|
984
974
|
'update submit listener context',
|
|
985
975
|
],
|
|
986
976
|
},
|
|
987
977
|
'navigate up': {
|
|
988
978
|
actions: [
|
|
989
979
|
'decrement selected index',
|
|
990
|
-
'update emoji insert listener context',
|
|
991
980
|
'update submit listener context',
|
|
992
981
|
],
|
|
993
982
|
},
|
|
994
983
|
'navigate to': {
|
|
995
|
-
actions: [
|
|
996
|
-
'set selected index',
|
|
997
|
-
'update emoji insert listener context',
|
|
998
|
-
'update submit listener context',
|
|
999
|
-
],
|
|
984
|
+
actions: ['set selected index', 'update submit listener context'],
|
|
1000
985
|
},
|
|
1001
986
|
'insert selected match': {
|
|
1002
987
|
actions: ['insert selected match'],
|
package/src/emoji-picker.feature
CHANGED
|
@@ -3,9 +3,58 @@ Feature: Emoji Picker
|
|
|
3
3
|
Background:
|
|
4
4
|
Given the editor is focused
|
|
5
5
|
|
|
6
|
-
Scenario: Picking a direct hit
|
|
6
|
+
Scenario Outline: Picking a direct hit
|
|
7
|
+
When <initial text> is inserted
|
|
8
|
+
When <inserted text> is inserted
|
|
9
|
+
Then the text is <final text>
|
|
10
|
+
|
|
11
|
+
Examples:
|
|
12
|
+
| initial text | inserted text | final text |
|
|
13
|
+
| "" | ":joy:" | "😂" |
|
|
14
|
+
| ":jo" | "y:" | "😂" |
|
|
15
|
+
| ":joy" | ":" | "😂" |
|
|
16
|
+
|
|
17
|
+
Scenario: Undo after direct hit
|
|
7
18
|
When ":joy:" is typed
|
|
8
19
|
Then the text is "😂"
|
|
20
|
+
When undo is performed
|
|
21
|
+
Then the text is ":joy:"
|
|
22
|
+
|
|
23
|
+
Scenario: Picking direct hit after undo
|
|
24
|
+
When ":joy:" is typed
|
|
25
|
+
And undo is performed
|
|
26
|
+
And "{Backspace}" is pressed
|
|
27
|
+
And ":" is typed
|
|
28
|
+
Then the text is "😂"
|
|
29
|
+
And the keyword is ""
|
|
30
|
+
And the matches are ""
|
|
31
|
+
|
|
32
|
+
Scenario: Picking wrong direct hit
|
|
33
|
+
When ":jo:" is typed
|
|
34
|
+
Then the text is ":jo:"
|
|
35
|
+
And the keyword is ":jo:"
|
|
36
|
+
And the matches are "😂, 😹"
|
|
37
|
+
|
|
38
|
+
Scenario: Colon after wrong direct hit
|
|
39
|
+
When ":jo:" is typed
|
|
40
|
+
And ":" is typed
|
|
41
|
+
Then the text is ":jo::"
|
|
42
|
+
And the keyword is ":jo::"
|
|
43
|
+
And the matches are ""
|
|
44
|
+
|
|
45
|
+
Scenario: Picking wrong direct hit after undoing direct hit
|
|
46
|
+
When ":joy:" is typed
|
|
47
|
+
And undo is performed
|
|
48
|
+
And "{Backspace}{Backspace}" is pressed
|
|
49
|
+
And ":" is typed
|
|
50
|
+
Then the text is ":jo:"
|
|
51
|
+
And the keyword is ""
|
|
52
|
+
And the matches are ""
|
|
53
|
+
|
|
54
|
+
Scenario: Two consecutive direct hits
|
|
55
|
+
When ":joy:" is typed
|
|
56
|
+
And ":joy_cat:" is typed
|
|
57
|
+
Then the text is "😂😹"
|
|
9
58
|
|
|
10
59
|
Scenario: Picking the closest hit with Enter
|
|
11
60
|
When ":joy" is typed
|
|
@@ -5,15 +5,22 @@ import {
|
|
|
5
5
|
type Context,
|
|
6
6
|
} from '@portabletext/editor/test/vitest'
|
|
7
7
|
import {defineSchema} from '@portabletext/schema'
|
|
8
|
-
import {
|
|
8
|
+
import {page, type Locator} from '@vitest/browser/context'
|
|
9
|
+
import {Before, Then} from 'racejar'
|
|
9
10
|
import {Feature} from 'racejar/vitest'
|
|
11
|
+
import {expect, vi} from 'vitest'
|
|
10
12
|
import emojiPickerFeature from './emoji-picker.feature?raw'
|
|
11
13
|
import {createMatchEmojis} from './match-emojis'
|
|
12
14
|
import {useEmojiPicker} from './use-emoji-picker'
|
|
13
15
|
|
|
16
|
+
type EmojiPickerTestContext = Context & {
|
|
17
|
+
keywordLocator: Locator
|
|
18
|
+
matchesLocator: Locator
|
|
19
|
+
}
|
|
20
|
+
|
|
14
21
|
Feature({
|
|
15
22
|
hooks: [
|
|
16
|
-
Before(async (context:
|
|
23
|
+
Before(async (context: EmojiPickerTestContext) => {
|
|
17
24
|
const {editor, locator} = await createTestEditor({
|
|
18
25
|
children: <EmojiPickerPlugin />,
|
|
19
26
|
schemaDefinition: defineSchema({
|
|
@@ -24,10 +31,33 @@ Feature({
|
|
|
24
31
|
|
|
25
32
|
context.locator = locator
|
|
26
33
|
context.editor = editor
|
|
34
|
+
context.keywordLocator = page.getByTestId('keyword')
|
|
35
|
+
context.matchesLocator = page.getByTestId('matches')
|
|
36
|
+
|
|
37
|
+
await vi.waitFor(() =>
|
|
38
|
+
expect.element(context.keywordLocator).toBeInTheDocument(),
|
|
39
|
+
)
|
|
40
|
+
await vi.waitFor(() =>
|
|
41
|
+
expect.element(context.matchesLocator).toBeInTheDocument(),
|
|
42
|
+
)
|
|
27
43
|
}),
|
|
28
44
|
],
|
|
29
45
|
featureText: emojiPickerFeature,
|
|
30
|
-
stepDefinitions
|
|
46
|
+
stepDefinitions: [
|
|
47
|
+
...stepDefinitions,
|
|
48
|
+
Then(
|
|
49
|
+
'the keyword is {string}',
|
|
50
|
+
(context: EmojiPickerTestContext, keyword: string) => {
|
|
51
|
+
expect(context.keywordLocator.element().textContent).toEqual(keyword)
|
|
52
|
+
},
|
|
53
|
+
),
|
|
54
|
+
Then(
|
|
55
|
+
'the matches are {string}',
|
|
56
|
+
(context: EmojiPickerTestContext, matches: string) => {
|
|
57
|
+
expect(context.matchesLocator.element().textContent).toEqual(matches)
|
|
58
|
+
},
|
|
59
|
+
),
|
|
60
|
+
],
|
|
31
61
|
parameterTypes,
|
|
32
62
|
})
|
|
33
63
|
|
|
@@ -39,7 +69,14 @@ const emojis: Record<string, Array<string>> = {
|
|
|
39
69
|
const matchEmojis = createMatchEmojis({emojis})
|
|
40
70
|
|
|
41
71
|
function EmojiPickerPlugin() {
|
|
42
|
-
useEmojiPicker({matchEmojis})
|
|
72
|
+
const {keyword, matches} = useEmojiPicker({matchEmojis})
|
|
43
73
|
|
|
44
|
-
return
|
|
74
|
+
return (
|
|
75
|
+
<>
|
|
76
|
+
<div data-testid="keyword">{keyword}</div>
|
|
77
|
+
<div data-testid="matches">
|
|
78
|
+
{matches.map((match) => match.emoji).join(', ')}
|
|
79
|
+
</div>
|
|
80
|
+
</>
|
|
81
|
+
)
|
|
45
82
|
}
|