@seamly/web-ui 18.1.1 → 18.3.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.
Files changed (165) hide show
  1. package/build/dist/lib/index.debug.js +286 -99
  2. package/build/dist/lib/index.debug.min.js +1 -1
  3. package/build/dist/lib/index.debug.min.js.LICENSE.txt +84 -16
  4. package/build/dist/lib/index.js +4104 -3887
  5. package/build/dist/lib/index.min.js +1 -1
  6. package/build/dist/lib/standalone.js +4351 -4084
  7. package/build/dist/lib/standalone.min.js +1 -1
  8. package/build/dist/lib/style-guide.js +746 -641
  9. package/build/dist/lib/style-guide.min.js +1 -1
  10. package/build/dist/lib/styles.css +1 -1
  11. package/build/dist/lib/utils.js +0 -1
  12. package/build/dist/lib/utils.min.js +1 -1
  13. package/build/dist/translations/de-informal.js +0 -1
  14. package/build/dist/translations/de-informal.min.js +1 -1
  15. package/build/dist/translations/en.js +0 -1
  16. package/build/dist/translations/en.min.js +1 -1
  17. package/build/dist/translations/es-informal.js +0 -1
  18. package/build/dist/translations/es-informal.min.js +1 -1
  19. package/build/dist/translations/nl-formal.js +0 -1
  20. package/build/dist/translations/nl-formal.min.js +1 -1
  21. package/build/dist/translations/nl-informal.js +0 -1
  22. package/build/dist/translations/nl-informal.min.js +1 -1
  23. package/package.json +4 -2
  24. package/src/javascripts/api/index.js +9 -9
  25. package/src/javascripts/api/producer.js +8 -8
  26. package/src/javascripts/config.js +9 -11
  27. package/src/javascripts/domains/app/actions.js +25 -0
  28. package/src/javascripts/domains/app/index.js +3 -0
  29. package/src/javascripts/domains/config/actions.js +6 -0
  30. package/src/javascripts/domains/config/hooks.js +6 -0
  31. package/src/javascripts/domains/config/index.js +8 -0
  32. package/src/javascripts/domains/config/middleware.js +26 -0
  33. package/src/javascripts/domains/config/reducer.js +74 -0
  34. package/src/javascripts/domains/config/selectors.js +23 -0
  35. package/src/javascripts/domains/forms/actions.js +1 -1
  36. package/src/javascripts/domains/forms/hooks.js +10 -14
  37. package/src/javascripts/domains/forms/provider.js +4 -6
  38. package/src/javascripts/domains/forms/selectors.js +3 -3
  39. package/src/javascripts/domains/i18n/index.js +9 -5
  40. package/src/javascripts/domains/interrupt/actions.js +6 -0
  41. package/src/javascripts/domains/interrupt/hooks.js +29 -0
  42. package/src/javascripts/domains/interrupt/index.js +9 -0
  43. package/src/javascripts/domains/interrupt/middleware.js +30 -0
  44. package/src/javascripts/domains/interrupt/reducer.js +22 -0
  45. package/src/javascripts/domains/interrupt/selectors.js +5 -0
  46. package/src/javascripts/domains/options/index.js +1 -0
  47. package/src/javascripts/domains/options/middleware.js +35 -0
  48. package/src/javascripts/domains/redux/create-redux-store.js +14 -6
  49. package/src/javascripts/domains/redux/hooks.js +2 -2
  50. package/src/javascripts/domains/redux/index.js +2 -1
  51. package/src/javascripts/domains/redux/provider.js +5 -0
  52. package/src/javascripts/domains/store/index.js +38 -0
  53. package/src/javascripts/{ui → domains}/store/state-reducer.js +4 -7
  54. package/src/javascripts/domains/translations/actions.js +3 -3
  55. package/src/javascripts/domains/translations/components/chat-status.js +6 -12
  56. package/src/javascripts/domains/translations/components/options-button.js +3 -3
  57. package/src/javascripts/domains/translations/components/options-dialog/form.js +2 -2
  58. package/src/javascripts/domains/translations/components/options-dialog/index.js +2 -5
  59. package/src/javascripts/domains/translations/hooks.js +1 -1
  60. package/src/javascripts/domains/translations/reducer.js +2 -2
  61. package/src/javascripts/domains/translations/selectors.js +2 -2
  62. package/src/javascripts/index.js +17 -5
  63. package/src/javascripts/lib/css.js +5 -5
  64. package/src/javascripts/lib/engine/index.js +38 -11
  65. package/src/javascripts/lib/external-api/index.js +6 -6
  66. package/src/javascripts/lib/i18n.js +2 -2
  67. package/src/javascripts/lib/parse-body.js +1 -1
  68. package/src/javascripts/lib/redux-helpers/index.js +18 -4
  69. package/src/javascripts/lib/split-url-params.js +2 -2
  70. package/src/javascripts/lib/store/providers/app-storage.js +1 -1
  71. package/src/javascripts/lib/store/providers/cookie-storage.js +1 -1
  72. package/src/javascripts/package/utils.js +0 -1
  73. package/src/javascripts/style-guide/components/app.js +5 -12
  74. package/src/javascripts/style-guide/components/links.js +6 -6
  75. package/src/javascripts/style-guide/components/static-core.js +23 -7
  76. package/src/javascripts/style-guide/state-helpers/index.js +1 -1
  77. package/src/javascripts/style-guide/states.js +27 -67
  78. package/src/javascripts/style-guide/style-guide-engine.js +1 -1
  79. package/src/javascripts/ui/components/chat-app.js +2 -2
  80. package/src/javascripts/ui/components/conversation/component-filter.js +2 -2
  81. package/src/javascripts/ui/components/conversation/conversation.js +2 -2
  82. package/src/javascripts/ui/components/conversation/event/card-component.js +29 -4
  83. package/src/javascripts/ui/components/conversation/event/carousel-component/components/pagination.js +2 -2
  84. package/src/javascripts/ui/components/conversation/event/carousel-component/index.js +4 -3
  85. package/src/javascripts/ui/components/conversation/event/carousel-message/components/slide.js +2 -1
  86. package/src/javascripts/ui/components/conversation/event/carousel-message/index.js +2 -2
  87. package/src/javascripts/ui/components/conversation/event/choice-prompt.js +11 -6
  88. package/src/javascripts/ui/components/conversation/event/cta.js +1 -6
  89. package/src/javascripts/ui/components/conversation/event/divider/variants/new-translation.js +1 -1
  90. package/src/javascripts/ui/components/conversation/event/event-participant.js +3 -5
  91. package/src/javascripts/ui/components/conversation/event/hooks/use-event-link-click-handler.js +2 -2
  92. package/src/javascripts/ui/components/conversation/event/hooks/use-formatted-date.js +3 -3
  93. package/src/javascripts/ui/components/conversation/event/hooks/use-text-rendering.js +3 -3
  94. package/src/javascripts/ui/components/conversation/event/participant.js +2 -2
  95. package/src/javascripts/ui/components/conversation/event/upload.js +12 -27
  96. package/src/javascripts/ui/components/conversation/message-container.js +4 -6
  97. package/src/javascripts/ui/components/core/seamly-activity-monitor.js +4 -5
  98. package/src/javascripts/ui/components/core/seamly-core.js +6 -7
  99. package/src/javascripts/ui/components/core/seamly-event-subscriber.js +16 -17
  100. package/src/javascripts/ui/components/core/seamly-file-upload.js +5 -6
  101. package/src/javascripts/ui/components/core/seamly-idle-detach-counter.js +2 -6
  102. package/src/javascripts/ui/components/core/seamly-initializer.js +7 -60
  103. package/src/javascripts/ui/components/core/seamly-instance-functions-loader.js +10 -10
  104. package/src/javascripts/ui/components/core/seamly-live-region.js +1 -1
  105. package/src/javascripts/ui/components/core/seamly-new-notifications.js +1 -1
  106. package/src/javascripts/ui/components/core/seamly-read-state.js +2 -2
  107. package/src/javascripts/ui/components/entry/entry-container.js +7 -10
  108. package/src/javascripts/ui/components/entry/toggle-button.js +24 -10
  109. package/src/javascripts/ui/components/entry/upload/index.js +4 -11
  110. package/src/javascripts/ui/components/faq/faq.js +4 -4
  111. package/src/javascripts/ui/components/form-controls/error.js +22 -0
  112. package/src/javascripts/ui/components/form-controls/file-input.js +3 -9
  113. package/src/javascripts/ui/components/form-controls/select.js +1 -1
  114. package/src/javascripts/ui/components/form-controls/wrapper.js +2 -9
  115. package/src/javascripts/ui/components/layout/agent-info.js +4 -4
  116. package/src/javascripts/ui/components/layout/app-frame.js +5 -5
  117. package/src/javascripts/ui/components/layout/chat-frame.js +3 -5
  118. package/src/javascripts/ui/components/layout/header.js +4 -18
  119. package/src/javascripts/ui/components/layout/privacy-disclaimer.js +2 -2
  120. package/src/javascripts/ui/components/options/cobrowsing.js +3 -7
  121. package/src/javascripts/ui/components/options/options-button.js +9 -13
  122. package/src/javascripts/ui/components/options/options-frame.js +1 -1
  123. package/src/javascripts/ui/components/options/transcript/index.js +2 -2
  124. package/src/javascripts/ui/components/options/transcript/transcript-form.js +1 -1
  125. package/src/javascripts/ui/components/warnings/cobrowsing-active-frame.js +3 -6
  126. package/src/javascripts/ui/components/warnings/idle-detach-warning.js +2 -6
  127. package/src/javascripts/ui/components/widgets/in-out-transition.js +2 -2
  128. package/src/javascripts/ui/components/widgets/lightbox.js +4 -4
  129. package/src/javascripts/ui/components/widgets/modal.js +3 -3
  130. package/src/javascripts/ui/components/widgets/upload-progress.js +2 -13
  131. package/src/javascripts/ui/hooks/component-helper-hooks.js +4 -15
  132. package/src/javascripts/ui/hooks/file-upload-hooks.js +3 -3
  133. package/src/javascripts/ui/hooks/focus-helper-hooks.js +4 -4
  134. package/src/javascripts/ui/hooks/live-region-hooks.js +2 -2
  135. package/src/javascripts/ui/hooks/seamly-entry-hooks.js +7 -6
  136. package/src/javascripts/ui/hooks/seamly-hooks.js +3 -9
  137. package/src/javascripts/ui/hooks/seamly-option-hooks.js +4 -4
  138. package/src/javascripts/ui/hooks/seamly-state-hooks.js +8 -16
  139. package/src/javascripts/ui/hooks/use-event-component-mapping.js +1 -1
  140. package/src/javascripts/ui/hooks/use-seamly-chat.js +1 -0
  141. package/src/javascripts/ui/hooks/use-seamly-commands.js +27 -49
  142. package/src/javascripts/ui/hooks/use-seamly-idle-detach-countdown.js +3 -3
  143. package/src/javascripts/ui/hooks/use-seamly-stored-visibility.js +3 -3
  144. package/src/javascripts/ui/hooks/use-seamly-visibility.js +3 -3
  145. package/src/javascripts/ui/hooks/utility-hooks.js +2 -2
  146. package/src/javascripts/ui/utils/form-utils.js +3 -3
  147. package/src/javascripts/ui/utils/general-utils.js +17 -11
  148. package/src/javascripts/ui/utils/seamly-utils.js +15 -83
  149. package/src/javascripts/ui/utils/validations.js +10 -7
  150. package/src/stylesheets/5-components/_card.scss +0 -1
  151. package/src/stylesheets/5-components/_choice-prompt.scss +5 -0
  152. package/src/stylesheets/5-components/_message.scss +10 -0
  153. package/src/stylesheets/5-components/_options.scss +3 -2
  154. package/translations/de-informal.js +0 -2
  155. package/translations/en.js +0 -2
  156. package/translations/es-informal.js +0 -2
  157. package/translations/nl-formal.js +0 -2
  158. package/translations/nl-informal.js +0 -2
  159. package/webpack/config.common.js +3 -3
  160. package/webpack/config.package.js +4 -4
  161. package/webpack/config.site.js +8 -6
  162. package/CHANGELOG.md +0 -551
  163. package/src/javascripts/ui/components/core/seamly-api.js +0 -44
  164. package/src/javascripts/ui/hooks/use-seamly-interrupt.js +0 -62
  165. package/src/javascripts/ui/store/index.js +0 -37
@@ -1,10 +1,5 @@
1
1
  import { useCallback, useEffect, useState } from 'preact/hooks'
2
- import {
3
- getSeamlyConfig,
4
- getUrlParams,
5
- getUrlSearchString,
6
- randomId,
7
- } from '@seamly/web-ui'
2
+ import { getUrlParams, getUrlSearchString, randomId } from '@seamly/web-ui'
8
3
  import StyleGuideView from './view'
9
4
  import StyleGuideLinks from './links'
10
5
  import { getStateObj } from '../states'
@@ -34,7 +29,7 @@ const StyleGuideApp = ({ config, styleGuideConfig, headingLevel = 2 }) => {
34
29
  })
35
30
 
36
31
  const updateState = useCallback(
37
- state => {
32
+ (state) => {
38
33
  if (stateUpdateCallback) {
39
34
  setStaticState(stateUpdateCallback(state))
40
35
  } else {
@@ -46,7 +41,7 @@ const StyleGuideApp = ({ config, styleGuideConfig, headingLevel = 2 }) => {
46
41
 
47
42
  const getState = useCallback(
48
43
  (layoutMode, feature) => {
49
- const { showDisclaimer, defaults } = config
44
+ const { defaults } = config
50
45
  const { headingText } = mainState[feature]
51
46
  setSelectedStateDescription(headingText)
52
47
  const bareState = mainState[feature][layoutMode]
@@ -57,13 +52,11 @@ const StyleGuideApp = ({ config, styleGuideConfig, headingLevel = 2 }) => {
57
52
  ...bareState,
58
53
  config: {
59
54
  ...bareState.config,
60
- ...getSeamlyConfig(config),
55
+ ...config,
61
56
  layoutMode: bareState.config.layoutMode,
62
57
  ...(overrideMessages ? { messages: overrideMessages } : {}),
63
58
  showFaq,
64
59
  },
65
- showDisclaimer:
66
- showDisclaimer != null ? showDisclaimer : bareState.showDisclaimer,
67
60
  headerTitles: {
68
61
  ...bareState.headerTitles,
69
62
  subTitle: agentName || bareState.headerTitles.subTitle,
@@ -82,7 +75,7 @@ const StyleGuideApp = ({ config, styleGuideConfig, headingLevel = 2 }) => {
82
75
  const overlay = document.querySelector('[aria-modal="true"]')
83
76
  if (overlay) {
84
77
  overlay.addEventListener('click', () => {
85
- setStaticState(s => ({
78
+ setStaticState((s) => ({
86
79
  ...s,
87
80
  visible: 'minimized',
88
81
  }))
@@ -4,7 +4,7 @@ import { categories } from '../states'
4
4
  const StyleGuideLinks = ({ setSelectedState, states, headingLevel }) => {
5
5
  const CategoryHeading = `h${headingLevel.toString()}`
6
6
  const FeatureHeading = `h${(parseInt(headingLevel, 10) + 1).toString()}`
7
- const onClickHandler = actionObj => {
7
+ const onClickHandler = (actionObj) => {
8
8
  window.history.pushState(
9
9
  null,
10
10
  null,
@@ -18,9 +18,9 @@ const StyleGuideLinks = ({ setSelectedState, states, headingLevel }) => {
18
18
  return (
19
19
  <nav aria-label="Style guide navigation" className="nav--styleguide">
20
20
  <ol className="list--styleguidestates">
21
- {Object.keys(categories).map(category => {
21
+ {Object.keys(categories).map((category) => {
22
22
  const { heading, description } = categories[category]
23
- const features = Object.keys(states).filter(feature => {
23
+ const features = Object.keys(states).filter((feature) => {
24
24
  const { category: featureCategory } = states[feature]
25
25
  return featureCategory === category
26
26
  })
@@ -30,7 +30,7 @@ const StyleGuideLinks = ({ setSelectedState, states, headingLevel }) => {
30
30
  <CategoryHeading>{heading}</CategoryHeading>
31
31
  <span className="category__description">{description}</span>
32
32
  <ol>
33
- {features.map(feature => {
33
+ {features.map((feature) => {
34
34
  const featureState = { ...states[feature] }
35
35
  delete featureState.category
36
36
  const {
@@ -50,7 +50,7 @@ const StyleGuideLinks = ({ setSelectedState, states, headingLevel }) => {
50
50
  </span>
51
51
  )}
52
52
  <ul className="list--styleguidelayouts">
53
- {Object.keys(rest).map(layoutMode => (
53
+ {Object.keys(rest).map((layoutMode) => (
54
54
  <li
55
55
  key={layoutMode}
56
56
  className={
@@ -74,7 +74,7 @@ const StyleGuideLinks = ({ setSelectedState, states, headingLevel }) => {
74
74
  ? 'page'
75
75
  : null
76
76
  }
77
- onClick={e => {
77
+ onClick={(e) => {
78
78
  e.preventDefault()
79
79
  if (layoutMode === 'inline') {
80
80
  setTimeout(() => {
@@ -6,10 +6,15 @@ import {
6
6
  StoreProvider,
7
7
  createReduxStore,
8
8
  } from '@seamly/web-ui'
9
- import stateReducer from '../../ui/store/state-reducer'
9
+ import stateReducer from '../../domains/store/state-reducer'
10
10
  import { Reducer as formReducer } from '../../domains/forms'
11
11
  import { Reducer as translationsReducer } from '../../domains/translations'
12
12
  import { Reducer as i18nReducer } from '../../domains/i18n'
13
+ import { Reducer as interruptReducer } from '../../domains/interrupt'
14
+ import {
15
+ Reducer as configReducer,
16
+ Actions as ConfigActions,
17
+ } from '../../domains/config'
13
18
 
14
19
  const bareApi = {
15
20
  send: () => {},
@@ -22,20 +27,31 @@ const bareApi = {
22
27
  const SeamlyTestCore = ({ state, children }) => {
23
28
  const liveMsgRef = useRef(() => {})
24
29
  const eventBusRef = useRef({ emit: () => {} })
30
+
25
31
  const store = useMemo(() => {
26
- const { translations: translationsSlice, ...restState } = state || {}
27
- return createReduxStore(
28
- {
32
+ const {
33
+ translations: translationsSlice,
34
+ interrupt: interruptSlice,
35
+ config: configSlice,
36
+ ...restState
37
+ } = state || {}
38
+ const newStore = createReduxStore({
39
+ reducers: {
29
40
  state: stateReducer,
41
+ [String(configReducer)]: configReducer,
30
42
  [String(formReducer)]: formReducer,
31
43
  [String(translationsReducer)]: translationsReducer,
32
44
  [String(i18nReducer)]: i18nReducer,
45
+ [String(interruptReducer)]: interruptReducer,
33
46
  },
34
- {
47
+ initialState: {
35
48
  state: restState,
36
- translations: translationsSlice,
49
+ [String(translationsReducer)]: translationsSlice,
50
+ [String(interruptReducer)]: interruptSlice,
37
51
  },
38
- )
52
+ })
53
+ newStore.dispatch(ConfigActions.initialize(configSlice || {}))
54
+ return newStore
39
55
  }, [state])
40
56
 
41
57
  return (
@@ -42,7 +42,7 @@ export function addTranslationData(event) {
42
42
  ? 'NL - ' + event.payload.body.buttonText
43
43
  : undefined,
44
44
  choices: event.payload.body.choices
45
- ? event.payload.body.choices.map(choice => ({
45
+ ? event.payload.body.choices.map((choice) => ({
46
46
  ...choice,
47
47
  text: 'NL - ' + choice.text,
48
48
  }))
@@ -1,4 +1,10 @@
1
- import { visibilityStates, randomId } from '@seamly/web-ui'
1
+ import {
2
+ visibilityStates,
3
+ randomId,
4
+ SeamlyOfflineError,
5
+ SeamlyGeneralError,
6
+ SeamlyConfigurationError,
7
+ } from '@seamly/web-ui'
2
8
  import { addTranslationData } from './state-helpers'
3
9
 
4
10
  const baseState = {
@@ -8,6 +14,7 @@ const baseState = {
8
14
  accountConfig: {},
9
15
  hideOnNoUserResponse: false,
10
16
  showFaq: false,
17
+ showDisclaimer: false,
11
18
  },
12
19
  initialState: {},
13
20
  unreadEvents: 0,
@@ -27,14 +34,12 @@ const baseState = {
27
34
  subTitle: 'Test subtitle',
28
35
  },
29
36
  interrupt: {
30
- hasInterrupt: false,
31
- meta: {},
37
+ error: undefined,
32
38
  },
33
39
  historyLoaded: false,
34
40
  skiplinkTargetId: randomId(),
35
41
  optionsButtonId: randomId(),
36
42
  cobrowseContainerId: randomId(),
37
- showDisclaimer: false,
38
43
  serviceData: {},
39
44
  options: {
40
45
  features: {},
@@ -117,8 +122,7 @@ const newTranslationDividerStart = {
117
122
  body: {
118
123
  language: 'Nederlands',
119
124
  subtype: 'new_translation',
120
- text:
121
- '[NL] Automatic translation to Dutch started. Please note that automatic translations may contain errors.',
125
+ text: '[NL] Automatic translation to Dutch started. Please note that automatic translations may contain errors.',
122
126
  translationEnabled: true,
123
127
  translationLocale: 'nl',
124
128
  },
@@ -304,7 +308,7 @@ const ctaMessage = {
304
308
  type: 'message',
305
309
  payload: {
306
310
  body: {
307
- description: 'More info about our products?',
311
+ description: 'Thanks for chatting!\n\nMore info about our **products**?',
308
312
  buttonLink: 'https://seamly.ai',
309
313
  buttonText: 'View website',
310
314
  buttonNewTab: true,
@@ -329,8 +333,7 @@ const longTextMessage = {
329
333
  type: 'message',
330
334
  payload: {
331
335
  body: {
332
- text:
333
- 'What do you want to do? This is a really long message from a bot that has a lot to say about a lot of things. Currently I am contemplating my own bot existence and constantly asking myself who I am. What do you want to do? This is a really long message from a bot that has a lot to say about a lot of things. Currently I am contemplating my own bot existence and constantly asking myself who I am. What do you want to do? This is a really long message from a bot that has a lot to say about a lot of things. Currently I am contemplating my own bot existence and constantly asking myself who I am.',
336
+ text: 'What do you want to do? This is a really long message from a bot that has a lot to say about a lot of things. Currently I am contemplating my own bot existence and constantly asking myself who I am. What do you want to do? This is a really long message from a bot that has a lot to say about a lot of things. Currently I am contemplating my own bot existence and constantly asking myself who I am. What do you want to do? This is a really long message from a bot that has a lot to say about a lot of things. Currently I am contemplating my own bot existence and constantly asking myself who I am.',
334
337
  type: 'text',
335
338
  variables: {},
336
339
  },
@@ -422,8 +425,7 @@ const textMessageWithLongLink = {
422
425
  variables: {
423
426
  link_1: {
424
427
  id: '1',
425
- name:
426
- 'click me click me please click me yoohoooo please please click me here I am click me now what are you waiting for click me now now now now now click meeeeeeeeeeeeee',
428
+ name: 'click me click me please click me yoohoooo please please click me here I am click me now what are you waiting for click me now now now now now click meeeeeeeeeeeeee',
427
429
  newTab: false,
428
430
  type: 'link',
429
431
  url: 'https://google.com',
@@ -450,8 +452,7 @@ const textMesageWithBullets = {
450
452
  type: 'message',
451
453
  payload: {
452
454
  body: {
453
- text:
454
- '<ul>\n<li>Bullets</li>\n<li>bullets</li>\n<li>bullets</li>\n</ul>\n',
455
+ text: '<ul>\n<li>Bullets</li>\n<li>bullets</li>\n<li>bullets</li>\n</ul>\n',
455
456
  type: 'text',
456
457
  variables: {},
457
458
  },
@@ -553,8 +554,7 @@ const choicePromptMessage = {
553
554
  choices: [
554
555
  {
555
556
  id: '703db9b3-2157-4d4a-9f9b-c382c6a10531',
556
- text:
557
- 'Dialog (this is intentionally made longer for testing purposes)',
557
+ text: 'Dialog (this is intentionally made longer for testing purposes)',
558
558
  type: 'choice',
559
559
  },
560
560
  {
@@ -579,7 +579,8 @@ const choicePromptMessage = {
579
579
  },
580
580
  {
581
581
  id: '9fd9a28f-462d-4f07-9661-606dbe3b9466',
582
- text: 'Content',
582
+ category: 'secondary',
583
+ text: 'Something else',
583
584
  type: 'choice',
584
585
  },
585
586
  ],
@@ -630,8 +631,7 @@ const userMessageLong = {
630
631
  type: 'message',
631
632
  payload: {
632
633
  body: {
633
- text:
634
- 'This is what the user typed. And sometimes the user has quite a lot to say and then we get longer lines that need to wrap well and not break the styling so here goes with just such a line right here!!',
634
+ text: 'This is what the user typed. And sometimes the user has quite a lot to say and then we get longer lines that need to wrap well and not break the styling so here goes with just such a line right here!!',
635
635
  type: 'text',
636
636
  variables: {},
637
637
  },
@@ -652,7 +652,6 @@ const fileDownloadPayload = {
652
652
  meta: {},
653
653
  body: {
654
654
  contentType: 'image/jpg',
655
- deleteAt: 1000 * (Date.now() + 3600000),
656
655
  filename: 'placeholder.jpg',
657
656
  filesize: 991078,
658
657
  url: 'https://via.placeholder.com/150',
@@ -675,7 +674,6 @@ const deletedFileDownloadAgentMessage = {
675
674
  ...fileDownloadAgentMessage.payload,
676
675
  body: {
677
676
  ...fileDownloadAgentMessage.payload.body,
678
- deleteAt: 1000 * (Date.now() - 3600000),
679
677
  },
680
678
  id: randomId(),
681
679
  },
@@ -691,18 +689,6 @@ const fileDownloadUserMessage = {
691
689
  },
692
690
  }
693
691
 
694
- const deletedFileDownloadUserMessage = {
695
- ...fileDownloadUserMessage,
696
- payload: {
697
- id: randomId(),
698
- ...fileDownloadUserMessage.payload,
699
- body: {
700
- ...fileDownloadUserMessage.payload.body,
701
- deleteAt: 1000 * (Date.now() - 3600000),
702
- },
703
- },
704
- }
705
-
706
692
  const emptyUrlFileDownloadUserMessage = {
707
693
  ...fileDownloadUserMessage,
708
694
  payload: {
@@ -807,7 +793,8 @@ const cardAskText = {
807
793
  type: 'ask',
808
794
  },
809
795
  buttonText: 'Ask about pizzas!',
810
- description: 'Pizza Margherita is a typical Neapolitan pizza.',
796
+ description:
797
+ 'Pizza Margherita is a **typical Neapolitan pizza**.\n\nIt is made with San Marzano tomatoes, mozzarella cheese, fresh basil, salt, and extra-virgin olive oil.',
811
798
  image:
812
799
  'https://via.placeholder.com/400x200/dee3e5/6a7f8c?text=Margherita',
813
800
  title: 'Pizza Margherita',
@@ -826,7 +813,7 @@ const cardNavigate = {
826
813
  type: 'navigate',
827
814
  },
828
815
  buttonText: 'Order now!',
829
- description: 'Pizza Margherita is a typical Neapolitan pizza.',
816
+ description: 'Pizza Margherita is a **typical Neapolitan pizza**.',
830
817
  image:
831
818
  'https://via.placeholder.com/400x200/dee3e5/6a7f8c?text=Margherita',
832
819
  title: 'Pizza Margherita',
@@ -891,7 +878,6 @@ const standardState = {
891
878
  textMessageWithLongLink,
892
879
  imageMessageWithLightbox,
893
880
  fileDownloadUserMessage,
894
- deletedFileDownloadUserMessage,
895
881
  emptyUrlFileDownloadUserMessage,
896
882
  textMesageWithBullets,
897
883
  choicePromptMessage,
@@ -908,7 +894,6 @@ const standardState = {
908
894
  userMessage,
909
895
  userMessageLong,
910
896
  fileDownloadUserMessage,
911
- deletedFileDownloadUserMessage,
912
897
  emptyUrlFileDownloadUserMessage,
913
898
  ],
914
899
  },
@@ -1114,8 +1099,7 @@ const standardState = {
1114
1099
  occurredAt: Date.now() * 1000,
1115
1100
  id: randomId(),
1116
1101
  body: {
1117
- text:
1118
- 'Above me should be a time indicator showing me the dialog continues today',
1102
+ text: 'Above me should be a time indicator showing me the dialog continues today',
1119
1103
  type: 'text',
1120
1104
  variables: {},
1121
1105
  },
@@ -1165,14 +1149,7 @@ const standardState = {
1165
1149
  description: '',
1166
1150
  ...baseState,
1167
1151
  interrupt: {
1168
- hasInterrupt: true,
1169
- meta: {
1170
- title: 'Connection issues',
1171
- message:
1172
- 'There might be a problem with your or our network connection. The chat session should resume as soon the connection is available again.',
1173
- srText:
1174
- 'The chat has connection issues. There might be a problem with your or our network connection. The chat session should resume as soon as the connection is available again.',
1175
- },
1152
+ error: new SeamlyOfflineError(),
1176
1153
  },
1177
1154
  },
1178
1155
  generalErrorInterrupt: {
@@ -1182,15 +1159,7 @@ const standardState = {
1182
1159
  description: '',
1183
1160
  ...baseState,
1184
1161
  interrupt: {
1185
- hasInterrupt: true,
1186
- meta: {
1187
- title: 'Something went wrong',
1188
- message: 'Do you want to start a new chat session?',
1189
- buttonText: 'Restart chat',
1190
- action: 'reset',
1191
- srText:
1192
- 'Something went wrong with the chat session. You can restart the chat.',
1193
- },
1162
+ error: new SeamlyGeneralError(),
1194
1163
  },
1195
1164
  },
1196
1165
  configErrorInterrupt: {
@@ -1200,13 +1169,7 @@ const standardState = {
1200
1169
  description: '',
1201
1170
  ...baseState,
1202
1171
  interrupt: {
1203
- hasInterrupt: true,
1204
- meta: {
1205
- title: 'Chat configuration error.',
1206
- message: 'We are sorry this happened, please retry at a later time.',
1207
- srText:
1208
- 'A chat configuration error occurred. Our apologies, please retry at a later time.',
1209
- },
1172
+ error: new SeamlyConfigurationError(),
1210
1173
  },
1211
1174
  },
1212
1175
  privacyDisclaimer: {
@@ -1218,7 +1181,6 @@ const standardState = {
1218
1181
  ...baseState.config,
1219
1182
  showDisclaimer: true,
1220
1183
  },
1221
- showDisclaimer: true,
1222
1184
  },
1223
1185
  cobrowserBar: {
1224
1186
  category: categoryKeys.features,
@@ -1756,7 +1718,6 @@ const standardState = {
1756
1718
  textMessageWithLongLink,
1757
1719
  imageMessageWithLightbox,
1758
1720
  fileDownloadUserMessage,
1759
- deletedFileDownloadUserMessage,
1760
1721
  emptyUrlFileDownloadUserMessage,
1761
1722
  textMesageWithBullets,
1762
1723
  choicePromptMessage,
@@ -1794,9 +1755,8 @@ const buildStandardState = (layoutModes, customComponentEventBodies = []) => {
1794
1755
  }, {}),
1795
1756
  }
1796
1757
  return Object.keys(intermediateState).reduce((acc, key) => {
1797
- const { headingText, category, description, ...rest } = intermediateState[
1798
- key
1799
- ]
1758
+ const { headingText, category, description, ...rest } =
1759
+ intermediateState[key]
1800
1760
  return {
1801
1761
  ...acc,
1802
1762
  [key]: {
@@ -11,7 +11,7 @@ class SeamlyStyleGuideInstance extends Engine {
11
11
 
12
12
  render() {
13
13
  const restComponents = {
14
- ...(this.customComponents || {}),
14
+ ...(this.config.customComponents || {}),
15
15
  view: undefined,
16
16
  }
17
17
 
@@ -1,9 +1,9 @@
1
1
  import View from './layout/view'
2
2
  import SeamlyCore from './core/seamly-core'
3
3
 
4
- const ChatApp = ({ config, eventBus }) => {
4
+ const ChatApp = (props) => {
5
5
  return (
6
- <SeamlyCore config={config} eventBus={eventBus}>
6
+ <SeamlyCore {...props}>
7
7
  <View />
8
8
  </SeamlyCore>
9
9
  )
@@ -1,5 +1,5 @@
1
1
  import { useMemo } from 'preact/hooks'
2
- import { useSeamlyConfig } from '../../hooks/seamly-hooks'
2
+ import { useConfig } from '../../../domains/config'
3
3
  import ComponentContext from './component-context'
4
4
  import ChoicePrompt from './event/choice-prompt'
5
5
  import Text from './event/text'
@@ -37,7 +37,7 @@ const eventTypeMapping = {
37
37
  }
38
38
 
39
39
  const ComponentFilter = ({ children }) => {
40
- const { customComponents } = useSeamlyConfig()
40
+ const { customComponents } = useConfig()
41
41
 
42
42
  const resolvedComponents = useMemo(() => {
43
43
  // Calculates a combined component mapping object based on the defaults with
@@ -33,7 +33,7 @@ const Conversation = () => {
33
33
 
34
34
  const renderEvents = () => {
35
35
  let prevParticipant = null
36
- return events.map(event => {
36
+ return events.map((event) => {
37
37
  const { type, payload } = event
38
38
  const { participant, fromClient } = payload
39
39
  let participantChanged = false
@@ -56,7 +56,7 @@ const Conversation = () => {
56
56
  })
57
57
  }
58
58
 
59
- const onClickHandler = e => {
59
+ const onClickHandler = (e) => {
60
60
  e.preventDefault()
61
61
  focusSkiplinkTarget()
62
62
  }
@@ -1,5 +1,6 @@
1
- import { useCallback, useMemo } from 'preact/hooks'
1
+ import { useCallback, useEffect, useMemo, useRef } from 'preact/hooks'
2
2
  import { className } from '../../../../lib/css'
3
+ import parseBody from '../../../../lib/parse-body'
3
4
  import { useGeneratedId, useSeamlyCommands } from '../../../hooks/seamly-hooks'
4
5
  import { cardTypes, actionTypes } from '../../../utils/seamly-utils'
5
6
 
@@ -8,11 +9,15 @@ const CardComponent = ({
8
9
  action,
9
10
  buttonText,
10
11
  description,
12
+ hasFocus,
11
13
  image,
12
14
  title,
15
+ isCarouselItem,
13
16
  }) => {
17
+ const cardRef = useRef(null)
14
18
  const { sendMessage, sendAction, emitEvent } = useSeamlyCommands()
15
19
  const descriptionId = useGeneratedId()
20
+ const isMounted = useRef()
16
21
 
17
22
  const CardActionComponent =
18
23
  action.type === cardTypes.navigate ? 'a' : 'button'
@@ -52,15 +57,35 @@ const CardComponent = ({
52
57
  [action, handleClick, emitCardEvent],
53
58
  )
54
59
 
60
+ useEffect(() => {
61
+ if (isCarouselItem) {
62
+ if (hasFocus && isMounted.current) {
63
+ window.requestAnimationFrame(() => cardRef.current.focus())
64
+ } else {
65
+ cardRef.current.blur()
66
+ }
67
+ }
68
+ isMounted.current = true
69
+ }, [hasFocus, isCarouselItem])
70
+
55
71
  return (
56
- <div className={className('card__wrapper')} id={id}>
57
- <img className={className('card__image')} src={image} alt={description} />
72
+ <div
73
+ className={className('card__wrapper')}
74
+ id={id}
75
+ tabIndex="-1" // set tabIndex of -1 so card can be focussed
76
+ ref={cardRef}
77
+ >
78
+ <img className={className('card__image')} src={image} alt="" />
58
79
  <div className={className('card__content')} id={id}>
59
80
  {title && <h2 className={className('card__title')}>{title}</h2>}
60
81
  {description && (
61
- <p className={className('card__description')}>{description}</p>
82
+ <div
83
+ className={className('card__description')}
84
+ dangerouslySetInnerHTML={{ __html: parseBody(description) }}
85
+ />
62
86
  )}
63
87
  <CardActionComponent
88
+ tabIndex={isCarouselItem && !hasFocus ? '-1' : undefined} // disable to prevent tabbing through cards
64
89
  className={className('button', 'button--primary')}
65
90
  aria-describedby={descriptionId}
66
91
  {...actionProps}
@@ -10,7 +10,7 @@ export default function CarouselPagination({
10
10
  }) {
11
11
  const itemCount = items.length
12
12
  const handlePaginate = useCallback(
13
- event => {
13
+ (event) => {
14
14
  const slideIndex = Number(event.target.dataset.item || '0')
15
15
  const nextIndex = Math.min(itemCount - 1, Math.max(0, slideIndex))
16
16
  if (nextIndex !== currentIndex) {
@@ -27,7 +27,7 @@ export default function CarouselPagination({
27
27
  const isActive = currentIndex === idx
28
28
  return (
29
29
  <li
30
- key={getItemKey(item, idx)}
30
+ key={getItemKey(item, idx, 'pagination-item-')}
31
31
  className={className(
32
32
  'carousel-pagination__item',
33
33
  isActive ? 'is-active' : undefined,
@@ -6,8 +6,8 @@ import CarouselPagination from './components/pagination'
6
6
  import CarouselControls from './components/controls'
7
7
  import CarouselMessageSlide from '../carousel-message/components/slide'
8
8
 
9
- const defaultGetItemKey = (item, idx) => idx
10
- const defaultGetItemLabel = item => item.label
9
+ const defaultGetItemKey = (item, idx, prefix) => `${prefix}${idx}`
10
+ const defaultGetItemLabel = (item) => item.label
11
11
 
12
12
  export default function CarouselComponent({
13
13
  currentIndex: originalIndex,
@@ -66,7 +66,7 @@ export default function CarouselComponent({
66
66
  const isActive = currentIndex === idx
67
67
  return (
68
68
  <div
69
- key={idx}
69
+ key={getItemKey(item, idx, 'item-')}
70
70
  ref={slideRefs.current[idx]}
71
71
  className={className(
72
72
  'carousel__slide',
@@ -78,6 +78,7 @@ export default function CarouselComponent({
78
78
  item={item}
79
79
  items={items}
80
80
  currentIndex={currentIndex}
81
+ isActive={isActive}
81
82
  />
82
83
  </div>
83
84
  )
@@ -6,6 +6,7 @@ export default function CarouselMessageSlide({
6
6
  item: slide,
7
7
  items,
8
8
  currentIndex,
9
+ isActive,
9
10
  }) {
10
11
  const { t } = useI18n()
11
12
 
@@ -19,7 +20,7 @@ export default function CarouselMessageSlide({
19
20
  total: items.length,
20
21
  })}
21
22
  >
22
- <CardComponent {...slide} />
23
+ <CardComponent {...slide} isCarouselItem={true} hasFocus={isActive} />
23
24
  </div>
24
25
  )
25
26
  }
@@ -2,8 +2,8 @@ import MessageContainer from '../../message-container'
2
2
  import CarouselComponent from '../carousel-component'
3
3
  import CarouselMessageSlide from './components/slide'
4
4
 
5
- const getItemKey = item => item.title
6
- const getItemLabel = item => item.title
5
+ const getItemKey = (item, idx, prefix = '') => `${prefix}${item.title}:${idx}`
6
+ const getItemLabel = (item) => item.title
7
7
  const CarouselMessage = ({ event }) => {
8
8
  const slides = event.payload.body.cards
9
9