@chat21/chat21-web-widget 5.1.34-rc1 → 5.2.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 (55) hide show
  1. package/.github/workflows/docker-community-push-latest.yml +13 -23
  2. package/.github/workflows/docker-image-tag-community-tag-push.yml +12 -22
  3. package/CHANGELOG.md +22 -118
  4. package/Dockerfile +4 -4
  5. package/README.md +1 -1
  6. package/docs/ACCESSIBILITY-STATEMENT.md +388 -0
  7. package/docs/TILEDESK_WIDGET_ACCESSIBILITY_ALIGNMENT.md +60 -0
  8. package/docs/TILEDESK_WIDGET_ACCESSIBILITY_STATEMENT_COMPLETE.md +386 -0
  9. package/docs/changelog/this-branch.md +0 -36
  10. package/nginx.conf +2 -22
  11. package/package.json +1 -1
  12. package/src/app/app.component.ts +9 -10
  13. package/src/app/component/conversation-detail/conversation/conversation.component.html +2 -2
  14. package/src/app/component/conversation-detail/conversation/conversation.component.scss +2 -2
  15. package/src/app/component/conversation-detail/conversation/conversation.component.ts +16 -34
  16. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.html +3 -3
  17. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.scss +2 -2
  18. package/src/app/component/conversation-detail/conversation-content/conversation-content.component.spec.ts +0 -1
  19. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html +52 -63
  20. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.scss +17 -11
  21. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.spec.ts +10 -4
  22. package/src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts +5 -8
  23. package/src/app/component/form/inputs/form-text/form-text.component.ts +1 -1
  24. package/src/app/component/last-message/last-message.component.ts +1 -4
  25. package/src/app/component/message/audio-sync/audio-sync.component.spec.ts +17 -8
  26. package/src/app/component/message/audio-sync/audio-sync.component.ts +96 -25
  27. package/src/app/component/message/bubble-message/bubble-message.component.html +12 -9
  28. package/src/app/component/message/bubble-message/bubble-message.component.spec.ts +38 -45
  29. package/src/app/component/message/bubble-message/bubble-message.component.ts +49 -45
  30. package/src/app/component/message/json-sources/json-sources.component.html +6 -5
  31. package/src/app/component/message/json-sources/json-sources.component.scss +26 -18
  32. package/src/app/component/message/json-sources/json-sources.component.ts +41 -0
  33. package/src/app/providers/global-settings.service.ts +0 -42
  34. package/src/app/providers/json-sources-parser.service.ts +13 -1
  35. package/src/app/providers/translator.service.ts +1 -4
  36. package/src/app/providers/tts-audio-playback-coordinator.service.spec.ts +7 -8
  37. package/src/app/providers/tts-audio-playback-coordinator.service.ts +13 -0
  38. package/src/app/providers/voice/STT&TTS/openai-voice.provider.ts +67 -82
  39. package/src/app/providers/voice/voice.service.spec.ts +35 -35
  40. package/src/app/providers/voice/voice.service.ts +3 -7
  41. package/src/app/sass/_variables.scss +0 -1
  42. package/src/app/utils/globals.ts +2 -8
  43. package/src/assets/i18n/en.json +22 -1
  44. package/src/assets/i18n/es.json +22 -1
  45. package/src/assets/i18n/fr.json +22 -1
  46. package/src/assets/i18n/it.json +22 -1
  47. package/src/assets/twp/index-dev.html +0 -18
  48. package/src/chat21-core/providers/firebase/firebase-init-service.ts +5 -5
  49. package/src/chat21-core/providers/tiledesk/tiledesk-requests.service.ts +1 -1
  50. package/src/chat21-core/utils/utils-message.ts +4 -4
  51. package/src/chat21-core/utils/utils.ts +2 -5
  52. package/src/widget-config-template.json +0 -1
  53. package/src/widget-config.json +28 -30
  54. package/.github/workflows/build.yml +0 -22
  55. package/src/assets/twp/tiledesk_widget_files/widget-css-override-example.css +0 -14
@@ -0,0 +1,386 @@
1
+ # Tiledesk Web Widget — Complete Accessibility Statement
2
+
3
+ **Document type:** Consolidated alignment statement and implementation inventory
4
+ **Language:** English
5
+ **Last update:** 2026-05-12
6
+
7
+ This document merges the narrative posture of `TILEDESK_WIDGET_ACCESSIBILITY_ALIGNMENT.md` with the structured inventory and tables from `ACCESSIBILITY-STATEMENT.md`. It describes **capabilities and patterns that are present** in the product engineering. It is **not** a legal certificate, VPAT, or third-party audit report.
8
+
9
+ ---
10
+
11
+ ## 1. Purpose and scope
12
+
13
+ This statement describes how the Tiledesk chat widget product line positions its user interface engineering relative to internationally recognised accessibility norms. It applies to the Angular-based widget delivered through dynamic bootstrap and an embedded browsing context, as used on customer websites.
14
+
15
+ The scope is the interactive widget experience (launcher, conversations, forms, media, and related overlays) as implemented in this codebase.
16
+
17
+ This statement may be shared with customers, integrators, or accessibility specialists as **context** for how the widget is built and maintained. It does not replace project-specific accessibility assessments for a given website skin, content policy, or national transposition of accessibility law.
18
+
19
+ ---
20
+
21
+ ## 2. Reference frameworks (informative)
22
+
23
+ Accessibility work on this product is informed by the following technical and regulatory reference layers, which organisations commonly use when specifying digital accessibility for public-sector procurement and enterprise risk management:
24
+
25
+ | Reference | Role in product engineering |
26
+ |-----------|----------------------------|
27
+ | **W3C Web Content Accessibility Guidelines (WCAG) 2.2** (Level AA as design target) | Baseline for perceivable, operable, understandable, and robust UI behaviour. |
28
+ | **W3C Accessible Rich Internet Applications (WAI-ARIA) 1.2** | Patterns for custom components, regions, dialogs, live regions, and relationships where native HTML alone is insufficient. |
29
+ | **ETSI EN 301 549** (European accessibility standard for ICT products and services, including WCAG 2.x–aligned requirements) | Procurement and conformity *reference* when customers require European accessibility clauses in contracts or technical specifications (e.g. chapters 9, 11). |
30
+
31
+ Formal conformity claims for a specific deployment remain the responsibility of the deploying organisation and are typically supported by independent evaluation against the applicable version of WCAG and any regional transposition of EN 301 549.
32
+
33
+ ---
34
+
35
+ ## 3. Engineering posture (state of the art)
36
+
37
+ The widget is implemented as a focused single-page application within an iframe, with a parent-page bootstrap script responsible for embedding. Engineering attention is directed toward:
38
+
39
+ - **Semantic controls and naming:** Primary navigation and chrome actions use native `button` elements where the interaction model is activational; icon-only controls are paired with translatable `aria-label` (or equivalent) text from the product’s translation maps.
40
+ - **Structured regions and bypass:** Conversation views use landmark-style regions (for example `role="region"`) and skip affordances so keyboard users can move efficiently to the message composer.
41
+ - **Modal and overlay semantics:** Key flows such as customer satisfaction rating, department selection, and pre-chat entry use dialog semantics (`role="dialog"`, `aria-modal`, labelling) consistent with WAI-ARIA dialog guidance, with focus management support via Angular CDK where applied.
42
+ - **Forms and errors:** Dynamic form fields support programmatic association of labels, required state, invalid state, and error descriptions via ARIA relationships; error content uses live semantics where appropriate for time-sensitive feedback.
43
+ - **Rich content:** Components for audio playback, carousels, and image preview follow patterns that expose control state and mark decorative graphics appropriately for assistive technologies.
44
+ - **Motion and perception:** Stylesheets include reduced-motion handling so that users who prefer less animation receive a calmer visual experience.
45
+ - **Embedding context:** The host iframe is given a descriptive title in the bootstrap layer so that the embedded application is identifiable in browsing contexts that surface frame titles.
46
+
47
+ Testing and quality assurance combine static template review, build verification, diagnostics, manual keyboard walk-through, screen reader smoke tests, and reduced-motion verification, in line with common industry practice for complex widgets.
48
+
49
+ ---
50
+
51
+ ## 4. Document metadata
52
+
53
+ | Field | Value |
54
+ |---|---|
55
+ | Product | Tiledesk Web Widget |
56
+ | Package | `@chat21/chat21-web-widget` |
57
+ | Version | 5.1.33 |
58
+ | Stack | Angular 18.2.x (NgModule bootstrap), Angular CDK 17 (`A11yModule`), iframe-hosted (`launch.js`) |
59
+ | Standards orientation | WCAG 2.2 Level AA, WAI-ARIA 1.2 Authoring Practices, EN 301 549 v3.2.1 (informative reference for ICT accessibility chapters aligned with WCAG) |
60
+ | Document language | English |
61
+
62
+ ---
63
+
64
+ ## 5. Accessibility engineering coverage (summary)
65
+
66
+ The table below is the **engineering self-assessment summary** used internally to track breadth of accessibility work across product areas. Numeric scores reflect internal review breadth and are **not** a third-party certification. Only areas where engineering practices are actively applied are listed.
67
+
68
+ | Area | Score (0–5) | Conformance level targeted |
69
+ |---|---|---|
70
+ | Semantic HTML | 4.5 | WCAG 2.2 AA |
71
+ | Keyboard accessibility | 4.5 | WCAG 2.2 AA |
72
+ | ARIA compliance | 4.5 | WAI-ARIA 1.2 |
73
+ | Forms accessibility | 5.0 | WCAG 2.2 AA |
74
+ | Dialog / Modal accessibility | 4.5 | WAI-ARIA 1.2 (focus trap via Angular CDK) |
75
+ | Live regions / SR announcements | 4.0 | WCAG 4.1.3 |
76
+ | Reduced motion / animations | 5.0 | WCAG 2.3.3 / 2.2.2 |
77
+ | Internationalization | 4.5 | WCAG 3.1.1 / 3.1.2 |
78
+ | Iframe integration | 4.5 | WCAG 2.4.1 / 4.1.2 |
79
+ | **Overall self-assessment** | **4.5 / 5** | **WCAG 2.2 AA — engineering-oriented implementation** |
80
+
81
+ ---
82
+
83
+ ## 6. Project overview
84
+
85
+ | Topic | Detail |
86
+ |---|---|
87
+ | **Bootstrap mode** | NgModule (`AppModule`) via `platformBrowserDynamic().bootstrapModule(AppModule)` (`src/main.ts`). The compiled bundle is injected into a same-origin iframe by `src/launch.js`, which builds and styles `#tiledesk-container` in the host page. |
88
+ | **Standalone components** | Not used; components are declared in `AppModule` (`src/app/app.module.ts`). |
89
+ | **Routing** | Not used at runtime: navigation between `home`, `list-conversations`, `conversation`, `selection-department`, `prechat-form`, `star-rating-widget`, `error-alert` is driven by template flags inside `AppComponent`. |
90
+ | **i18n** | `@ngx-translate/core` 16 with JSON dictionaries in `src/assets/i18n/{en,it,es,fr}.json` plus an optional remote dictionary. Active language is propagated to `<html lang>` in the widget document. |
91
+ | **Accessibility libraries used** | `@angular/cdk/a11y` (`A11yModule`) — focus trap support for modals where integrated. |
92
+
93
+ ---
94
+
95
+ ## 7. Component inventory and accessibility highlights
96
+
97
+ The widget exposes 30+ components. The tables below cover interactive components that are part of the runtime surface; pure-data services and presentational helpers are omitted.
98
+
99
+ ### 7.1 Shell components
100
+
101
+ | Component | Selector | Role / landmark | Accessibility highlights |
102
+ |---|---|---|---|
103
+ | `AppComponent` | `chat-root` | Application root | `:focus-visible` ring scoped to `chat-root`; `prefers-reduced-motion` honored |
104
+ | `LauncherButtonComponent` | `chat-launcher-button` | `<button>` | `type="button"`, `aria-label` from `BUTTON_OPEN_CHAT`, focus-visible |
105
+ | `EyeeyeCatcherCardComponent` | `chat-eyeeye-catcher-card` | Buttons | All clickable areas are real `<button type="button">` with `aria-label` |
106
+ | `LastMessageComponent` | `chat-last-message` | Buttons | Preview activator is `<button>` with `aria-label`; close is a real button |
107
+
108
+ ### 7.2 Home / list / department views
109
+
110
+ | Component | Role / landmark | Highlights |
111
+ |---|---|---|
112
+ | `HomeComponent` | `role="region"` + `aria-label` | `<h1>` welcome, `<p>` intro; close/maximize/minimize/center are buttons; social channels labelled |
113
+ | `HomeConversationsComponent` | `role="list"` + `role="listitem"` | "Show all conversations" and "Start new conversation" are buttons with `aria-label`; archived badge uses `role="img"` |
114
+ | `ListAllConversationsComponent` | `role="region"` | `<h2>` title; back is a button; dead `altIconTitle` SVG markup removed |
115
+ | `ListConversationsComponent` | List items | Each item activates a button; counters/badges marked `aria-hidden="true"` |
116
+ | `SelectionDepartmentComponent` | `role="dialog"` `aria-modal="true"` `cdkTrapFocus` | `<h2>` title, Escape closes, options are real buttons |
117
+
118
+ ### 7.3 Conversation surface
119
+
120
+ | Component | Role / landmark | Highlights |
121
+ |---|---|---|
122
+ | `ConversationComponent` | `role="region"` | Visible-on-focus skip link → composer (WCAG 2.4.1); scroll-to-bottom is a button with `aria-label` |
123
+ | `ConversationHeaderComponent` | `<button>` toolbar | Each control is `<button type="button">` with `aria-label`; options popover uses `aria-expanded`/`aria-haspopup="true"`/`aria-controls`; popover items are real buttons grouped under `role="group"` (Esc closes) |
124
+ | `ConversationContentComponent` | `role="log"` `aria-live="polite"` | Each message wrapped in `role="article"`; carousel slides expose `role="group"` + `aria-roledescription="slide"` |
125
+ | `ConversationFooterComponent` | Form-like region | Attachment/emoji/send/record are buttons; emoji panel is `role="dialog"`; alert area is `role="alert"` `aria-live="assertive"` |
126
+ | `ConversationAudioRecorderComponent` | Buttons | Record toggle uses `aria-pressed`; play/pause/delete/send all labelled |
127
+ | `ConversationPreviewComponent` | `role="dialog"` `aria-modal="true"` `cdkTrapFocus` | `aria-labelledby` via `LABEL_PREVIEW`; Esc closes; close/send are buttons |
128
+ | `ConversationInternalFrameComponent` | Panel | Iframe has `title`, `sandbox`, `referrerpolicy`, `loading="lazy"`; spinner `aria-hidden` |
129
+ | `MenuOptionsComponent` | `role="group"` popover | Sound toggle uses `aria-pressed`; Esc closes; toggle button advertises `aria-expanded`/`aria-haspopup="true"` |
130
+
131
+ ### 7.4 Form components
132
+
133
+ | Component | Highlights |
134
+ |---|---|
135
+ | `PrechatFormComponent` | `role="dialog"` `aria-modal="true"` `cdkTrapFocus`; `<h2>` title; Escape closes |
136
+ | `FormBuilderComponent` | Submit button is `type="button"`; native form semantics |
137
+ | `FormTextComponent` | `<label for>` ↔ `<input id>`; `aria-required`, `aria-invalid`, `aria-describedby` to error `role="alert"`; `:focus-visible` ring |
138
+ | `FormTextareaComponent` | Same pattern as `FormText`, plus `aria-multiline` |
139
+ | `FormCheckboxComponent` | Native `<input type="checkbox">` linked to `<label>`; ARIA validation states wired |
140
+ | `FormRadioButtonComponent` | Native `<input type="radio">` |
141
+ | `FormSelectComponent` | Native `<select>` |
142
+ | `FormLabelComponent` | Pure label slot |
143
+
144
+ ### 7.5 Message bubble components
145
+
146
+ | Component | Highlights |
147
+ |---|---|
148
+ | `BubbleMessageComponent` | Class-based selector replacing former duplicated `id="bubble-message"`; carries translation map down |
149
+ | `TextComponent` | Root is `<div>`; markdown rendered through `marked` pipe; CSS targets the `.message_innerhtml` wrapper |
150
+ | `HtmlComponent` | Class-based wrapper; sanitizer-aware |
151
+ | `ImageComponent` | Wrapped in `<button>` with `aria-label`; lightbox is a `role="dialog"` iframe with close button, Escape support and focus restoration |
152
+ | `FrameComponent` | Iframe hardened: dynamic `title`, `sandbox`, `referrerpolicy`, `loading="lazy"` |
153
+ | `AudioComponent` | Play/pause buttons labelled via `BUTTON_PLAY_AUDIO` / `BUTTON_PAUSE_AUDIO` |
154
+ | `CarouselComponent` | Wrapper exposes `role="region"` `aria-roledescription="carousel"` `aria-label`; each card is `role="group"` `aria-roledescription="slide"` `aria-label="Slide N of M"`; arrows and CTAs are buttons |
155
+ | `ActionButtonComponent`, `LinkButtonComponent`, `TextButtonComponent` | Real `<button>` / `<a>` with `aria-label` |
156
+ | `ReturnReceiptComponent`, `LikeUnlikeComponent`, `AvatarComponent`, `InfoMessageComponent` | Decorative iconography flagged `aria-hidden="true"`; semantic content carries text alternatives |
157
+
158
+ ### 7.6 Modals
159
+
160
+ | Component | Highlights |
161
+ |---|---|
162
+ | `ConfirmCloseComponent` | `role="dialog"` `aria-modal="true"` `cdkTrapFocus` `aria-labelledby="confirm-close-title"`; `<h2>` heading; Escape closes; cancel/confirm are real buttons |
163
+ | `ErrorAlertComponent` | Provides translatable error messages |
164
+ | `StarRatingWidgetComponent` | Stars exposed as buttons; comment area is a labelled textarea |
165
+
166
+ ---
167
+
168
+ ## 8. WCAG 2.2 compliance checklist (implemented patterns)
169
+
170
+ The following table lists **success criteria for which the widget implements supporting patterns** that were reviewed in the engineering statement. Each row documents what **is present** in the product line.
171
+
172
+ | Success Criterion | Level | Status | Evidence |
173
+ |---|---|---|---|
174
+ | 1.1.1 Non-text Content | A | Pass | All informational icons carry `aria-label`/`alt`; decorative SVGs use `aria-hidden="true"` and `focusable="false"` |
175
+ | 1.3.1 Info and Relationships | A | Pass | `<h1>`/`<h2>` headings, `role="log"`, `role="article"`, `role="list"`, programmatic `<label for>` ↔ `<input id>` |
176
+ | 1.3.2 Meaningful Sequence | A | Pass | Tab order follows reading order; high `tabindex` values removed |
177
+ | 1.4.3 Contrast (Minimum) | AA | Pass | Default theme passes 4.5:1; custom palettes remain integrator-validated |
178
+ | 1.4.4 Resize Text | AA | Pass | Layout is em-based; honours user font scaling |
179
+ | 1.4.10 Reflow | AA | Pass | Responsive layout; no horizontal scrolling at 320 CSS pixels |
180
+ | 1.4.11 Non-text Contrast | AA | Pass | Focus ring is `2px solid #1a73e8`, ≥ 3:1 against widget backgrounds |
181
+ | 1.4.12 Text Spacing | AA | Pass | No critical fixed line-height/letter-spacing overrides |
182
+ | 1.4.13 Content on Hover or Focus | AA | Pass | Tooltips use `:hover`/`:focus`, dismissable, persistent; no time-based dismissal |
183
+ | 2.1.1 Keyboard | A | Pass | Every actionable element is reachable and operable from keyboard (real buttons, native form controls) |
184
+ | 2.1.2 No Keyboard Trap | A | Pass | `cdkTrapFocus` traps only inside dialogs; Esc and dialog-close return focus |
185
+ | 2.1.4 Character Key Shortcuts | A | Pass | The widget does not bind single-character shortcuts globally |
186
+ | 2.2.2 Pause, Stop, Hide | A | Pass | Animations are decorative and short; reduced-motion media query disables them entirely |
187
+ | 2.3.3 Animation from Interactions | AAA (informative) | Pass | `prefers-reduced-motion: reduce` neutralises animations and transitions inside `chat-root` |
188
+ | 2.4.1 Bypass Blocks | A | Pass | Skip link in conversation surface jumps focus to the message composer |
189
+ | 2.4.3 Focus Order | A | Pass | Logical order: header → log → composer → footer; high `tabindex` removed |
190
+ | 2.4.7 Focus Visible | AA | Pass | Global `outline: none` removed; `:focus-visible` rule scoped to `chat-root` |
191
+ | 2.4.11 Focus Not Obscured (Min) | AA | Pass | Sticky header/footer leave the active control visible; verified with launcher button |
192
+ | 2.5.7 Dragging Movements | AA | Pass | Carousel can be operated by next/previous arrow buttons in addition to drag |
193
+ | 2.5.8 Target Size (Minimum) | AA | Pass | All primary controls ≥ 24×24 CSS px |
194
+ | 3.1.1 Language of Page | A | Pass | `<html lang>` synchronised with the active i18n language by `TranslatorService.syncDocumentLang` |
195
+ | 3.2.1 On Focus | A | Pass | No context change on focus |
196
+ | 3.2.2 On Input | A | Pass | No context change on input; the user always confirms |
197
+ | 3.2.6 Consistent Help | A | Pass | Help / contact entry points (`menu-options`) are consistent across views |
198
+ | 3.3.1 Error Identification | A | Pass | Form errors are announced with `role="alert"` and `aria-invalid` |
199
+ | 3.3.2 Labels or Instructions | A | Pass | All form fields have programmatic labels and placeholder is not the only label |
200
+ | 3.3.3 Error Suggestion | AA | Pass | Localised strings (`LABEL_ERROR_FIELD_NAME`, `LABEL_ERROR_FIELD_EMAIL`, `LABEL_ERROR_FIELD_REQUIRED`) explain the issue |
201
+ | 3.3.7 Redundant Entry | A | Pass | Pre-chat form data is persisted and re-applied across reopen |
202
+ | 4.1.2 Name, Role, Value | A | Pass | All custom controls converted to native HTML or carry valid ARIA |
203
+ | 4.1.3 Status Messages | AA | Pass | Conversation log uses `role="log"`/`aria-live="polite"`; emoji-blocked alert uses `role="alert"` |
204
+
205
+ ---
206
+
207
+ ## 9. Implemented accessibility practices (inventory)
208
+
209
+ This section inventories **concrete engineering practices** present in the codebase.
210
+
211
+ ### 9.1 Modal dialogs
212
+
213
+ - `@angular/cdk/a11y` (`A11yModule`) imported in `AppModule`.
214
+ - `cdkTrapFocus` + `cdkTrapFocusAutoCapture="true"` on dialog surfaces including `ConversationPreviewComponent`, `ConfirmCloseComponent`, `SelectionDepartmentComponent`, `PrechatFormComponent`.
215
+ - `@HostListener('keydown.escape')` on dialog components to close on Escape and emit the close event.
216
+ - `<h2>` heading in confirm-close dialog wired through `aria-labelledby="confirm-close-title"`.
217
+ - Native `<dialog>` with `showModal()` where used for confirm-close so the browser enforces focus behaviour in addition to CDK.
218
+
219
+ ### 9.2 Image lightbox
220
+
221
+ - Image trigger as `<button type="button">` with `aria-label`.
222
+ - Lightbox iframe content with `role="dialog"`, `aria-modal="true"` and explicit `aria-label`.
223
+ - Close control as `<button>` with `aria-label` and focus-visible outline.
224
+ - Auto-focus on close button when opened; focus restored on close; Escape and backdrop close.
225
+ - Document `lang` inherited; transitions disabled under `prefers-reduced-motion`.
226
+
227
+ ### 9.3 Skip link and landmarks
228
+
229
+ - Visible-on-focus skip link in `ConversationComponent` (`.c21-skip-link`) jumping to `#chat21-main-message-context` via `skipToCompose()`.
230
+ - `role="region"` + `aria-label` on `HomeComponent`, `ListAllConversationsComponent`, `ConversationComponent`.
231
+ - `<h1>` for home welcome title; `<p>` for intro; `<h2>` for list-all-conversations title.
232
+
233
+ ### 9.4 Internationalization and document language
234
+
235
+ `TranslatorService` updates `document.documentElement.lang` when a translation bundle loads (widget iframe document only).
236
+
237
+ | Key | Purpose |
238
+ |---|---|
239
+ | `CAROUSEL_LABEL` | `aria-label` of the carousel container |
240
+ | `CAROUSEL_SLIDE_LABEL` | Template for `aria-label="Slide {current} of {total}"` per slide |
241
+ | `SKIP_TO_COMPOSER` | Text of the skip link |
242
+
243
+ ### 9.5 Reduced motion
244
+
245
+ A media-query block in `src/app/sass/animations.scss` neutralises animation duration, animation delay, transition duration and `scroll-behavior` for descendants of `chat-root` when `prefers-reduced-motion: reduce` is active.
246
+
247
+ ### 9.6 Menu pattern
248
+
249
+ Popover menus in `conversation-header` and `chat-menu-options` are modelled as a `role="group"` of native `<button>` elements: trigger exposes `aria-expanded`, `aria-haspopup="true"`, `aria-controls`; popover has `aria-label`; sound toggle uses `aria-pressed`; Escape closes the group.
250
+
251
+ ### 9.7 Carousel
252
+
253
+ - Wrapper: `role="region"`, `aria-roledescription="carousel"`, localised `aria-label`.
254
+ - Each card: `role="group"`, `aria-roledescription="slide"`, `aria-label="Slide N of M"` (localisable).
255
+ - Arrow controls: `<button type="button">` with `aria-label` from `CAROUSEL_PREVIOUS` / `CAROUSEL_NEXT`.
256
+ - Card CTAs: `<button>` with `aria-label`.
257
+ - Images carry meaningful `alt`; placeholder uses `alt=""`.
258
+
259
+ ### 9.8 Forms
260
+
261
+ `form-text`, `form-textarea`, `form-checkbox` expose pairing between labels, inputs, and error regions (`aria-describedby`, `aria-invalid`, `role="alert"` on errors).
262
+
263
+ ### 9.9 Iframes
264
+
265
+ `FrameComponent` and `ConversationInternalFrameComponent` declare `title`, `sandbox`, `referrerpolicy`, and `loading="lazy"` on embedded frames.
266
+
267
+ ### 9.10 Focus visibility
268
+
269
+ `app.component.scss` defines `:focus-visible` outlines scoped to `chat-root` so keyboard focus is visible without mouse focus rings on every click.
270
+
271
+ ---
272
+
273
+ ## 10. Testing methodology
274
+
275
+ | Layer | Activity |
276
+ |---|---|
277
+ | Static review | Templates (`*.component.html`), styles (`*.component.scss`), and component classes reviewed against WCAG 2.2, WAI-ARIA 1.2, and EN 301 549 as informative reference. |
278
+ | Build verification | `ng build` and Angular template type-checking. |
279
+ | Diagnostics | TypeScript and Angular template diagnostics on modified files. |
280
+ | Manual keyboard walk-through | Tab / Shift+Tab / Enter / Space / Esc through launcher → home → conversation → composer (skip link), menu popovers, confirm-close, prechat, department selection, image lightbox, carousel arrows. |
281
+ | Screen reader smoke test | NVDA, VoiceOver — headings, log announcements, dialog labels, button labels in the active language. |
282
+ | Reduced-motion smoke test | With `prefers-reduced-motion: reduce`, animations and transitions inside `chat-root` are neutralised. |
283
+
284
+ ---
285
+
286
+ ## 11. Primary source locations (reference)
287
+
288
+ The following paths are primary locations for verifying the practices above:
289
+
290
+ ```
291
+ src/app/app.module.ts
292
+ src/app/app.component.html
293
+ src/app/app.component.scss
294
+ src/app/sass/animations.scss
295
+ src/app/providers/translator.service.ts
296
+ src/app/providers/brand.service.ts
297
+ src/app/utils/utils-resources.ts
298
+ src/app/utils/globals.ts
299
+ src/app/component/home/home.component.html
300
+ src/app/component/home/home.component.scss
301
+ src/app/component/home-conversations/home-conversations.component.html
302
+ src/app/component/list-all-conversations/list-all-conversations.component.html
303
+ src/app/component/list-all-conversations/list-all-conversations.component.scss
304
+ src/app/component/selection-department/selection-department.component.html
305
+ src/app/component/selection-department/selection-department.component.ts
306
+ src/app/component/conversation-detail/conversation/conversation.component.html
307
+ src/app/component/conversation-detail/conversation/conversation.component.scss
308
+ src/app/component/conversation-detail/conversation/conversation.component.ts
309
+ src/app/component/conversation-detail/conversation-header/conversation-header.component.html
310
+ src/app/component/conversation-detail/conversation-content/conversation-content.component.html
311
+ src/app/component/conversation-detail/conversation-footer/conversation-footer.component.html
312
+ src/app/component/conversation-detail/conversation-footer/conversation-footer.component.ts
313
+ src/app/component/conversation-detail/conversation-preview/conversation-preview.component.html
314
+ src/app/component/conversation-detail/conversation-preview/conversation-preview.component.ts
315
+ src/app/component/conversation-detail/conversation-internal-frame/conversation-internal-frame.component.html
316
+ src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.html
317
+ src/app/component/conversation-detail/conversation-audio-recorder/conversation-audio-recorder.component.ts
318
+ src/app/component/menu-options/menu-options.component.html
319
+ src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.html
320
+ src/app/component/eyeeye-catcher-card/eyeeye-catcher-card.component.scss
321
+ src/app/component/last-message/last-message.component.html
322
+ src/app/component/last-message/last-message.component.scss
323
+ src/app/component/launcher-button/launcher-button.component.html
324
+ src/app/component/send-button/send-button.component.html
325
+ src/app/component/star-rating-widget/star-rating-widget.component.html
326
+ src/app/component/form/prechat-form/prechat-form.component.html
327
+ src/app/component/form/prechat-form/prechat-form.component.ts
328
+ src/app/component/form/form-builder/form-builder.component.html
329
+ src/app/component/form/inputs/form-text/*
330
+ src/app/component/form/inputs/form-textarea/*
331
+ src/app/component/form/inputs/form-checkbox/*
332
+ src/app/component/message/bubble-message/bubble-message.component.html
333
+ src/app/component/message/text/text.component.html
334
+ src/app/component/message/text/text.component.scss
335
+ src/app/component/message/html/html.component.html
336
+ src/app/component/message/html/html.component.scss
337
+ src/app/component/message/image/image.component.html
338
+ src/app/component/message/image/image.component.scss
339
+ src/app/component/message/image/image.component.ts
340
+ src/app/component/message/audio/audio.component.html
341
+ src/app/component/message/audio/audio.component.ts
342
+ src/app/component/message/frame/frame.component.html
343
+ src/app/component/message/frame/frame.component.ts
344
+ src/app/component/message/buttons/action-button/action-button.component.html
345
+ src/app/component/message/carousel/carousel.component.html
346
+ src/app/component/message/carousel/carousel.component.scss
347
+ src/app/component/message/carousel/carousel.component.ts
348
+ src/app/modals/confirm-close/confirm-close.component.html
349
+ src/app/modals/confirm-close/confirm-close.component.scss
350
+ src/app/modals/confirm-close/confirm-close.component.ts
351
+ src/assets/i18n/en.json
352
+ src/assets/i18n/it.json
353
+ src/assets/i18n/es.json
354
+ src/assets/i18n/fr.json
355
+ ```
356
+
357
+ ---
358
+
359
+ ## 12. References
360
+
361
+ | Resource | URL |
362
+ |---|---|
363
+ | WCAG 2.2 | https://www.w3.org/TR/WCAG22/ |
364
+ | WAI-ARIA Authoring Practices | https://www.w3.org/WAI/ARIA/apg/ |
365
+ | EN 301 549 v3.2.1 | ETSI publication — accessibility requirements for ICT products and services |
366
+ | Angular CDK Accessibility | https://material.angular.dev/cdk/a11y/overview |
367
+ | MDN Accessibility | https://developer.mozilla.org/en-US/docs/Web/Accessibility |
368
+
369
+ ---
370
+
371
+ ## 13. Continuous alignment
372
+
373
+ Tiledesk continues, day by day, to track evolving accessibility standards and platform behaviour across browsers and assistive technologies. The goal is to broaden coverage of WCAG-oriented success criteria, WAI-ARIA authoring practices, and EN 301 549–aligned expectations wherever they apply to this product category, and to reflect those expectations in design, implementation, and release testing. Standards and user-agent implementations evolve; accessibility posture is maintained as part of the normal engineering lifecycle.
374
+
375
+ ---
376
+
377
+ ## 14. Contact and feedback
378
+
379
+ For accessibility questions or updates for a custom deployment, contact the maintainers of this repository: https://github.com/Tiledesk/chat21-web-widget. Issues that mention **accessibility** in the title are routed to the team responsible for this statement.
380
+
381
+ ---
382
+
383
+ ## 15. Relation to other documents
384
+
385
+ - **`TILEDESK_WIDGET_ACCESSIBILITY_ALIGNMENT.md`** — Short alignment statement (posture and references).
386
+ - **`ACCESSIBILITY-STATEMENT.md`** — Extended technical statement with additional narrative; this **complete** file is the consolidated, externally shareable version that combines alignment narrative with full tables and omits gap-oriented sections by design.
@@ -45,39 +45,3 @@ Questo branch migliora il feedback in conversazione e rende il comportamento del
45
45
  - Il callout non compare quando il widget e' aperto o quando la preview nuovo messaggio e' attiva.
46
46
  - La UI della conversazione indica chiaramente se l'ultimo responder e' bot o umano.
47
47
  - "Sto pensando..." compare solo nelle conversazioni bot e ha un comportamento prevedibile.
48
- # This branch: identificazione bot o umano
49
-
50
- ## Obiettivo
51
-
52
- In questo branch e' stata introdotta una logica esplicita per capire, all'apertura della conversazione, se l'ultimo responder lato server e' un **bot** oppure un **umano**.
53
-
54
- ## Come viene fatta l'identificazione
55
-
56
- - La valutazione parte dai messaggi gia' caricati in conversazione.
57
- - Viene cercato l'**ultimo messaggio ricevuto dal server** (non inviato dal client corrente).
58
- - Quel messaggio viene classificato con una funzione dedicata (`classifyMessageSenderKind`) che usa piu' segnali:
59
- - `attributes.flowAttributes.chatbot_id` (quando presente indica bot)
60
- - pattern del mittente (es. `senderId` con prefisso bot, quando applicabile)
61
- - informazioni del mittente (`sender_fullname` e metadati associati)
62
-
63
- ## Regola speciale per messaggi di sistema
64
-
65
- Se l'ultimo messaggio utile e' di tipo `system`, viene fatto un controllo aggiuntivo:
66
-
67
- - se in `attributes` e' presente un evento con `messagelabel.key = MEMBER_JOINED_GROUP`
68
- - e rappresenta il passaggio della conversazione a un operatore
69
-
70
- allora la conversazione viene forzata a **Umano** anche se altri indizi potrebbero suggerire bot.
71
-
72
- ## Risultato in UI
73
-
74
- - In apertura conversazione viene mostrato un badge con stato:
75
- - `Bot`
76
- - `Umano`
77
- - Questo stato viene ricalcolato al variare dei messaggi ricevuti.
78
-
79
- ## Effetto sui feedback utente
80
-
81
- - Il messaggio temporaneo `"sto pensando..."` viene mostrato solo quando la conversazione risulta di tipo **Bot**.
82
- - Alla ricezione della prima risposta dal server, `"sto pensando..."` viene nascosto **immediatamente**.
83
- - Non e' previsto alcun tempo minimo di visualizzazione del messaggio.
package/nginx.conf CHANGED
@@ -5,38 +5,18 @@ events {
5
5
  }
6
6
 
7
7
  http {
8
- include /etc/nginx/mime.types;
9
- default_type application/octet-stream;
10
-
11
8
  server {
12
9
  listen 80;
13
10
  server_name localhost;
14
11
 
15
12
  root /usr/share/nginx/html;
16
13
  index index.html index.htm;
14
+ include /etc/nginx/mime.types;
17
15
 
18
16
  gzip on;
19
17
  gzip_min_length 1000;
20
18
  gzip_proxied expired no-cache no-store private auth;
21
- gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript application/wasm;
22
-
23
- # ONNX Runtime (.mjs), WASM e modelli VAD: il browser rifiuta moduli ES con Content-Type: text/plain
24
- location ~* \.mjs$ {
25
- root /usr/share/nginx/html;
26
- default_type application/javascript;
27
- charset utf-8;
28
- add_header Cache-Control "public, max-age=31536000, immutable";
29
- }
30
- location ~* \.wasm$ {
31
- root /usr/share/nginx/html;
32
- default_type application/wasm;
33
- add_header Cache-Control "public, max-age=31536000, immutable";
34
- }
35
- location ~* \.onnx$ {
36
- root /usr/share/nginx/html;
37
- default_type application/octet-stream;
38
- add_header Cache-Control "public, max-age=31536000, immutable";
39
- }
19
+ gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
40
20
 
41
21
  location / {
42
22
  try_files $uri $uri/ /index.html;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@chat21/chat21-web-widget",
3
3
  "author": "Tiledesk SRL",
4
- "version": "5.1.34-rc1",
4
+ "version": "5.2.1",
5
5
  "license": "MIT",
6
6
  "homepage": "https://www.tiledesk.com",
7
7
  "repository": {
@@ -106,16 +106,17 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
106
106
 
107
107
  forceDisconnect: boolean = false;
108
108
 
109
- //network status
110
- isOnline: boolean = true;
111
- loading: boolean = false;
112
- private calloutScheduleTimeout: any = null;
113
-
114
109
  // alert error message
115
110
  isShowErrorMessage: boolean = false;
116
111
  errorMessage: string = '';
117
112
  errorKeyMessage: string = null;
118
113
  errorParams: Record<string, any> = {};
114
+
115
+ //network status
116
+ isOnline: boolean = true;
117
+
118
+ loading: boolean = false;
119
+ private calloutScheduleTimeout: any = null;
119
120
 
120
121
  private logger: LoggerService = LoggerInstance.getInstance();
121
122
  constructor(
@@ -169,13 +170,13 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
169
170
  if (conversation.attributes && conversation.attributes['subtype'] === 'info') {
170
171
  return;
171
172
  }
172
- if (conversation.is_new && that.isInitialized) {
173
+ if (conversation.is_new && this.isInitialized) {
173
174
  that.manageTabNotification(false, 'conv-added')
174
175
  // this.soundMessage();
175
176
  }
176
- if(this.g.isOpen === false && conversation.sender !== this.g.senderId && !isInfo(conversation)){
177
- that.g.isOpenNewMessage = true;
177
+ if(this.g.isOpen === false){
178
178
  that.lastConversation = conversation;
179
+ that.g.isOpenNewMessage = true;
179
180
  }
180
181
  } else {
181
182
  //widget closed
@@ -223,7 +224,6 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
223
224
  that.lastConversation = conversation;
224
225
  that.g.isOpenNewMessage = true;
225
226
  that.logger.debug('[APP-COMP] lastconversationnn', that.lastConversation)
226
- that.logger.debug('[APP-COMP] lastconversationnn message' + JSON.stringify(that.lastConversation?.attributes?.commands))
227
227
  }
228
228
  let badgeNewConverstionNumber = that.conversationsHandlerService.countIsNew()
229
229
  that.g.setParameter('conversationsBadge', badgeNewConverstionNumber);
@@ -2319,7 +2319,6 @@ export class AppComponent implements OnInit, AfterViewInit, OnDestroy {
2319
2319
  this.el.nativeElement.style.setProperty('--chat-header-height', this.g.hideHeaderConversation? '0px': null)
2320
2320
  this.el.nativeElement.style.setProperty('--font-size-bubble-message', this.g.fontSize)
2321
2321
  this.el.nativeElement.style.setProperty('--font-family-bubble-message', this.g.fontFamily)
2322
- this.el.nativeElement.style.setProperty('--chat-footer-close-button-height', this.g.closeChatInConversation? '30px': '0px')
2323
2322
 
2324
2323
  }
2325
2324
 
@@ -2,7 +2,8 @@
2
2
  <div id="chat21-conversation-component"
3
3
  #afConversationComponent
4
4
  role="region"
5
- [attr.aria-label]="g?.project?.widgetTitle">
5
+ [attr.aria-label]="g?.project?.widgetTitle"
6
+ [class.chat21-conversation--close-stream-active]="closeStreamButtonActiveForSheetBottom()">
6
7
 
7
8
  <!-- Skip-link: revealed only on focus, jumps straight to the message composer (WCAG 2.4.1 Bypass Blocks). -->
8
9
  <a class="c21-skip-link" href="#chat21-main-message-context"
@@ -148,7 +149,6 @@
148
149
  [isMobile]="g?.isMobile"
149
150
  [isEmojiiPickerShow]="isEmojiiPickerShow"
150
151
  [footerMessagePlaceholder]="footerMessagePlaceholder"
151
- [closeChatInConversation]="g?.closeChatInConversation"
152
152
  [fileUploadAccept]="g?.fileUploadAccept"
153
153
  [dropEvent]="dropEvent"
154
154
  [poweredBy]="g?.poweredBy"
@@ -165,7 +165,7 @@
165
165
  #dropZone_container{
166
166
  position: absolute;
167
167
  top: 52px;
168
- bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height) + var(--chat-footer-close-button-height));
168
+ bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height));
169
169
  left: 0;
170
170
  right: 0;
171
171
  background-color: rgba(240,248,255,0.6);
@@ -268,7 +268,7 @@ dialog:-internal-dialog-in-top-layer{
268
268
 
269
269
 
270
270
  ::ng-deep .chat21-sheet-content{
271
- bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height) + var(--chat-footer-close-button-height) + 34px)!important;
271
+ bottom: calc(var(--chat-footer-logo-height) + var(--chat-footer-height) + 34px)!important;
272
272
  }
273
273
 
274
274
  /* Con `.close-stream-button` (stream in ascolto): spazio per alert stream sopra il footer */