@memori.ai/memori-react 2.7.1 → 2.8.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.
Files changed (58) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +49 -28
  3. package/dist/components/Chat/Chat.d.ts +2 -0
  4. package/dist/components/Chat/Chat.js +2 -2
  5. package/dist/components/Chat/Chat.js.map +1 -1
  6. package/dist/components/MediaWidget/MediaItemWidget.d.ts +3 -1
  7. package/dist/components/MediaWidget/MediaItemWidget.js +8 -4
  8. package/dist/components/MediaWidget/MediaItemWidget.js.map +1 -1
  9. package/dist/components/MediaWidget/MediaItemWidget.test.js +4 -0
  10. package/dist/components/MediaWidget/MediaItemWidget.test.js.map +1 -1
  11. package/dist/components/MediaWidget/MediaWidget.d.ts +2 -0
  12. package/dist/components/MediaWidget/MediaWidget.js +2 -2
  13. package/dist/components/MediaWidget/MediaWidget.js.map +1 -1
  14. package/dist/components/MemoriWidget/MemoriWidget.d.ts +2 -1
  15. package/dist/components/MemoriWidget/MemoriWidget.js +2 -1
  16. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  17. package/dist/components/StartPanel/StartPanel.js +6 -9
  18. package/dist/components/StartPanel/StartPanel.js.map +1 -1
  19. package/dist/components/ui/Select.css +2 -1
  20. package/dist/index.d.ts +1 -0
  21. package/dist/index.js +2 -2
  22. package/dist/index.js.map +1 -1
  23. package/esm/components/Chat/Chat.d.ts +2 -0
  24. package/esm/components/Chat/Chat.js +2 -2
  25. package/esm/components/Chat/Chat.js.map +1 -1
  26. package/esm/components/MediaWidget/MediaItemWidget.d.ts +3 -1
  27. package/esm/components/MediaWidget/MediaItemWidget.js +8 -4
  28. package/esm/components/MediaWidget/MediaItemWidget.js.map +1 -1
  29. package/esm/components/MediaWidget/MediaItemWidget.test.js +4 -0
  30. package/esm/components/MediaWidget/MediaItemWidget.test.js.map +1 -1
  31. package/esm/components/MediaWidget/MediaWidget.d.ts +2 -0
  32. package/esm/components/MediaWidget/MediaWidget.js +2 -2
  33. package/esm/components/MediaWidget/MediaWidget.js.map +1 -1
  34. package/esm/components/MemoriWidget/MemoriWidget.d.ts +2 -1
  35. package/esm/components/MemoriWidget/MemoriWidget.js +2 -1
  36. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  37. package/esm/components/StartPanel/StartPanel.js +6 -9
  38. package/esm/components/StartPanel/StartPanel.js.map +1 -1
  39. package/esm/components/ui/Select.css +2 -1
  40. package/esm/index.d.ts +1 -0
  41. package/esm/index.js +2 -2
  42. package/esm/index.js.map +1 -1
  43. package/package.json +1 -1
  44. package/src/components/Chat/Chat.tsx +6 -1
  45. package/src/components/MediaWidget/MediaItemWidget.stories.tsx +25 -0
  46. package/src/components/MediaWidget/MediaItemWidget.test.tsx +11 -0
  47. package/src/components/MediaWidget/MediaItemWidget.tsx +12 -0
  48. package/src/components/MediaWidget/MediaWidget.tsx +4 -1
  49. package/src/components/MediaWidget/__snapshots__/MediaItemWidget.test.tsx.snap +12 -0
  50. package/src/components/MemoriWidget/MemoriWidget.stories.tsx +34 -0
  51. package/src/components/MemoriWidget/MemoriWidget.tsx +3 -0
  52. package/src/components/StartPanel/StartPanel.tsx +23 -19
  53. package/src/components/StartPanel/__snapshots__/StartPanel.test.tsx.snap +62 -50
  54. package/src/components/layouts/__snapshots__/Chat.test.tsx.snap +62 -50
  55. package/src/components/layouts/__snapshots__/FullPage.test.tsx.snap +62 -50
  56. package/src/components/layouts/__snapshots__/Totem.test.tsx.snap +62 -50
  57. package/src/components/ui/Select.css +2 -1
  58. package/src/index.tsx +3 -0
@@ -9,7 +9,9 @@ import {
9
9
  import { hasTouchscreen } from '../../helpers/utils';
10
10
  import { getResourceUrl } from '../../helpers/media';
11
11
  import ChatBubble from '../ChatBubble/ChatBubble';
12
- import MediaWidget from '../MediaWidget/MediaWidget';
12
+ import MediaWidget, {
13
+ Props as MediaWidgetProps,
14
+ } from '../MediaWidget/MediaWidget';
13
15
  import Button from '../ui/Button';
14
16
  import memoriApiClient from '@memori.ai/memori-api-client';
15
17
  import ChatInputs from '../ChatInputs/ChatInputs';
@@ -51,6 +53,7 @@ export interface Props {
51
53
  startListening: () => void;
52
54
  stopListening: () => void;
53
55
  resetTranscript: () => void;
56
+ customMediaRenderer?: MediaWidgetProps['customMediaRenderer'];
54
57
  }
55
58
 
56
59
  const Chat: React.FC<Props> = ({
@@ -87,6 +90,7 @@ const Chat: React.FC<Props> = ({
87
90
  startListening,
88
91
  stopListening,
89
92
  resetTranscript,
93
+ customMediaRenderer,
90
94
  }) => {
91
95
  const scrollToBottom = () => {
92
96
  setTimeout(() => {
@@ -249,6 +253,7 @@ const Chat: React.FC<Props> = ({
249
253
  baseUrl={baseUrl}
250
254
  apiUrl={apiUrl}
251
255
  translateTo={translateTo}
256
+ customMediaRenderer={customMediaRenderer}
252
257
  />
253
258
  </React.Fragment>
254
259
  ))}
@@ -94,3 +94,28 @@ ImagesGrid.args = {
94
94
  }?random=${i}`,
95
95
  })),
96
96
  };
97
+
98
+ export const WithCustomMediaRenderer = Template.bind({});
99
+ WithCustomMediaRenderer.args = {
100
+ items: [
101
+ {
102
+ mediumID: '95226d7e-7bae-465e-8b80-995587bb5971',
103
+ mimeType: 'image/png',
104
+ title: 'Image',
105
+ url: 'https://picsum.photos/200/300',
106
+ },
107
+ {
108
+ mediumID: '95226d7e-7bae-465e-8b80-995587bb5973',
109
+ mimeType: 'application/pdf',
110
+ title: 'PDF',
111
+ url: 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf',
112
+ content:
113
+ 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf',
114
+ },
115
+ ],
116
+ customMediaRenderer: (mimeType: string) => {
117
+ if (mimeType === 'image/png') {
118
+ return <div>Custom Image Renderer</div>;
119
+ }
120
+ },
121
+ };
@@ -118,3 +118,14 @@ it('renders MediaItemWidget unchanged with css snippet to exec', () => {
118
118
  );
119
119
  expect(container).toMatchSnapshot();
120
120
  });
121
+
122
+ it('renders MediaItemWidget unchanged with custom media renderer', () => {
123
+ const { container } = render(
124
+ <MediaItemWidget
125
+ items={[]}
126
+ sessionID={sessionID}
127
+ customMediaRenderer={mimeType => <pre>{mimeType}</pre>}
128
+ />
129
+ );
130
+ expect(container).toMatchSnapshot();
131
+ });
@@ -20,6 +20,7 @@ export interface Props {
20
20
  translateTo?: string;
21
21
  baseURL?: string;
22
22
  apiURL?: string;
23
+ customMediaRenderer?: (mimeType: string) => JSX.Element | null;
23
24
  }
24
25
 
25
26
  export const RenderMediaItem = ({
@@ -31,6 +32,7 @@ export const RenderMediaItem = ({
31
32
  baseURL,
32
33
  apiURL,
33
34
  onClick,
35
+ customMediaRenderer,
34
36
  }: {
35
37
  isChild?: boolean;
36
38
  item: Medium;
@@ -40,6 +42,7 @@ export const RenderMediaItem = ({
40
42
  baseURL?: string;
41
43
  apiURL?: string;
42
44
  onClick?: (mediumID: string) => void;
45
+ customMediaRenderer?: (mimeType: string) => JSX.Element | null;
43
46
  }) => {
44
47
  const url = getResourceUrl({
45
48
  resourceURI: item.url,
@@ -49,6 +52,12 @@ export const RenderMediaItem = ({
49
52
  apiURL,
50
53
  });
51
54
 
55
+ const customRenderer = customMediaRenderer?.(item.mimeType);
56
+
57
+ if (customRenderer) {
58
+ return customRenderer;
59
+ }
60
+
52
61
  switch (item.mimeType) {
53
62
  case 'image/jpeg':
54
63
  case 'image/png':
@@ -245,6 +254,7 @@ const MediaItemWidget: React.FC<Props> = ({
245
254
  translateTo,
246
255
  baseURL,
247
256
  apiURL,
257
+ customMediaRenderer,
248
258
  }: Props) => {
249
259
  const [media, setMedia] = useState(items);
250
260
  const [openModalMedium, setOpenModalMedium] = useState<Medium>();
@@ -333,6 +343,7 @@ const MediaItemWidget: React.FC<Props> = ({
333
343
  url: item.url,
334
344
  content: item.content,
335
345
  }}
346
+ customMediaRenderer={customMediaRenderer}
336
347
  />
337
348
  </Transition.Child>
338
349
  ))}
@@ -388,6 +399,7 @@ const MediaItemWidget: React.FC<Props> = ({
388
399
  url: openModalMedium.url,
389
400
  content: openModalMedium.content,
390
401
  }}
402
+ customMediaRenderer={customMediaRenderer}
391
403
  />
392
404
  </Modal>
393
405
  )}
@@ -5,7 +5,7 @@ import {
5
5
  import React, { useEffect, useState } from 'react';
6
6
  import Button from '../ui/Button';
7
7
  import LinkItemWidget from './LinkItemWidget';
8
- import MediaItemWidget from './MediaItemWidget';
8
+ import MediaItemWidget, { Props as MediaItemProps } from './MediaItemWidget';
9
9
  import { Transition } from '@headlessui/react';
10
10
  import cx from 'classnames';
11
11
  import { useTranslation } from 'react-i18next';
@@ -19,6 +19,7 @@ export interface Props {
19
19
  baseUrl?: string;
20
20
  apiUrl?: string;
21
21
  translateTo?: string;
22
+ customMediaRenderer?: MediaItemProps['customMediaRenderer'];
22
23
  }
23
24
 
24
25
  const MediaWidget: React.FC<Props> = ({
@@ -30,6 +31,7 @@ const MediaWidget: React.FC<Props> = ({
30
31
  baseUrl,
31
32
  apiUrl,
32
33
  translateTo,
34
+ customMediaRenderer,
33
35
  }: Props) => {
34
36
  const { t } = useTranslation();
35
37
  const [showHints, setShowHints] = useState(true);
@@ -48,6 +50,7 @@ const MediaWidget: React.FC<Props> = ({
48
50
  translateTo={translateTo}
49
51
  baseURL={baseUrl}
50
52
  apiURL={apiUrl}
53
+ customMediaRenderer={customMediaRenderer}
51
54
  />
52
55
  )}
53
56
  {links?.length > 0 && <LinkItemWidget items={links} baseUrl={baseUrl} />}
@@ -151,6 +151,18 @@ exports[`renders MediaItemWidget unchanged with css snippet to show 1`] = `
151
151
  </div>
152
152
  `;
153
153
 
154
+ exports[`renders MediaItemWidget unchanged with custom media renderer 1`] = `
155
+ <div>
156
+ <div
157
+ class="memori-media-items"
158
+ >
159
+ <div
160
+ class="memori-media-items--grid"
161
+ />
162
+ </div>
163
+ </div>
164
+ `;
165
+
154
166
  exports[`renders MediaItemWidget unchanged with img 1`] = `
155
167
  <div>
156
168
  <div
@@ -84,6 +84,19 @@ WithPublicPageIntegrationAndFullbodyAvatar.args = {
84
84
  },
85
85
  };
86
86
 
87
+ export const WithPublicPageIntegrationAndNonDefaultLang = Template.bind({});
88
+ WithPublicPageIntegrationAndNonDefaultLang.args = {
89
+ memori,
90
+ tenant,
91
+ integration: {
92
+ ...integration,
93
+ customData: JSON.stringify({
94
+ ...JSON.parse(integration.customData ?? '{}'),
95
+ lang: 'es',
96
+ }),
97
+ },
98
+ };
99
+
87
100
  export const ShowInstruct = Template.bind({});
88
101
  ShowInstruct.args = {
89
102
  memori: {
@@ -114,3 +127,24 @@ WithAzureSpeechKey.args = {
114
127
  tenant,
115
128
  AZURE_COGNITIVE_SERVICES_TTS_KEY: 'provide your key here',
116
129
  };
130
+
131
+ export const WithCustomMediaRenderer = Template.bind({});
132
+ WithCustomMediaRenderer.args = {
133
+ memori,
134
+ tenant,
135
+ customMediaRenderer: (mimeType: string) => (
136
+ <div
137
+ style={{
138
+ width: '100%',
139
+ height: '100%',
140
+ backgroundColor: 'black',
141
+ color: 'white',
142
+ display: 'flex',
143
+ justifyContent: 'center',
144
+ alignItems: 'center',
145
+ }}
146
+ >
147
+ {mimeType}
148
+ </div>
149
+ ),
150
+ };
@@ -203,6 +203,7 @@ export interface Props {
203
203
  AZURE_COGNITIVE_SERVICES_TTS_KEY?: string;
204
204
  onStateChange?: (state?: DialogState) => void;
205
205
  additionalInfo?: OpenSession['additionalInfo'] & { [key: string]: string };
206
+ customMediaRenderer?: ChatProps['customMediaRenderer'];
206
207
  }
207
208
 
208
209
  const MemoriWidget = ({
@@ -235,6 +236,7 @@ const MemoriWidget = ({
235
236
  AZURE_COGNITIVE_SERVICES_TTS_KEY,
236
237
  onStateChange,
237
238
  additionalInfo,
239
+ customMediaRenderer,
238
240
  }: Props) => {
239
241
  const { t, i18n } = useTranslation();
240
242
 
@@ -2312,6 +2314,7 @@ const MemoriWidget = ({
2312
2314
  resetTranscript,
2313
2315
  listening,
2314
2316
  isPlayingAudio,
2317
+ customMediaRenderer,
2315
2318
  };
2316
2319
 
2317
2320
  const integrationBackground =
@@ -205,26 +205,30 @@ const StartPanel: React.FC<Props> = ({
205
205
 
206
206
  {integrationConfig?.multilanguage && !instruct && (
207
207
  <div className="memori--language-chooser">
208
- <Select
209
- label={
210
- t('write_and_speak.iWantToTalkToIn', {
211
- name: memori.name,
212
- }) || undefined
213
- }
208
+ <label id="user-lang-pref-label" htmlFor="user-lang-pref">
209
+ {t('write_and_speak.iWantToTalkToIn', {
210
+ name: memori.name,
211
+ })}
212
+ </label>
213
+ <select
214
+ id="user-lang-pref"
215
+ className="memori-select--button"
214
216
  value={(userLang ?? i18n.language).toUpperCase()}
215
- displayValue={
216
- chatLanguages.find(
217
- lang =>
218
- lang.value.toUpperCase() ===
219
- (userLang ?? i18n.language).toUpperCase()
220
- )?.label
221
- }
222
- onChange={setUserLang}
223
- options={chatLanguages.map(lang => ({
224
- label: lang.label,
225
- value: lang.value.toUpperCase(),
226
- }))}
227
- />
217
+ aria-labelledby="user-lang-pref-label"
218
+ onChange={e => {
219
+ setUserLang(e.target.value);
220
+ }}
221
+ >
222
+ {chatLanguages.map(lang => (
223
+ <option
224
+ key={lang.value}
225
+ value={lang.value}
226
+ aria-label={lang.label}
227
+ >
228
+ {lang.label}
229
+ </option>
230
+ ))}
231
+ </select>
228
232
  </div>
229
233
  )}
230
234
 
@@ -537,60 +537,72 @@ exports[`renders StartPanel with integrationConfig unchanged 1`] = `
537
537
  <div
538
538
  class="memori--language-chooser"
539
539
  >
540
- <div
541
- class="memori-select"
540
+ <label
541
+ for="user-lang-pref"
542
+ id="user-lang-pref-label"
543
+ >
544
+ write_and_speak.iWantToTalkToIn
545
+ </label>
546
+ <select
547
+ aria-labelledby="user-lang-pref-label"
548
+ class="memori-select--button"
549
+ id="user-lang-pref"
542
550
  >
543
- <input
544
- hidden=""
545
- name="day"
546
- readonly=""
547
- style="position: fixed; top: 1px; left: 1px; width: 1px; height: 0px; padding: 0px; margin: -1px; overflow: hidden; clip: rect(0px, 0px, 0px, 0px); white-space: nowrap; border-width: 0px; display: none;"
548
- type="hidden"
551
+ <option
552
+ aria-label="Italiano"
553
+ value="IT"
554
+ >
555
+ Italiano
556
+ </option>
557
+ <option
558
+ aria-label="English"
549
559
  value="EN"
550
- />
551
- <label
552
- class="memori-select--label"
553
- data-headlessui-state=""
554
- id="headlessui-listbox-label-:r0:"
555
560
  >
556
- write_and_speak.iWantToTalkToIn
557
- :
558
- </label>
559
- <button
560
- aria-expanded="false"
561
- aria-haspopup="true"
562
- aria-label="write_and_speak.iWantToTalkToIn"
563
- aria-labelledby="headlessui-listbox-label-:r0: headlessui-listbox-button-:r1:"
564
- class="memori-select--button"
565
- data-headlessui-state=""
566
- id="headlessui-listbox-button-:r1:"
567
- type="button"
561
+ English
562
+ </option>
563
+ <option
564
+ aria-label="Deutsch"
565
+ value="DE"
568
566
  >
569
- <span
570
- class="memori-select--value"
571
- >
572
- English
573
- </span>
574
- <span
575
- class="memori-select--icon"
576
- >
577
- <svg
578
- aria-hidden="true"
579
- fill="currentColor"
580
- focusable="false"
581
- role="img"
582
- viewBox="0 0 20 20"
583
- xmlns="http://www.w3.org/2000/svg"
584
- >
585
- <path
586
- clip-rule="evenodd"
587
- d="M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 9.2a.75.75 0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 0l-3.25-3.5a.75.75 0 01.04-1.06z"
588
- fill-rule="evenodd"
589
- />
590
- </svg>
591
- </span>
592
- </button>
593
- </div>
567
+ Deutsch
568
+ </option>
569
+ <option
570
+ aria-label="Español"
571
+ value="ES"
572
+ >
573
+ Español
574
+ </option>
575
+ <option
576
+ aria-label="Français"
577
+ value="FR"
578
+ >
579
+ Français
580
+ </option>
581
+ <option
582
+ aria-label="Português"
583
+ value="PT"
584
+ >
585
+ Português
586
+ </option>
587
+ <option
588
+ aria-label="Українська"
589
+ value="UK"
590
+ >
591
+ Українська
592
+ </option>
593
+ <option
594
+ aria-label="Русский"
595
+ value="RU"
596
+ >
597
+ Русский
598
+ </option>
599
+ <option
600
+ aria-label="Polski"
601
+ value="PL"
602
+ >
603
+ Polski
604
+ </option>
605
+ </select>
594
606
  </div>
595
607
  <button
596
608
  class="memori-button memori-button--primary memori-button--rounded memori-button--padded memori--start-button"
@@ -237,60 +237,72 @@ exports[`renders Chat layout unchanged 1`] = `
237
237
  <div
238
238
  class="memori--language-chooser"
239
239
  >
240
- <div
241
- class="memori-select"
240
+ <label
241
+ for="user-lang-pref"
242
+ id="user-lang-pref-label"
243
+ >
244
+ write_and_speak.iWantToTalkToIn
245
+ </label>
246
+ <select
247
+ aria-labelledby="user-lang-pref-label"
248
+ class="memori-select--button"
249
+ id="user-lang-pref"
242
250
  >
243
- <input
244
- hidden=""
245
- name="day"
246
- readonly=""
247
- style="position: fixed; top: 1px; left: 1px; width: 1px; height: 0px; padding: 0px; margin: -1px; overflow: hidden; clip: rect(0px, 0px, 0px, 0px); white-space: nowrap; border-width: 0px; display: none;"
248
- type="hidden"
251
+ <option
252
+ aria-label="Italiano"
249
253
  value="IT"
250
- />
251
- <label
252
- class="memori-select--label"
253
- data-headlessui-state=""
254
- id="headlessui-listbox-label-:r7:"
255
254
  >
256
- write_and_speak.iWantToTalkToIn
257
- :
258
- </label>
259
- <button
260
- aria-expanded="false"
261
- aria-haspopup="true"
262
- aria-label="write_and_speak.iWantToTalkToIn"
263
- aria-labelledby="headlessui-listbox-label-:r7: headlessui-listbox-button-:r8:"
264
- class="memori-select--button"
265
- data-headlessui-state=""
266
- id="headlessui-listbox-button-:r8:"
267
- type="button"
255
+ Italiano
256
+ </option>
257
+ <option
258
+ aria-label="English"
259
+ value="EN"
268
260
  >
269
- <span
270
- class="memori-select--value"
271
- >
272
- Italiano
273
- </span>
274
- <span
275
- class="memori-select--icon"
276
- >
277
- <svg
278
- aria-hidden="true"
279
- fill="currentColor"
280
- focusable="false"
281
- role="img"
282
- viewBox="0 0 20 20"
283
- xmlns="http://www.w3.org/2000/svg"
284
- >
285
- <path
286
- clip-rule="evenodd"
287
- d="M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 9.2a.75.75 0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 0l-3.25-3.5a.75.75 0 01.04-1.06z"
288
- fill-rule="evenodd"
289
- />
290
- </svg>
291
- </span>
292
- </button>
293
- </div>
261
+ English
262
+ </option>
263
+ <option
264
+ aria-label="Deutsch"
265
+ value="DE"
266
+ >
267
+ Deutsch
268
+ </option>
269
+ <option
270
+ aria-label="Español"
271
+ value="ES"
272
+ >
273
+ Español
274
+ </option>
275
+ <option
276
+ aria-label="Français"
277
+ value="FR"
278
+ >
279
+ Français
280
+ </option>
281
+ <option
282
+ aria-label="Português"
283
+ value="PT"
284
+ >
285
+ Português
286
+ </option>
287
+ <option
288
+ aria-label="Українська"
289
+ value="UK"
290
+ >
291
+ Українська
292
+ </option>
293
+ <option
294
+ aria-label="Русский"
295
+ value="RU"
296
+ >
297
+ Русский
298
+ </option>
299
+ <option
300
+ aria-label="Polski"
301
+ value="PL"
302
+ >
303
+ Polski
304
+ </option>
305
+ </select>
294
306
  </div>
295
307
  <button
296
308
  class="memori-button memori-button--primary memori-button--rounded memori-button--padded memori--start-button"