@portabletext/plugin-emoji-picker 0.0.15 → 1.0.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/plugin-emoji-picker",
3
- "version": "0.0.15",
3
+ "version": "1.0.1",
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": "catalog:",
56
+ "typescript": "5.9.3",
78
57
  "typescript-eslint": "^8.46.1",
79
- "vitest": "^3.2.4"
58
+ "vitest": "^3.2.4",
59
+ "racejar": "1.3.2",
60
+ "@portabletext/schema": "1.2.0",
61
+ "@portabletext/editor": "2.15.5"
80
62
  },
81
63
  "peerDependencies": {
82
- "@portabletext/editor": "workspace:^2.15.5",
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
+ }
@@ -0,0 +1,113 @@
1
+ import {emojis} from './emojis'
2
+ import type {MatchEmojis} from './match-emojis'
3
+
4
+ /**
5
+ * Proposed, but not required type, to represent an emoji match.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * {
10
+ * type: 'exact',
11
+ * key: '😂-joy',
12
+ * emoji: '😂',
13
+ * keyword: 'joy',
14
+ * }
15
+ * ```
16
+ * @example
17
+ * ```tsx
18
+ * {
19
+ * type: 'partial',
20
+ * key: '😹-joy-_cat',
21
+ * emoji: '😹',
22
+ * keyword: 'joy',
23
+ * startSlice: '',
24
+ * endSlice: '_cat',
25
+ * }
26
+ * ```
27
+ *
28
+ * @beta
29
+ */
30
+ export type EmojiMatch =
31
+ | {
32
+ type: 'exact'
33
+ key: string
34
+ emoji: string
35
+ keyword: string
36
+ }
37
+ | {
38
+ type: 'partial'
39
+ key: string
40
+ emoji: string
41
+ keyword: string
42
+ startSlice: string
43
+ endSlice: string
44
+ }
45
+
46
+ /**
47
+ * Proposed, but not required, default implementation of `MatchEmojis`.
48
+ *
49
+ * @beta
50
+ */
51
+ export const matchEmojis = createMatchEmojis({emojis})
52
+
53
+ /**
54
+ * Proposed, but not required, function to create a `MatchEmojis` function.
55
+ *
56
+ * @example
57
+ * ```ts
58
+ * const matchEmojis = createMatchEmojis({
59
+ * emojis: {
60
+ * '😂': ['joy'],
61
+ * '😹': ['joy_cat'],
62
+ * },
63
+ * })
64
+ * ```
65
+ *
66
+ * @beta
67
+ */
68
+ export function createMatchEmojis(config: {
69
+ emojis: Record<string, ReadonlyArray<string>>
70
+ }): MatchEmojis<EmojiMatch> {
71
+ return ({keyword}: {keyword: string}) => {
72
+ const foundEmojis: Array<EmojiMatch> = []
73
+
74
+ if (keyword.length < 1) {
75
+ return foundEmojis
76
+ }
77
+
78
+ for (const emoji in config.emojis) {
79
+ const emojiKeywords = config.emojis[emoji] ?? []
80
+
81
+ for (const emojiKeyword of emojiKeywords) {
82
+ const keywordIndex = emojiKeyword.indexOf(keyword)
83
+
84
+ if (keywordIndex === -1) {
85
+ continue
86
+ }
87
+
88
+ if (emojiKeyword === keyword) {
89
+ foundEmojis.push({
90
+ type: 'exact',
91
+ key: `${emoji}-${keyword}`,
92
+ emoji,
93
+ keyword,
94
+ })
95
+ } else {
96
+ const start = emojiKeyword.slice(0, keywordIndex)
97
+ const end = emojiKeyword.slice(keywordIndex + keyword.length)
98
+
99
+ foundEmojis.push({
100
+ type: 'partial',
101
+ key: `${emoji}-${start}${keyword}${end}`,
102
+ emoji,
103
+ keyword,
104
+ startSlice: start,
105
+ endSlice: end,
106
+ })
107
+ }
108
+ }
109
+ }
110
+
111
+ return foundEmojis
112
+ }
113
+ }
@@ -28,7 +28,7 @@ import {
28
28
  type AnyEventObject,
29
29
  type CallbackLogicFunction,
30
30
  } from 'xstate'
31
- import type {EmojiMatch, MatchEmojis} from './match-emojis'
31
+ import type {BaseEmojiMatch, MatchEmojis} from './match-emojis'
32
32
 
33
33
  /*******************
34
34
  * Keyboard shortcuts
@@ -175,10 +175,10 @@ function createKeywordFoundEvent(payload: {
175
175
  } as const
176
176
  }
177
177
 
178
- type EmojiPickerContext<TEmojiMatch = EmojiMatch> = {
178
+ type EmojiPickerContext = {
179
179
  editor: Editor
180
- matches: ReadonlyArray<TEmojiMatch>
181
- matchEmojis: MatchEmojis<TEmojiMatch>
180
+ matches: ReadonlyArray<BaseEmojiMatch>
181
+ matchEmojis: MatchEmojis<BaseEmojiMatch>
182
182
  selectedIndex: number
183
183
  keywordAnchor:
184
184
  | {
@@ -192,15 +192,8 @@ type EmojiPickerContext<TEmojiMatch = EmojiMatch> = {
192
192
  }
193
193
 
194
194
  type EmojiPickerEvent =
195
- | {
196
- type: 'colon inserted'
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 colonListenerCallback: CallbackLogicFunction<
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
- {type: 'context changed'; context: EmojiPickerContext},
353
+ AnyEventObject,
367
354
  EmojiPickerEvent,
368
355
  {context: EmojiPickerContext}
369
- > = ({sendBack, input, receive}) => {
370
- let context = input.context
371
-
372
- receive((event) => {
373
- context = event.context
374
- })
375
-
376
- const unregisterBehaviors = [
377
- input.context.editor.registerBehavior({
378
- behavior: defineBehavior<{
379
- emoji: string
380
- anchor: BlockOffset
381
- focus: BlockOffset
382
- }>({
383
- on: 'custom.insert emoji',
384
- actions: [
385
- ({event}) => [
386
- effect(() => {
387
- sendBack({type: 'dismiss'})
388
- }),
389
- raise({
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
- 'colon listener': fromCallback(colonListenerCallback),
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 !== 'colon inserted' &&
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 !== 'colon inserted' &&
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 !== 'colon inserted' &&
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: 'colon listener',
856
+ src: 'trigger listener',
880
857
  input: ({context}) => ({editor: context.editor}),
881
858
  },
882
859
  on: {
883
- 'colon inserted': {
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'],
@@ -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 {Before} from 'racejar'
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'
12
+ import {createMatchEmojis} from './create-match-emojis'
10
13
  import emojiPickerFeature from './emoji-picker.feature?raw'
11
- 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: 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 null
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
  }
package/src/emojis.ts CHANGED
@@ -1,6 +1,3 @@
1
- /**
2
- * @beta
3
- */
4
1
  export const emojis: Record<string, Array<string>> = {
5
2
  '😀': [
6
3
  'grinning_face',
package/src/index.ts CHANGED
@@ -1,2 +1,3 @@
1
+ export * from './create-match-emojis'
1
2
  export * from './match-emojis'
2
3
  export * from './use-emoji-picker'