@seamly/web-ui 24.4.1 → 24.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seamly/web-ui",
3
- "version": "24.4.1",
3
+ "version": "24.5.0-beta.1",
4
4
  "main": "build/dist/lib/index.js",
5
5
  "types": "build/src/javascripts/index.d.ts",
6
6
  "exports": {
@@ -21,9 +21,9 @@
21
21
  "webpack/*"
22
22
  ],
23
23
  "dependencies": {
24
- "@reduxjs/toolkit": "^2.6.1",
24
+ "@reduxjs/toolkit": "^2.8.1",
25
25
  "@ultraq/icu-message-formatter": "^0.14.3",
26
- "core-js": "^3.41.0",
26
+ "core-js": "^3.42.0",
27
27
  "focus-trap": "^7.6.4",
28
28
  "include-media": "^2.0.0",
29
29
  "js-cookie": "^3.0.5",
@@ -32,11 +32,11 @@
32
32
  "reconnecting-websocket": "^4.4.0"
33
33
  },
34
34
  "devDependencies": {
35
- "@babel/core": "^7.26.10",
36
- "@babel/preset-env": "^7.26.9",
37
- "@babel/preset-react": "^7.26.3",
38
- "@babel/preset-typescript": "^7.27.0",
39
- "@playwright/test": "^1.51.1",
35
+ "@babel/core": "^7.27.1",
36
+ "@babel/preset-env": "^7.27.2",
37
+ "@babel/preset-react": "^7.27.1",
38
+ "@babel/preset-typescript": "^7.27.1",
39
+ "@playwright/test": "1.51.1",
40
40
  "@seamly/doc-site": "^4.0.0",
41
41
  "@seamly/eslint-config": "^3.1.0",
42
42
  "@seamly/prettier-config": "^3.1.0",
@@ -45,22 +45,22 @@
45
45
  "@testing-library/preact": "^3.2.4",
46
46
  "@types/core-js": "^2.5.8",
47
47
  "@types/jest": "^29.5.12",
48
- "@typescript-eslint/eslint-plugin": "^8.29.0",
49
- "@typescript-eslint/parser": "^8.29.0",
48
+ "@typescript-eslint/eslint-plugin": "^8.32.0",
49
+ "@typescript-eslint/parser": "^8.32.0",
50
50
  "babel-jest": "^29.7.0",
51
51
  "babel-loader": "^10.0.0",
52
52
  "copy-webpack-plugin": "^13.0.0",
53
53
  "eslint": "^8.57.0",
54
54
  "eslint-config-prettier": "^9.1.0",
55
- "eslint-import-resolver-typescript": "^3.9.1",
55
+ "eslint-import-resolver-typescript": "^3.10.1",
56
56
  "eslint-plugin-filenames": "^1.3.2",
57
57
  "eslint-plugin-import": "^2.29.1",
58
58
  "eslint-plugin-jest": "^28.11.0",
59
- "eslint-plugin-prettier": "^5.2.6",
60
- "eslint-plugin-react": "^7.37.4",
59
+ "eslint-plugin-prettier": "^5.4.0",
60
+ "eslint-plugin-react": "^7.37.5",
61
61
  "eslint-plugin-react-hooks": "^4.6.2",
62
62
  "file-loader": "^6.2.0",
63
- "fork-ts-checker-webpack-plugin": "^9.0.3",
63
+ "fork-ts-checker-webpack-plugin": "^9.1.0",
64
64
  "husky": "^9.1.7",
65
65
  "jest": "^29.7.0",
66
66
  "jest-environment-jsdom": "^29.7.0",
@@ -69,23 +69,24 @@
69
69
  "openapi-typescript": "^6.7",
70
70
  "playwright-test-coverage": "^1.2.12",
71
71
  "postcss": "^8.5.3",
72
- "preact": "^10.26.4",
72
+ "preact": "^10.26.6",
73
73
  "prettier": "^3.5.3",
74
74
  "rimraf": "^6.0.1",
75
75
  "style-loader": "^4.0.0",
76
- "stylelint": "^16.17.0",
77
- "typescript": "^5.8.2",
78
- "webpack": "^5.98.0",
76
+ "stylelint": "^16.19.1",
77
+ "typescript": "^5.8.3",
78
+ "webpack": "^5.99.8",
79
79
  "webpack-bundle-analyzer": "^4.10.2",
80
80
  "webpack-cli": "^6.0.1",
81
81
  "webpack-dev-server": "^5.2.1",
82
82
  "webpack-merge": "^6.0.1"
83
83
  },
84
84
  "resolutions": {
85
+ "sass": "1.79.6",
85
86
  "string-width": "^4.2.3"
86
87
  },
87
88
  "peerDependencies": {
88
- "preact": "^10.25.1"
89
+ "preact": "^10.26.6"
89
90
  },
90
91
  "scripts": {
91
92
  "build:clean": "rimraf build; mkdir -p build",
@@ -104,6 +104,7 @@ export type Config = Partial<Omit<ChannelConfig, 'preChat'>> & {
104
104
  connectWhenInView: boolean
105
105
  visibilityCallback?: VisibilityCallback
106
106
  errorCallback?: ErrorCallback
107
+ alwaysShowEntryLabel?: boolean
107
108
  api: ApiConfig
108
109
  appContainerClassNames?: string[] | ((_config: Config) => string[])
109
110
  zIndex?: number
@@ -7,6 +7,7 @@ import type { ChannelEvent } from 'domains/store/store.types'
7
7
 
8
8
  export const initialConfigState: Config = {
9
9
  ...defaultConfig,
10
+ alwaysShowEntryLabel: false,
10
11
  api: {
11
12
  domain: '',
12
13
  key: '',
@@ -37,6 +38,7 @@ export const initialConfigState: Config = {
37
38
  }
38
39
 
39
40
  const configKeys: (keyof Config)[] = [
41
+ 'alwaysShowEntryLabel',
40
42
  'hideOnNoUserResponse',
41
43
  'connectWhenInView',
42
44
  'showDisclaimer',
@@ -141,7 +141,6 @@ export const calculateNewEntryMeta = (
141
141
  ...entryMeta,
142
142
  active: newActive,
143
143
  optionsOverride: {
144
- ...(entryMeta.optionsOverride || {}),
145
144
  ...(type ? { [type]: options } : {}),
146
145
  },
147
146
  actions,
@@ -79,7 +79,11 @@ const Conversation = () => {
79
79
  <div className={className('chat__body')}>
80
80
  <div className={className('conversation__container')}>
81
81
  <PrivacyDisclaimer />
82
- <ol className={className('conversation')}>
82
+ <ol
83
+ className={className('conversation')}
84
+ role="log"
85
+ aria-label={t('chat.srLabel')}
86
+ >
83
87
  <ComponentFilter>
84
88
  <Events />
85
89
  </ComponentFilter>
@@ -1,58 +1,11 @@
1
- import { useEffect, useMemo, useRef } from 'preact/hooks'
2
- import { newMessageScreenReaderWait } from 'config'
1
+ import { useEffect } from 'preact/hooks'
3
2
  import { useI18n } from 'domains/i18n/hooks'
4
- import { useVisibility } from 'domains/visibility/hooks'
5
- import {
6
- useEvents,
7
- useLiveRegion,
8
- useSeamlyIsHistoryLoaded,
9
- } from 'ui/hooks/seamly-hooks'
10
- import { debounce } from 'ui/utils/general-utils'
3
+ import { useLiveRegion, useSeamlyIsHistoryLoaded } from 'ui/hooks/seamly-hooks'
11
4
 
12
5
  const SeamlyNewNotifications = () => {
13
6
  const { t } = useI18n()
14
- const events = useEvents()
15
- const previousEventCount = useRef(0)
16
- const previousServerEventCount = useRef(0)
17
7
  const isHistoryLoaded = useSeamlyIsHistoryLoaded()
18
8
  const { sendPolite } = useLiveRegion()
19
- const { isOpen } = useVisibility()
20
- const prevIsOpen = useRef(null)
21
- const debounceFunc = useRef<
22
- ((_runInstant?: boolean | undefined) => void) | null
23
- >(null)
24
-
25
- const notifyUnread = useMemo(() => {
26
- return debounce((eventArray) => {
27
- const serverEventCount = eventArray.filter(
28
- ({ payload }) => !payload.fromClient && !payload.fromHistory,
29
- ).length
30
- if (serverEventCount > previousServerEventCount.current) {
31
- sendPolite(
32
- t('message.srNewEventCount', {
33
- newCount: serverEventCount - previousServerEventCount.current,
34
- }),
35
- )
36
- previousServerEventCount.current = serverEventCount
37
- }
38
- }, newMessageScreenReaderWait)
39
- }, [sendPolite, t])
40
-
41
- useEffect(() => {
42
- if (events.length > previousEventCount.current) {
43
- if (isOpen) {
44
- debounceFunc.current = notifyUnread(events)
45
- }
46
- previousEventCount.current = events.length
47
- }
48
- }, [events, notifyUnread, isOpen])
49
-
50
- useEffect(() => {
51
- if (prevIsOpen.current && !isOpen && debounceFunc.current) {
52
- debounceFunc.current(true)
53
- debounceFunc.current = null
54
- }
55
- }, [isOpen])
56
9
 
57
10
  useEffect(() => {
58
11
  if (isHistoryLoaded) {
@@ -1,5 +1,6 @@
1
1
  import { useEffect, useMemo } from 'preact/hooks'
2
2
  import { maxCharacterSrDebounceDelay, maxCharacterWarningLimit } from 'config'
3
+ import { useConfig } from 'domains/config/hooks'
3
4
  import { useFormControl } from 'domains/forms/hooks'
4
5
  import { useI18n } from 'domains/i18n/hooks'
5
6
  import { useAppDispatch } from 'domains/store'
@@ -62,6 +63,7 @@ export const useEntryTextTranslation = (controlName: 'textMessageEntry') => {
62
63
  entryMeta: { optionsOverride },
63
64
  } = useSeamlyStateContext()
64
65
 
66
+ const { alwaysShowEntryLabel } = useConfig()
65
67
  const { t } = useI18n()
66
68
 
67
69
  const placeholder: string = useMemo(
@@ -89,8 +91,11 @@ export const useEntryTextTranslation = (controlName: 'textMessageEntry') => {
89
91
  )
90
92
 
91
93
  const labelClass = useMemo(
92
- () => (optionsOverride?.text?.label ? 'label' : 'visually-hidden'),
93
- [optionsOverride?.text?.label],
94
+ () =>
95
+ alwaysShowEntryLabel === true || optionsOverride?.text?.label
96
+ ? 'label'
97
+ : 'visually-hidden',
98
+ [alwaysShowEntryLabel, optionsOverride?.text?.label],
94
99
  )
95
100
 
96
101
  return { placeholder, label, labelClass }
@@ -9,6 +9,7 @@ import { className } from 'lib/css'
9
9
  import Suggestions from 'ui/components/suggestions'
10
10
  import { useSeamlyAppContainerClassNames } from 'ui/hooks/component-helper-hooks'
11
11
  import { useSeamlyLayoutMode } from 'ui/hooks/seamly-state-hooks'
12
+ import { useGeneratedId } from 'ui/hooks/utility-hooks'
12
13
 
13
14
  const Chat = forwardRef<
14
15
  HTMLElement,
@@ -18,6 +19,7 @@ const Chat = forwardRef<
18
19
  const { namespace, layoutMode } = useConfig()
19
20
  const { isInline } = useSeamlyLayoutMode()
20
21
  const appContainerClassNames = useSeamlyAppContainerClassNames()
22
+ const headingId = useGeneratedId()
21
23
  const userHasResponded = useUserHasResponded()
22
24
  const { t } = useI18n()
23
25
 
@@ -55,8 +57,15 @@ const Chat = forwardRef<
55
57
  onKeyDown={onKeyDownHandler}
56
58
  tabIndex={-1}
57
59
  ref={forwardedRef}
58
- aria-label={t('chat.srLabel')}
60
+ role={layoutMode === 'window' ? 'dialog' : undefined}
61
+ aria-labelledby={headingId}
59
62
  >
63
+ <h2
64
+ className={className('chat__title', 'visually-hidden')}
65
+ id={headingId}
66
+ >
67
+ {t('chat.srLabel')}
68
+ </h2>
60
69
  <div className={className('chat-wrapper')}>{children}</div>
61
70
  {layoutMode === 'inline' && isOpen && <Suggestions isAside={true} />}
62
71
  </section>
@@ -11,6 +11,7 @@ export default function TranscriptForm({ controlName, describedById }) {
11
11
  id="email-id"
12
12
  name={controlName}
13
13
  type="email"
14
+ autocomplete="email"
14
15
  className={className('transcript__input')}
15
16
  aria-describedby={describedById}
16
17
  labelClass={className('label')}
@@ -2,7 +2,7 @@
2
2
 
3
3
  /** SET TEXT COLOR */
4
4
  @function set-text-color($color) {
5
- @if color.lightness($color) > 66.66 {
5
+ @if color.channel($color, 'lightness', $space: hsl) > 66.66 {
6
6
  @return $grey-e;
7
7
  } @else {
8
8
  @return $white;
@@ -11,7 +11,7 @@
11
11
 
12
12
  /** SET BORDER COLOR */
13
13
  @function set-border-color($color) {
14
- @if color.lightness($color) > 66.66 {
14
+ @if color.channel($color, 'lightness', $space: hsl) > 66.66 {
15
15
  @return $grey-c;
16
16
  } @else {
17
17
  @return $white;