@seamly/web-ui 18.2.0 → 19.0.0-beta.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/build/dist/lib/index.debug.js +598 -136
- package/build/dist/lib/index.debug.min.js +1 -1
- package/build/dist/lib/index.debug.min.js.LICENSE.txt +190 -22
- package/build/dist/lib/index.js +4745 -4468
- package/build/dist/lib/index.min.js +1 -1
- package/build/dist/lib/index.min.js.LICENSE.txt +1 -1
- package/build/dist/lib/standalone.js +4839 -4465
- package/build/dist/lib/standalone.min.js +1 -1
- package/build/dist/lib/standalone.min.js.LICENSE.txt +1 -1
- package/build/dist/lib/style-guide.js +1770 -980
- package/build/dist/lib/style-guide.min.js +1 -1
- package/build/dist/lib/styles.css +1 -1
- package/build/dist/lib/utils.js +0 -1
- package/build/dist/lib/utils.min.js +1 -1
- package/package.json +29 -29
- package/src/javascripts/api/index.js +33 -48
- package/src/javascripts/api/producer.js +9 -12
- package/src/javascripts/config.js +9 -11
- package/src/javascripts/domains/app/actions.js +43 -0
- package/src/javascripts/domains/app/hooks.js +6 -0
- package/src/javascripts/domains/app/index.js +6 -0
- package/src/javascripts/domains/app/reducer.js +16 -0
- package/src/javascripts/domains/app/selectors.js +8 -0
- package/src/javascripts/domains/app/utils.js +4 -0
- package/src/javascripts/domains/config/actions.js +4 -0
- package/src/javascripts/domains/config/hooks.js +6 -0
- package/src/javascripts/domains/config/index.js +8 -0
- package/src/javascripts/domains/config/middleware.js +22 -0
- package/src/javascripts/domains/config/reducer.js +63 -0
- package/src/javascripts/domains/config/selectors.js +23 -0
- package/src/javascripts/domains/config/utils.js +4 -0
- package/src/javascripts/domains/forms/actions.js +2 -4
- package/src/javascripts/domains/forms/hooks.js +10 -14
- package/src/javascripts/domains/forms/provider.js +4 -6
- package/src/javascripts/domains/forms/reducer.js +1 -2
- package/src/javascripts/domains/forms/selectors.js +4 -4
- package/src/javascripts/domains/forms/utils.js +5 -0
- package/src/javascripts/domains/i18n/actions.js +35 -0
- package/src/javascripts/domains/i18n/hooks.js +38 -0
- package/src/javascripts/domains/i18n/index.js +5 -80
- package/src/javascripts/domains/i18n/reducer.js +58 -0
- package/src/javascripts/domains/i18n/selectors.js +15 -0
- package/src/javascripts/domains/i18n/utils.js +9 -0
- package/src/javascripts/domains/interrupt/actions.js +4 -0
- package/src/javascripts/domains/interrupt/hooks.js +29 -0
- package/src/javascripts/domains/interrupt/index.js +9 -0
- package/src/javascripts/domains/interrupt/middleware.js +30 -0
- package/src/javascripts/domains/interrupt/reducer.js +21 -0
- package/src/javascripts/domains/interrupt/selectors.js +6 -0
- package/src/javascripts/domains/interrupt/utils.js +4 -0
- package/src/javascripts/domains/options/index.js +1 -0
- package/src/javascripts/domains/options/middleware.js +35 -0
- package/src/javascripts/domains/redux/create-redux-store.js +14 -6
- package/src/javascripts/domains/redux/hooks.js +3 -2
- package/src/javascripts/domains/redux/index.js +2 -1
- package/src/javascripts/domains/redux/provider.js +5 -0
- package/src/javascripts/domains/store/index.js +44 -0
- package/src/javascripts/{ui → domains}/store/state-reducer.js +4 -7
- package/src/javascripts/domains/translations/actions.js +4 -6
- package/src/javascripts/domains/translations/components/chat-status.js +7 -13
- package/src/javascripts/domains/translations/components/options-button.js +3 -3
- package/src/javascripts/domains/translations/components/options-dialog/form.js +12 -7
- package/src/javascripts/domains/translations/components/options-dialog/index.js +2 -5
- package/src/javascripts/domains/translations/hooks.js +1 -1
- package/src/javascripts/domains/translations/index.js +1 -0
- package/src/javascripts/domains/translations/middleware.js +43 -0
- package/src/javascripts/domains/translations/reducer.js +4 -11
- package/src/javascripts/domains/translations/selectors.js +3 -3
- package/src/javascripts/domains/translations/utils.js +4 -0
- package/src/javascripts/index.js +20 -5
- package/src/javascripts/lib/css.js +5 -5
- package/src/javascripts/lib/engine/index.js +39 -11
- package/src/javascripts/lib/external-api/index.js +6 -6
- package/src/javascripts/lib/mutex.js +30 -0
- package/src/javascripts/lib/parse-body.js +1 -1
- package/src/javascripts/lib/redux-helpers/index.js +25 -8
- package/src/javascripts/lib/split-url-params.js +2 -2
- package/src/javascripts/lib/store/providers/app-storage.js +1 -1
- package/src/javascripts/lib/store/providers/cookie-storage.js +1 -1
- package/src/javascripts/package/utils.js +0 -1
- package/src/javascripts/style-guide/components/app.js +12 -14
- package/src/javascripts/style-guide/components/links.js +6 -6
- package/src/javascripts/style-guide/components/static-core.js +32 -10
- package/src/javascripts/style-guide/state-helpers/index.js +1 -1
- package/src/javascripts/style-guide/states.js +29 -71
- package/src/javascripts/style-guide/style-guide-engine.js +13 -12
- package/src/javascripts/ui/components/chat-app.js +2 -2
- package/src/javascripts/ui/components/conversation/component-filter.js +2 -2
- package/src/javascripts/ui/components/conversation/conversation.js +2 -2
- package/src/javascripts/ui/components/conversation/event/card-component.js +24 -3
- package/src/javascripts/ui/components/conversation/event/carousel-component/components/pagination.js +2 -2
- package/src/javascripts/ui/components/conversation/event/carousel-component/index.js +4 -3
- package/src/javascripts/ui/components/conversation/event/carousel-message/components/slide.js +2 -1
- package/src/javascripts/ui/components/conversation/event/carousel-message/index.js +2 -2
- package/src/javascripts/ui/components/conversation/event/choice-prompt.js +5 -5
- package/src/javascripts/ui/components/conversation/event/divider/variants/new-translation.js +2 -2
- package/src/javascripts/ui/components/conversation/event/event-participant.js +3 -5
- package/src/javascripts/ui/components/conversation/event/hooks/use-event-link-click-handler.js +2 -2
- package/src/javascripts/ui/components/conversation/event/hooks/use-formatted-date.js +3 -3
- package/src/javascripts/ui/components/conversation/event/hooks/use-text-rendering.js +3 -3
- package/src/javascripts/ui/components/conversation/event/participant.js +2 -2
- package/src/javascripts/ui/components/conversation/event/upload.js +12 -27
- package/src/javascripts/ui/components/conversation/message-container.js +4 -6
- package/src/javascripts/ui/components/core/seamly-activity-monitor.js +4 -5
- package/src/javascripts/ui/components/core/seamly-core.js +6 -7
- package/src/javascripts/ui/components/core/seamly-event-subscriber.js +18 -17
- package/src/javascripts/ui/components/core/seamly-file-upload.js +5 -6
- package/src/javascripts/ui/components/core/seamly-idle-detach-counter.js +2 -6
- package/src/javascripts/ui/components/core/seamly-initializer.js +7 -60
- package/src/javascripts/ui/components/core/seamly-instance-functions-loader.js +10 -16
- package/src/javascripts/ui/components/core/seamly-live-region.js +1 -1
- package/src/javascripts/ui/components/core/seamly-new-notifications.js +5 -6
- package/src/javascripts/ui/components/core/seamly-read-state.js +8 -6
- package/src/javascripts/ui/components/entry/entry-container.js +7 -10
- package/src/javascripts/ui/components/entry/text-entry/hooks.js +6 -4
- package/src/javascripts/ui/components/entry/text-entry/text-entry-form.js +10 -3
- package/src/javascripts/ui/components/entry/toggle-button.js +24 -10
- package/src/javascripts/ui/components/entry/upload/file-upload-form.js +6 -3
- package/src/javascripts/ui/components/entry/upload/index.js +11 -13
- package/src/javascripts/ui/components/faq/faq.js +6 -6
- package/src/javascripts/ui/components/form-controls/error.js +22 -0
- package/src/javascripts/ui/components/form-controls/file-input.js +3 -9
- package/src/javascripts/ui/components/form-controls/select.js +1 -1
- package/src/javascripts/ui/components/form-controls/wrapper.js +2 -9
- package/src/javascripts/ui/components/layout/agent-info.js +4 -4
- package/src/javascripts/ui/components/layout/app-frame.js +15 -12
- package/src/javascripts/ui/components/layout/chat-frame.js +3 -5
- package/src/javascripts/ui/components/layout/header.js +4 -18
- package/src/javascripts/ui/components/layout/interrupt.js +6 -2
- package/src/javascripts/ui/components/layout/privacy-disclaimer.js +2 -2
- package/src/javascripts/ui/components/options/cobrowsing.js +3 -7
- package/src/javascripts/ui/components/options/options-button.js +9 -13
- package/src/javascripts/ui/components/options/options-frame.js +1 -1
- package/src/javascripts/ui/components/options/transcript/index.js +2 -2
- package/src/javascripts/ui/components/options/transcript/transcript-form.js +1 -1
- package/src/javascripts/ui/components/warnings/cobrowsing-active-frame.js +3 -6
- package/src/javascripts/ui/components/warnings/idle-detach-warning.js +2 -6
- package/src/javascripts/ui/components/warnings/resume-conversation-prompt.js +1 -1
- package/src/javascripts/ui/components/widgets/in-out-transition.js +2 -2
- package/src/javascripts/ui/components/widgets/lightbox.js +4 -4
- package/src/javascripts/ui/components/widgets/modal.js +3 -3
- package/src/javascripts/ui/components/widgets/upload-progress.js +3 -14
- package/src/javascripts/ui/hooks/component-helper-hooks.js +4 -15
- package/src/javascripts/ui/hooks/file-upload-hooks.js +3 -3
- package/src/javascripts/ui/hooks/focus-helper-hooks.js +4 -4
- package/src/javascripts/ui/hooks/live-region-hooks.js +2 -2
- package/src/javascripts/ui/hooks/seamly-api-hooks.js +0 -6
- package/src/javascripts/ui/hooks/seamly-entry-hooks.js +22 -25
- package/src/javascripts/ui/hooks/seamly-hooks.js +3 -10
- package/src/javascripts/ui/hooks/seamly-option-hooks.js +4 -4
- package/src/javascripts/ui/hooks/seamly-state-hooks.js +8 -16
- package/src/javascripts/ui/hooks/use-event-component-mapping.js +1 -1
- package/src/javascripts/ui/hooks/use-seamly-chat.js +1 -0
- package/src/javascripts/ui/hooks/use-seamly-commands.js +31 -54
- package/src/javascripts/ui/hooks/use-seamly-idle-detach-countdown.js +3 -3
- package/src/javascripts/ui/hooks/use-seamly-stored-visibility.js +3 -3
- package/src/javascripts/ui/hooks/use-seamly-visibility.js +6 -8
- package/src/javascripts/ui/hooks/use-single-file-upload.js +4 -1
- package/src/javascripts/ui/hooks/utility-hooks.js +2 -2
- package/src/javascripts/ui/utils/form-utils.js +3 -3
- package/src/javascripts/ui/utils/general-utils.js +21 -22
- package/src/javascripts/ui/utils/seamly-utils.js +15 -83
- package/src/javascripts/ui/utils/validations.js +10 -7
- package/src/stylesheets/1-settings/_config.scss +2 -1
- package/src/stylesheets/3-app/_app.scss +3 -4
- package/src/stylesheets/5-components/_card.scss +0 -1
- package/src/stylesheets/5-components/_faq.scss +3 -8
- package/src/stylesheets/5-components/_message.scss +10 -0
- package/src/stylesheets/5-components/_modal.scss +3 -3
- package/src/stylesheets/5-components/_options.scss +3 -2
- package/webpack/config.common.js +3 -3
- package/webpack/config.package.js +4 -22
- package/webpack/config.site.js +8 -6
- package/webpack/defaults.js +0 -3
- package/CHANGELOG.md +0 -561
- package/build/dist/translations/de-informal.js +0 -275
- package/build/dist/translations/de-informal.min.js +0 -1
- package/build/dist/translations/en.js +0 -275
- package/build/dist/translations/en.min.js +0 -1
- package/build/dist/translations/es-informal.js +0 -281
- package/build/dist/translations/es-informal.min.js +0 -1
- package/build/dist/translations/nl-formal.js +0 -275
- package/build/dist/translations/nl-formal.min.js +0 -1
- package/build/dist/translations/nl-informal.js +0 -275
- package/build/dist/translations/nl-informal.min.js +0 -1
- package/src/javascripts/lib/i18n.js +0 -46
- package/src/javascripts/ui/components/core/seamly-api.js +0 -44
- package/src/javascripts/ui/hooks/use-seamly-interrupt.js +0 -62
- package/src/javascripts/ui/store/index.js +0 -37
- package/translations/de-informal.js +0 -237
- package/translations/en.js +0 -234
- package/translations/es-informal.js +0 -243
- package/translations/nl-formal.js +0 -230
- package/translations/nl-informal.js +0 -230
|
@@ -33,18 +33,25 @@ export default function TextEntryForm({ controlName, skipLinkId }) {
|
|
|
33
33
|
|
|
34
34
|
const handleFocus = useCallback(() => {
|
|
35
35
|
if (reachedCharacterWarning) {
|
|
36
|
-
sendAssertive(t('input.srCharacterLimitText', remainingChars))
|
|
36
|
+
sendAssertive(t('input.srCharacterLimitText', { limit: remainingChars }))
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
emitEvent('ui.inputFocus')
|
|
40
40
|
}, [t, sendAssertive, reachedCharacterWarning, remainingChars, emitEvent])
|
|
41
41
|
const placeholder = useMemo(
|
|
42
42
|
() =>
|
|
43
|
-
t('input.inputPlaceholder',
|
|
43
|
+
t('input.inputPlaceholder', {
|
|
44
|
+
hasLimit: hasCharacterLimit,
|
|
45
|
+
limit: hasCharacterLimit ? characterLimit : null,
|
|
46
|
+
}),
|
|
44
47
|
[t, hasCharacterLimit, characterLimit],
|
|
45
48
|
)
|
|
46
49
|
const labelText = useMemo(
|
|
47
|
-
() =>
|
|
50
|
+
() =>
|
|
51
|
+
t('input.inputLabel', {
|
|
52
|
+
hasLimit: hasCharacterLimit,
|
|
53
|
+
limit: hasCharacterLimit ? characterLimit : null,
|
|
54
|
+
}),
|
|
48
55
|
[t, hasCharacterLimit, characterLimit],
|
|
49
56
|
)
|
|
50
57
|
// When the input holds a value, the component should be blocked from switching
|
|
@@ -7,9 +7,10 @@ import {
|
|
|
7
7
|
useFocusIfSeamlyContainedFocus,
|
|
8
8
|
useSeamlyCurrentAgent,
|
|
9
9
|
useSeamlyHeaderData,
|
|
10
|
-
useSeamlyInterrupt,
|
|
11
10
|
useGeneratedId,
|
|
11
|
+
useSeamlyStateContext,
|
|
12
12
|
} from '../../hooks/seamly-hooks'
|
|
13
|
+
import { useInterrupt } from '../../../domains/interrupt'
|
|
13
14
|
|
|
14
15
|
const ToggleButton = ({ onOpenChat }) => {
|
|
15
16
|
const { t } = useI18n()
|
|
@@ -17,11 +18,13 @@ const ToggleButton = ({ onOpenChat }) => {
|
|
|
17
18
|
const { isOpen } = useSeamlyVisibility()
|
|
18
19
|
const prevIsOpen = useRef(null)
|
|
19
20
|
const buttonRef = useRef(null)
|
|
21
|
+
const lastEventRef = useRef()
|
|
20
22
|
const focusSkiplinkTarget = useSkiplinkTargetFocusing()
|
|
21
23
|
const focusIfContained = useFocusIfSeamlyContainedFocus()
|
|
22
24
|
const currentAgent = useSeamlyCurrentAgent()
|
|
23
25
|
const agentSubtitle = useSeamlyHeaderData().subTitle
|
|
24
|
-
const { hasInterrupt } =
|
|
26
|
+
const { hasInterrupt } = useInterrupt()
|
|
27
|
+
const { headerCollapseButtonId } = useSeamlyStateContext()
|
|
25
28
|
|
|
26
29
|
const showAgentInfo = currentAgent && !hasInterrupt
|
|
27
30
|
|
|
@@ -35,13 +38,23 @@ const ToggleButton = ({ onOpenChat }) => {
|
|
|
35
38
|
prevIsOpen.current = isOpen
|
|
36
39
|
}, [isOpen, focusIfContained])
|
|
37
40
|
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
const handleMouseUp = () => {
|
|
42
|
+
lastEventRef.current = 'mouse'
|
|
43
|
+
}
|
|
44
|
+
const handleKeyUp = () => {
|
|
45
|
+
lastEventRef.current = 'key'
|
|
46
|
+
}
|
|
47
|
+
const handleClick = () => {
|
|
48
|
+
onOpenChat()
|
|
49
|
+
if (lastEventRef.current === 'mouse') {
|
|
50
|
+
// Sets focus on the input when opening through mouse interaction.
|
|
51
|
+
// This avoids focus hijacking for keyboard users.
|
|
52
|
+
// TODO: function is executed before the component is rendered, needs to be fixed.
|
|
53
|
+
focusSkiplinkTarget()
|
|
54
|
+
} else if (lastEventRef.current === 'key') {
|
|
55
|
+
focusIfContained(headerCollapseButtonId)
|
|
56
|
+
}
|
|
43
57
|
}
|
|
44
|
-
|
|
45
58
|
return (
|
|
46
59
|
<div className={className('toggle-button')}>
|
|
47
60
|
<div id={titleId}>
|
|
@@ -59,9 +72,10 @@ const ToggleButton = ({ onOpenChat }) => {
|
|
|
59
72
|
type="button"
|
|
60
73
|
aria-labelledby={titleId}
|
|
61
74
|
className={className('toggle-button__button')}
|
|
62
|
-
onClick={onOpenChat}
|
|
63
75
|
ref={buttonRef}
|
|
64
|
-
onMouseUp={
|
|
76
|
+
onMouseUp={handleMouseUp}
|
|
77
|
+
onKeyUp={handleKeyUp}
|
|
78
|
+
onClick={handleClick}
|
|
65
79
|
/>
|
|
66
80
|
</div>
|
|
67
81
|
)
|
|
@@ -15,8 +15,8 @@ export default function FileInputForm({
|
|
|
15
15
|
}) {
|
|
16
16
|
const { t } = useI18n()
|
|
17
17
|
const [{ value: fileList }] = useFormControl(controlName)
|
|
18
|
-
const
|
|
19
|
-
|
|
18
|
+
const hasFile = fileList && fileList.length > 0
|
|
19
|
+
const selectedFileName = hasFile ? fileList[0].name : ''
|
|
20
20
|
|
|
21
21
|
return (
|
|
22
22
|
<Form className={className('input', 'input--file')}>
|
|
@@ -25,7 +25,10 @@ export default function FileInputForm({
|
|
|
25
25
|
id={skiplinkId}
|
|
26
26
|
accept={accept}
|
|
27
27
|
labelText={t('fileUpload.labelText')}
|
|
28
|
-
outputText={t('fileUpload.selectedText',
|
|
28
|
+
outputText={t('fileUpload.selectedText', {
|
|
29
|
+
hasFile,
|
|
30
|
+
filename: selectedFileName,
|
|
31
|
+
})}
|
|
29
32
|
contentHint={contentHint}
|
|
30
33
|
/>
|
|
31
34
|
<div className={className('upload__button-container')}>
|
|
@@ -30,11 +30,8 @@ const Upload = () => {
|
|
|
30
30
|
const skiplinkTargetId = useSkiplink()
|
|
31
31
|
const focusSkiplinkTarget = useSkiplinkTargetFocusing()
|
|
32
32
|
// This hook should be refactored at some point
|
|
33
|
-
const {
|
|
34
|
-
|
|
35
|
-
allowedMimeTypes,
|
|
36
|
-
maxSize,
|
|
37
|
-
} = useFileUploadMeta()
|
|
33
|
+
const { serviceAllowsUploads, allowedMimeTypes, maxSize } =
|
|
34
|
+
useFileUploadMeta()
|
|
38
35
|
const cancelButtonRef = useRef(null)
|
|
39
36
|
const canUpload = useRef(serviceAllowsUploads)
|
|
40
37
|
|
|
@@ -43,16 +40,14 @@ const Upload = () => {
|
|
|
43
40
|
|
|
44
41
|
const hasError = false
|
|
45
42
|
|
|
46
|
-
const {
|
|
47
|
-
|
|
48
|
-
uploadHandle,
|
|
49
|
-
hasServerError,
|
|
50
|
-
progress,
|
|
51
|
-
} = useSingleFileUpload(formName, fileInputName)
|
|
43
|
+
const { hasFile, selectedFileName, uploadHandle, hasServerError, progress } =
|
|
44
|
+
useSingleFileUpload(formName, fileInputName)
|
|
52
45
|
const notificationId = useGeneratedId()
|
|
53
46
|
const prevIsComplete = useRef(true)
|
|
54
47
|
|
|
55
|
-
const contentHintText = t('fileUpload.contentHint',
|
|
48
|
+
const contentHintText = t('fileUpload.contentHint', {
|
|
49
|
+
size: formatBytes(maxSize),
|
|
50
|
+
})
|
|
56
51
|
const prevContentHintText = useRef('')
|
|
57
52
|
const containerRef = useRef(null)
|
|
58
53
|
|
|
@@ -184,7 +179,10 @@ const Upload = () => {
|
|
|
184
179
|
contentHint={contentHintText}
|
|
185
180
|
isComplete={isComplete}
|
|
186
181
|
isUploading={isUploading}
|
|
187
|
-
outputText={t('fileUpload.selectedText',
|
|
182
|
+
outputText={t('fileUpload.selectedText', {
|
|
183
|
+
hasFile,
|
|
184
|
+
filename: selectedFileName,
|
|
185
|
+
})}
|
|
188
186
|
onClickCancel={handleOnClickCancel}
|
|
189
187
|
/>
|
|
190
188
|
)}
|
|
@@ -10,9 +10,7 @@ import {
|
|
|
10
10
|
} from '../../hooks/seamly-state-hooks'
|
|
11
11
|
import { useGeneratedId } from '../../hooks/utility-hooks'
|
|
12
12
|
import { useSkiplinkTargetFocusing } from '../../hooks/focus-helper-hooks'
|
|
13
|
-
import { useSeamlyHasUserResponded } from '../../hooks/seamly-api-hooks'
|
|
14
13
|
import { useLiveRegion } from '../../hooks/live-region-hooks'
|
|
15
|
-
import useSeamlyInterrupt from '../../hooks/use-seamly-interrupt'
|
|
16
14
|
import useSeamlyIdleDetachCountdown from '../../hooks/use-seamly-idle-detach-countdown'
|
|
17
15
|
import useSeamlyResumeConversationPrompt from '../../hooks/use-seamly-resume-conversation-prompt'
|
|
18
16
|
import { useI18n } from '../../../domains/i18n'
|
|
@@ -20,6 +18,8 @@ import InOutTransition, {
|
|
|
20
18
|
transitionStartStates,
|
|
21
19
|
} from '../widgets/in-out-transition'
|
|
22
20
|
import { useTranslatedEventData } from '../../../domains/translations'
|
|
21
|
+
import { useInterrupt } from '../../../domains/interrupt'
|
|
22
|
+
import { useUserHasResponded } from '../../../domains/app'
|
|
23
23
|
|
|
24
24
|
const Faq = () => {
|
|
25
25
|
const { t } = useI18n()
|
|
@@ -27,7 +27,7 @@ const Faq = () => {
|
|
|
27
27
|
const sectionId = useGeneratedId()
|
|
28
28
|
const focusSkiplinkTarget = useSkiplinkTargetFocusing()
|
|
29
29
|
const { sendPolite } = useLiveRegion()
|
|
30
|
-
const { hasInterrupt } =
|
|
30
|
+
const { hasInterrupt } = useInterrupt()
|
|
31
31
|
const { hasCountdown, endCountdown } = useSeamlyIdleDetachCountdown()
|
|
32
32
|
const { hasPrompt, continueChat } = useSeamlyResumeConversationPrompt()
|
|
33
33
|
|
|
@@ -42,7 +42,7 @@ const Faq = () => {
|
|
|
42
42
|
classNames: [
|
|
43
43
|
itemBaseClass,
|
|
44
44
|
...categories.map(
|
|
45
|
-
cat =>
|
|
45
|
+
(cat) =>
|
|
46
46
|
`faqs__item--${String(cat)
|
|
47
47
|
.toLowerCase()
|
|
48
48
|
.replace(/[^a-z0-9_\\-]/, '')}`,
|
|
@@ -55,7 +55,7 @@ const Faq = () => {
|
|
|
55
55
|
const prevHasFaqs = useRef(false)
|
|
56
56
|
|
|
57
57
|
const { isInline } = useSeamlyLayoutMode()
|
|
58
|
-
const hasResponded =
|
|
58
|
+
const hasResponded = useUserHasResponded()
|
|
59
59
|
const hideForWindow = !isInline && hasResponded
|
|
60
60
|
const prevHideForWindow = useRef(hideForWindow)
|
|
61
61
|
|
|
@@ -138,7 +138,7 @@ const Faq = () => {
|
|
|
138
138
|
)}
|
|
139
139
|
{!!renderedFaqList.length && (
|
|
140
140
|
<ul className={className('faqs__list')}>
|
|
141
|
-
{renderedFaqList.map(faq => (
|
|
141
|
+
{renderedFaqList.map((faq) => (
|
|
142
142
|
<li key={faq.id.toString()} className={className(faq.classNames)}>
|
|
143
143
|
<button
|
|
144
144
|
type="button"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useState, useEffect } from 'preact/hooks'
|
|
2
|
+
import { className } from '../../../lib/css'
|
|
3
|
+
import Icon from '../layout/icon'
|
|
4
|
+
|
|
5
|
+
export default function Error({ id, error }) {
|
|
6
|
+
const [isAvailable, setIsAvailable] = useState(false)
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const timerId = setTimeout(() => setIsAvailable(true), 300) // 300 = magic number, could be less or more
|
|
9
|
+
return () => clearTimeout(timerId) // clear timer if error is mounted+unmounted within 300
|
|
10
|
+
}, [])
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div aria-live="assertive" aria-atomic="true">
|
|
14
|
+
{isAvailable && error && (
|
|
15
|
+
<span id={id} className={className('error')}>
|
|
16
|
+
<Icon name="error" size="16" />
|
|
17
|
+
{error}
|
|
18
|
+
</span>
|
|
19
|
+
)}
|
|
20
|
+
</div>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
@@ -3,6 +3,7 @@ import { className } from '../../../lib/css'
|
|
|
3
3
|
import { useGeneratedId } from '../../hooks/seamly-hooks'
|
|
4
4
|
import { useFormControl, useFormContext } from '../../../domains/forms'
|
|
5
5
|
import Icon from '../layout/icon'
|
|
6
|
+
import Error from './error'
|
|
6
7
|
|
|
7
8
|
export default function FileInput({
|
|
8
9
|
id,
|
|
@@ -38,7 +39,7 @@ export default function FileInput({
|
|
|
38
39
|
}, [setFocusWithin, onBlur])
|
|
39
40
|
|
|
40
41
|
const handleChange = useCallback(
|
|
41
|
-
e => {
|
|
42
|
+
(e) => {
|
|
42
43
|
const customEvent = {
|
|
43
44
|
target: {
|
|
44
45
|
value: e.target.files,
|
|
@@ -57,14 +58,7 @@ export default function FileInput({
|
|
|
57
58
|
{contentHint}
|
|
58
59
|
</span>
|
|
59
60
|
)}
|
|
60
|
-
<
|
|
61
|
-
{hasError && (
|
|
62
|
-
<span id={errorId} className={className('error')}>
|
|
63
|
-
<Icon name="error" size="16" />
|
|
64
|
-
{error}
|
|
65
|
-
</span>
|
|
66
|
-
)}
|
|
67
|
-
</div>
|
|
61
|
+
<Error id={errorId} error={hasError && error} />
|
|
68
62
|
<div
|
|
69
63
|
className={className([
|
|
70
64
|
'file-upload',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { className } from '../../../lib/css'
|
|
2
|
-
import
|
|
2
|
+
import Error from './error'
|
|
3
3
|
|
|
4
4
|
const FormControlWrapper = ({
|
|
5
5
|
contentHint,
|
|
@@ -23,14 +23,7 @@ const FormControlWrapper = ({
|
|
|
23
23
|
{contentHint}
|
|
24
24
|
</span>
|
|
25
25
|
)}
|
|
26
|
-
<
|
|
27
|
-
{!validity && (
|
|
28
|
-
<span id={`${id}-error`} className={className('error')}>
|
|
29
|
-
<Icon name="error" size="16" />
|
|
30
|
-
{errorText}
|
|
31
|
-
</span>
|
|
32
|
-
)}
|
|
33
|
-
</div>
|
|
26
|
+
<Error id={`${id}-error`} error={!validity && errorText} />
|
|
34
27
|
{children}
|
|
35
28
|
</>
|
|
36
29
|
)
|
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
useSeamlyUnreadCount,
|
|
5
5
|
useSeamlyVisibility,
|
|
6
6
|
useSeamlyCurrentAgent,
|
|
7
|
-
useSeamlyInterrupt,
|
|
8
|
-
useSeamlyConfig,
|
|
9
7
|
} from '../../hooks/seamly-hooks'
|
|
10
8
|
import { className } from '../../../lib/css'
|
|
11
9
|
import { useI18n } from '../../../domains/i18n'
|
|
10
|
+
import { useInterrupt } from '../../../domains/interrupt'
|
|
11
|
+
import { useConfig } from '../../../domains/config'
|
|
12
12
|
|
|
13
13
|
const AgentInfo = () => {
|
|
14
14
|
const { t } = useI18n()
|
|
@@ -16,8 +16,8 @@ const AgentInfo = () => {
|
|
|
16
16
|
const unreadMessageCount = useSeamlyUnreadCount()
|
|
17
17
|
const { isOpen } = useSeamlyVisibility()
|
|
18
18
|
const currentAgent = useSeamlyCurrentAgent()
|
|
19
|
-
const { hasInterrupt } =
|
|
20
|
-
const { defaults } =
|
|
19
|
+
const { hasInterrupt } = useInterrupt()
|
|
20
|
+
const { defaults } = useConfig()
|
|
21
21
|
|
|
22
22
|
const { startChatIcon } = defaults || {}
|
|
23
23
|
|
|
@@ -1,40 +1,43 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useCallback, useMemo } from 'preact/hooks'
|
|
2
2
|
import { className } from '../../../lib/css'
|
|
3
3
|
import {
|
|
4
4
|
useSeamlyAppContainerClassNames,
|
|
5
|
-
useSeamlyConfig,
|
|
6
5
|
useSeamlyVisibility,
|
|
7
6
|
useSeamlyLayoutMode,
|
|
8
|
-
useSeamlyHasUserResponded,
|
|
9
7
|
useSeamlyContainerElement,
|
|
10
8
|
} from '../../hooks/seamly-hooks'
|
|
11
9
|
import Faq from '../faq/faq'
|
|
12
10
|
import { visibilityStates } from '../../utils/seamly-utils'
|
|
11
|
+
import { useConfig } from '../../../domains/config'
|
|
12
|
+
import { useUserHasResponded } from '../../../domains/app'
|
|
13
|
+
import { useI18n } from '../../../domains/i18n'
|
|
13
14
|
|
|
14
15
|
const AppFrame = ({ children }) => {
|
|
15
16
|
const [, setSeamlyContainerElement] = useSeamlyContainerElement()
|
|
16
17
|
const { isOpen, isVisible, setVisibility } = useSeamlyVisibility()
|
|
17
|
-
const {
|
|
18
|
+
const { zIndex, showFaq } = useConfig()
|
|
18
19
|
const { isModal, isInline } = useSeamlyLayoutMode()
|
|
19
20
|
const appContainerClassNames = useSeamlyAppContainerClassNames()
|
|
20
|
-
const userResponded =
|
|
21
|
-
const
|
|
22
|
-
const { locale } = context || {}
|
|
21
|
+
const userResponded = useUserHasResponded()
|
|
22
|
+
const { locale } = useI18n()
|
|
23
23
|
|
|
24
24
|
const containerElementRef = useCallback(
|
|
25
|
-
container => {
|
|
25
|
+
(container) => {
|
|
26
26
|
setSeamlyContainerElement(container)
|
|
27
27
|
},
|
|
28
28
|
[setSeamlyContainerElement],
|
|
29
29
|
)
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
const blockLang = useMemo(() => {
|
|
32
32
|
if (locale) {
|
|
33
33
|
const htmlElementLang = document
|
|
34
34
|
.querySelector('html')
|
|
35
35
|
.getAttribute('lang')
|
|
36
|
-
if (
|
|
36
|
+
if (htmlElementLang !== locale) {
|
|
37
|
+
return locale
|
|
38
|
+
}
|
|
37
39
|
}
|
|
40
|
+
return undefined
|
|
38
41
|
}, [locale])
|
|
39
42
|
|
|
40
43
|
const classNames = ['app', ...appContainerClassNames]
|
|
@@ -47,14 +50,14 @@ const AppFrame = ({ children }) => {
|
|
|
47
50
|
classNames.push('app--user-responded')
|
|
48
51
|
}
|
|
49
52
|
|
|
50
|
-
const onKeyDownHandler = e => {
|
|
53
|
+
const onKeyDownHandler = (e) => {
|
|
51
54
|
if ((e.code && e.code === 'Escape') || e.keyCode === 27)
|
|
52
55
|
if (!isInline && isOpen) {
|
|
53
56
|
setVisibility(visibilityStates.minimized)
|
|
54
57
|
}
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
const onClickHandler = e => {
|
|
60
|
+
const onClickHandler = (e) => {
|
|
58
61
|
if (isModal) {
|
|
59
62
|
e.stopPropagation()
|
|
60
63
|
}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { className } from '../../../lib/css'
|
|
2
|
-
import {
|
|
3
|
-
useSeamlyInterrupt,
|
|
4
|
-
useSeamlyVisibility,
|
|
5
|
-
} from '../../hooks/seamly-hooks'
|
|
2
|
+
import { useSeamlyVisibility } from '../../hooks/seamly-hooks'
|
|
6
3
|
import CobrowsingActiveFrame from '../warnings/cobrowsing-active-frame'
|
|
7
4
|
import AppOptions from '../app-options'
|
|
8
5
|
import { ChatStatus as TranslationsChatStatus } from '../../../domains/translations'
|
|
6
|
+
import { useInterrupt } from '../../../domains/interrupt'
|
|
9
7
|
|
|
10
8
|
function ChatFrame({ children, interruptComponent: InterruptComponent }) {
|
|
11
|
-
const { hasInterrupt, meta } =
|
|
9
|
+
const { hasInterrupt, meta } = useInterrupt()
|
|
12
10
|
const { isOpen } = useSeamlyVisibility()
|
|
13
11
|
|
|
14
12
|
const getContent = () => {
|
|
@@ -1,27 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useRef } from 'preact/hooks'
|
|
2
2
|
import { className } from '../../../lib/css'
|
|
3
3
|
import Icon from './icon'
|
|
4
4
|
import { useI18n } from '../../../domains/i18n'
|
|
5
|
-
import {
|
|
6
|
-
useSeamlyVisibility,
|
|
7
|
-
useFocusIfSeamlyContainedFocus,
|
|
8
|
-
} from '../../hooks/seamly-hooks'
|
|
5
|
+
import { useSeamlyStateContext } from '../../hooks/seamly-hooks'
|
|
9
6
|
|
|
10
7
|
const Header = ({ children, onCloseChat }) => {
|
|
11
|
-
const {
|
|
12
|
-
const prevIsOpen = useRef(null)
|
|
8
|
+
const { headerCollapseButtonId } = useSeamlyStateContext()
|
|
13
9
|
const closeButton = useRef(null)
|
|
14
|
-
const focusIfContained = useFocusIfSeamlyContainedFocus()
|
|
15
|
-
|
|
16
|
-
useLayoutEffect(() => {
|
|
17
|
-
// Because we can open the app from the external API we
|
|
18
|
-
// need to determine if current keyboard focus resides inside
|
|
19
|
-
// the Seamly app first otherwise focus will be hijacked
|
|
20
|
-
if (isOpen && prevIsOpen.current === false) {
|
|
21
|
-
focusIfContained(closeButton.current)
|
|
22
|
-
}
|
|
23
|
-
prevIsOpen.current = isOpen
|
|
24
|
-
}, [isOpen, focusIfContained])
|
|
25
10
|
|
|
26
11
|
const { t } = useI18n()
|
|
27
12
|
return (
|
|
@@ -33,6 +18,7 @@ const Header = ({ children, onCloseChat }) => {
|
|
|
33
18
|
className={className('button', 'header-controls__collapse')}
|
|
34
19
|
onClick={onCloseChat}
|
|
35
20
|
ref={closeButton}
|
|
21
|
+
id={headerCollapseButtonId}
|
|
36
22
|
>
|
|
37
23
|
<Icon name="chevronDown" size="32" alt={t('header.collapseApp')} />
|
|
38
24
|
</button>
|
|
@@ -25,14 +25,18 @@ const Interrupt = ({
|
|
|
25
25
|
useEffect(() => {
|
|
26
26
|
if (isExpiredError) {
|
|
27
27
|
seamlyCommands[action]()
|
|
28
|
-
}
|
|
28
|
+
}
|
|
29
|
+
}, [action, seamlyCommands, isExpiredError])
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (!isExpiredError && srText) {
|
|
29
33
|
// Wait for live regions to stabilise in case this occurs
|
|
30
34
|
// at an initial render
|
|
31
35
|
setTimeout(() => {
|
|
32
36
|
sendPolite(srText)
|
|
33
37
|
}, 200)
|
|
34
38
|
}
|
|
35
|
-
}, [sendPolite,
|
|
39
|
+
}, [sendPolite, srText, isExpiredError])
|
|
36
40
|
|
|
37
41
|
const onClickHandler = () => {
|
|
38
42
|
seamlyCommands[action]()
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { className } from '../../../lib/css'
|
|
2
|
-
import { useSeamlyDisclaimerState } from '../../hooks/seamly-hooks'
|
|
3
2
|
import { useI18n } from '../../../domains/i18n'
|
|
3
|
+
import { useConfig } from '../../../domains/config'
|
|
4
4
|
|
|
5
5
|
const PrivacyDisclaimer = () => {
|
|
6
6
|
const { t } = useI18n()
|
|
7
|
-
const showDisclaimer =
|
|
7
|
+
const { showDisclaimer } = useConfig()
|
|
8
8
|
|
|
9
9
|
return (
|
|
10
10
|
showDisclaimer && (
|
|
@@ -18,12 +18,8 @@ import {
|
|
|
18
18
|
const Cobrowsing = () => {
|
|
19
19
|
const { t } = useI18n()
|
|
20
20
|
const cobrowsingDescriptionId = useGeneratedId()
|
|
21
|
-
const {
|
|
22
|
-
|
|
23
|
-
features,
|
|
24
|
-
setUserSelectedOption,
|
|
25
|
-
hideOption,
|
|
26
|
-
} = useSeamlyOptions()
|
|
21
|
+
const { userSelectedOptions, features, setUserSelectedOption, hideOption } =
|
|
22
|
+
useSeamlyOptions()
|
|
27
23
|
const { enabled: canActivateCobrowsing } = features.cobrowsing || {}
|
|
28
24
|
const prevCanActivateCobrowsing = useRef(null)
|
|
29
25
|
const [cobrowsingToggleActive, setCobrowsingToggleActive] = useState(
|
|
@@ -53,7 +49,7 @@ const Cobrowsing = () => {
|
|
|
53
49
|
prevCanActivateCobrowsing.current = canActivateCobrowsing
|
|
54
50
|
}, [canActivateCobrowsing, setUserSelectedOption, sendAssertive, t])
|
|
55
51
|
|
|
56
|
-
const toggleAndFocus = isCancel => {
|
|
52
|
+
const toggleAndFocus = (isCancel) => {
|
|
57
53
|
if (!isCancel && !userSelectedOptions.cobrowsing) {
|
|
58
54
|
focusContainer()
|
|
59
55
|
} else {
|
|
@@ -12,12 +12,8 @@ import { getKey, keyNames, focusElement } from '../../utils/general-utils'
|
|
|
12
12
|
|
|
13
13
|
const OptionsButton = () => {
|
|
14
14
|
const { t } = useI18n()
|
|
15
|
-
const {
|
|
16
|
-
|
|
17
|
-
showOption,
|
|
18
|
-
panelActive,
|
|
19
|
-
hideOption,
|
|
20
|
-
} = useSeamlyOptions()
|
|
15
|
+
const { menuOptions, showOption, panelActive, hideOption } =
|
|
16
|
+
useSeamlyOptions()
|
|
21
17
|
const { id } = useOptionButton()
|
|
22
18
|
const focusOutDelayTimeoutID = useRef(null)
|
|
23
19
|
|
|
@@ -48,7 +44,7 @@ const OptionsButton = () => {
|
|
|
48
44
|
requestAnimationFrame(() => {
|
|
49
45
|
requestAnimationFrame(() => {
|
|
50
46
|
const firstActiveOptionIndex = menuOptions.findIndex(
|
|
51
|
-
option => option.available,
|
|
47
|
+
(option) => option.available,
|
|
52
48
|
)
|
|
53
49
|
const focusIndex =
|
|
54
50
|
firstActiveOptionIndex === -1 ? 0 : firstActiveOptionIndex
|
|
@@ -64,13 +60,13 @@ const OptionsButton = () => {
|
|
|
64
60
|
hideOption()
|
|
65
61
|
}
|
|
66
62
|
if (multiMenu) {
|
|
67
|
-
setMenuIsOpen(o => !o)
|
|
63
|
+
setMenuIsOpen((o) => !o)
|
|
68
64
|
} else if (firstOption.available) {
|
|
69
65
|
showOption(firstOption.name)
|
|
70
66
|
}
|
|
71
67
|
}
|
|
72
68
|
|
|
73
|
-
const onMainKeyDownHandler = e => {
|
|
69
|
+
const onMainKeyDownHandler = (e) => {
|
|
74
70
|
if (!menuIsOpen) {
|
|
75
71
|
return
|
|
76
72
|
}
|
|
@@ -89,7 +85,7 @@ const OptionsButton = () => {
|
|
|
89
85
|
}
|
|
90
86
|
}
|
|
91
87
|
|
|
92
|
-
const onButtonKeyDownHandler = e => {
|
|
88
|
+
const onButtonKeyDownHandler = (e) => {
|
|
93
89
|
if (getKey(e) === keyNames.ArrowDown) {
|
|
94
90
|
setMenuIsOpen(true)
|
|
95
91
|
e.preventDefault()
|
|
@@ -194,7 +190,7 @@ const OptionsButton = () => {
|
|
|
194
190
|
>
|
|
195
191
|
<button
|
|
196
192
|
type="button"
|
|
197
|
-
ref={item => {
|
|
193
|
+
ref={(item) => {
|
|
198
194
|
menuItemButtons.current[i] = item
|
|
199
195
|
}}
|
|
200
196
|
className={className([
|
|
@@ -202,8 +198,8 @@ const OptionsButton = () => {
|
|
|
202
198
|
'button--secondary',
|
|
203
199
|
...(available ? [] : ['button--disabled']),
|
|
204
200
|
])}
|
|
205
|
-
onKeyDown={e => onMenuItemKeyDownHandler(e, i)}
|
|
206
|
-
onKeyPress={e => onKeyPressHandler(e, i)}
|
|
201
|
+
onKeyDown={(e) => onMenuItemKeyDownHandler(e, i)}
|
|
202
|
+
onKeyPress={(e) => onKeyPressHandler(e, i)}
|
|
207
203
|
onClick={() => onMenuItemClickHandler(name, available)}
|
|
208
204
|
aria-disabled={!available ? 'true' : null}
|
|
209
205
|
>
|
|
@@ -38,8 +38,8 @@ const Transcript = () => {
|
|
|
38
38
|
)
|
|
39
39
|
|
|
40
40
|
const handleSubmit = useCallback(
|
|
41
|
-
values => {
|
|
42
|
-
const emailAddress = values[controlName]
|
|
41
|
+
(values) => {
|
|
42
|
+
const emailAddress = values[controlName].trim()
|
|
43
43
|
sendAction({
|
|
44
44
|
type: actionTypes.sendTranscript,
|
|
45
45
|
body: { emailAddress },
|
|
@@ -9,7 +9,7 @@ export default function TranscriptForm({ controlName, describedById }) {
|
|
|
9
9
|
<Form noValidate="true">
|
|
10
10
|
<Input
|
|
11
11
|
name={controlName}
|
|
12
|
-
type="
|
|
12
|
+
type="email"
|
|
13
13
|
className={className('transcript__input')}
|
|
14
14
|
aria-describedby={describedById}
|
|
15
15
|
labelClass={className('label')}
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import { useEffect, useRef } from 'preact/hooks'
|
|
2
2
|
import CobrowsingActive from './cobrowsing-active'
|
|
3
|
-
import {
|
|
4
|
-
useLiveRegion,
|
|
5
|
-
useSeamlyInterrupt,
|
|
6
|
-
useSeamlyOptions,
|
|
7
|
-
} from '../../hooks/seamly-hooks'
|
|
3
|
+
import { useLiveRegion, useSeamlyOptions } from '../../hooks/seamly-hooks'
|
|
8
4
|
import { useI18n } from '../../../domains/i18n'
|
|
5
|
+
import { useInterrupt } from '../../../domains/interrupt'
|
|
9
6
|
|
|
10
7
|
const CobrowsingActiveFrame = () => {
|
|
11
8
|
const { t } = useI18n()
|
|
12
9
|
const { userSelectedOptions } = useSeamlyOptions()
|
|
13
|
-
const { hasInterrupt } =
|
|
10
|
+
const { hasInterrupt } = useInterrupt()
|
|
14
11
|
const { cobrowsing } = userSelectedOptions
|
|
15
12
|
const prevCobrowsing = useRef(cobrowsing)
|
|
16
13
|
const { sendPolite } = useLiveRegion()
|