@coveo/atomic 3.61.4 → 3.62.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/custom-elements.json +886 -814
  2. package/dist/cjs/components/commerce/atomic-commerce-category-facet/atomic-commerce-category-facet.cjs +19 -7
  3. package/dist/cjs/components/common/atomic-generated-answer-content/atomic-generated-answer-content.cjs +23 -1
  4. package/dist/cjs/components/common/atomic-generated-answer-thread/atomic-generated-answer-thread.cjs +1 -1
  5. package/dist/cjs/components/common/atomic-generated-answer-thread-item/atomic-generated-answer-thread-item.cjs +3 -2
  6. package/dist/cjs/components/common/facets/category-facet/active-value.cjs +69 -0
  7. package/dist/cjs/components/common/facets/category-facet/selected-value.cjs +78 -0
  8. package/dist/cjs/components/common/facets/category-facet/value-link.cjs +5 -6
  9. package/dist/cjs/components/common/generated-answer/answer-announcement.cjs +73 -0
  10. package/dist/cjs/components/common/generated-answer/copy-button.cjs +2 -1
  11. package/dist/cjs/components/common/generated-answer/feedback-button.cjs +2 -1
  12. package/dist/cjs/components/common/generated-answer/generated-content/markdown-utils.cjs +1 -2
  13. package/dist/cjs/components/common/generated-answer/generated-content-container.cjs +6 -1
  14. package/dist/cjs/components/common/generated-answer/render-answer-content.cjs +5 -2
  15. package/dist/cjs/components/common/generated-answer/show-button.cjs +1 -0
  16. package/dist/cjs/components/insight/atomic-insight-generated-answer/atomic-insight-generated-answer.cjs +3 -1
  17. package/dist/cjs/components/search/atomic-category-facet/atomic-category-facet.cjs +19 -8
  18. package/dist/cjs/components/search/atomic-generated-answer/atomic-generated-answer.cjs +8 -1
  19. package/dist/cjs/global/environment.cjs +1 -1
  20. package/dist/cjs/versions.cjs +1 -1
  21. package/dist/esm/components/commerce/atomic-commerce-category-facet/atomic-commerce-category-facet.js +19 -7
  22. package/dist/esm/components/common/atomic-generated-answer-content/atomic-generated-answer-content.js +23 -1
  23. package/dist/esm/components/common/atomic-generated-answer-thread/atomic-generated-answer-thread.js +1 -1
  24. package/dist/esm/components/common/atomic-generated-answer-thread-item/atomic-generated-answer-thread-item.js +3 -2
  25. package/dist/esm/components/common/facets/category-facet/active-value.js +31 -0
  26. package/dist/esm/components/common/facets/category-facet/selected-value.js +30 -0
  27. package/dist/esm/components/common/facets/category-facet/value-link.js +5 -6
  28. package/dist/esm/components/common/generated-answer/answer-announcement.js +32 -0
  29. package/dist/esm/components/common/generated-answer/copy-button.js +2 -1
  30. package/dist/esm/components/common/generated-answer/feedback-button.js +2 -1
  31. package/dist/esm/components/common/generated-answer/generated-content/markdown-utils.js +1 -2
  32. package/dist/esm/components/common/generated-answer/generated-content-container.js +6 -1
  33. package/dist/esm/components/common/generated-answer/render-answer-content.js +5 -2
  34. package/dist/esm/components/common/generated-answer/show-button.js +1 -0
  35. package/dist/esm/components/insight/atomic-insight-generated-answer/atomic-insight-generated-answer.js +3 -1
  36. package/dist/esm/components/search/atomic-category-facet/atomic-category-facet.js +19 -8
  37. package/dist/esm/components/search/atomic-generated-answer/atomic-generated-answer.js +8 -1
  38. package/dist/esm/global/environment.js +1 -1
  39. package/dist/esm/versions.js +1 -1
  40. package/dist/types/components/commerce/atomic-commerce-category-facet/atomic-commerce-category-facet.d.ts +7 -1
  41. package/dist/types/components/common/atomic-generated-answer-content/atomic-generated-answer-content.d.ts +7 -0
  42. package/dist/types/components/common/facets/category-facet/active-value.d.ts +19 -0
  43. package/dist/types/components/common/facets/category-facet/selected-value.d.ts +22 -0
  44. package/dist/types/components/common/facets/category-facet/value-link.d.ts +0 -1
  45. package/dist/types/components/common/generated-answer/answer-announcement.d.ts +22 -0
  46. package/dist/types/components/common/generated-answer/generated-content-container.d.ts +1 -0
  47. package/dist/types/components/common/generated-answer/show-button.d.ts +1 -0
  48. package/dist/types/components/insight/atomic-insight-generated-answer/atomic-insight-generated-answer.d.ts +1 -0
  49. package/dist/types/components/search/atomic-category-facet/atomic-category-facet.d.ts +8 -2
  50. package/dist/types/components/search/atomic-generated-answer/atomic-generated-answer.d.ts +2 -0
  51. package/package.json +9 -9
  52. package/dist/cjs/components/common/facets/category-facet/parent-value-link.cjs +0 -47
  53. package/dist/esm/components/common/facets/category-facet/parent-value-link.js +0 -9
  54. package/dist/types/components/common/facets/category-facet/parent-value-link.d.ts +0 -6
@@ -174,6 +174,7 @@ class AtomicGeneratedAnswer extends external_lit_namespaceObject.LitElement {
174
174
  'generated-answer-content-background': this.areFollowUpsEnabled,
175
175
  'agent-scrollable': this.areFollowUpsEnabled
176
176
  })}
177
+ tabindex=${this.areFollowUpsEnabled ? '0' : external_lit_namespaceObject.nothing}
177
178
  >
178
179
  <article>${this.renderAnswerContent()}</article>
179
180
  </div>
@@ -198,6 +199,9 @@ class AtomicGeneratedAnswer extends external_lit_namespaceObject.LitElement {
198
199
  </div>
199
200
  `;
200
201
  }
202
+ get shouldAnnounceAnswerStatus() {
203
+ return !this.areFollowUpsEnabled;
204
+ }
201
205
  get hasNoAnswerGenerated() {
202
206
  return this.controller.hasNoAnswerGenerated;
203
207
  }
@@ -222,11 +226,13 @@ class AtomicGeneratedAnswer extends external_lit_namespaceObject.LitElement {
222
226
  async copyToClipboard(answer) {
223
227
  await this.controller.copyToClipboard(answer, ()=>{
224
228
  this.copied = true;
229
+ this.ariaCopyMessage.message = this.bindings.i18n.t('generated-answer-copied');
225
230
  setTimeout(()=>{
226
231
  this.copied = false;
227
232
  }, 2000);
228
233
  }, ()=>{
229
234
  this.copyError = true;
235
+ this.ariaCopyMessage.message = this.bindings.i18n.t('failed-to-copy-generated-answer');
230
236
  setTimeout(()=>{
231
237
  this.copyError = false;
232
238
  }, 2000);
@@ -421,7 +427,7 @@ class AtomicGeneratedAnswer extends external_lit_namespaceObject.LitElement {
421
427
  constructor(){
422
428
  super(), this.DEFAULT_COLLAPSED_HEIGHT = 16, this.MAX_COLLAPSED_HEIGHT = 32, this.MIN_COLLAPSED_HEIGHT = 9, this.REQUIRED_FIELDS_TO_INCLUDE_IN_CITATIONS = [
423
429
  'filetype'
424
- ], this.withToggle = false, this.collapsible = false, this.maxCollapsedHeight = this.DEFAULT_COLLAPSED_HEIGHT, this.disableCitationAnchoring = false, this.tabsIncluded = [], this.tabsExcluded = [], this.copied = false, this.copyError = false, this.ariaMessage = new accessibility_utils_cjs_namespaceObject.AriaLiveRegionController(this, 'generated-answer', false, true), this.ariaErrorMessage = new accessibility_utils_cjs_namespaceObject.AriaLiveRegionController(this, 'generated-answer-error', true), this.onGeneratedAnswerStateUpdate = ()=>{
430
+ ], this.withToggle = false, this.collapsible = false, this.maxCollapsedHeight = this.DEFAULT_COLLAPSED_HEIGHT, this.disableCitationAnchoring = false, this.tabsIncluded = [], this.tabsExcluded = [], this.copied = false, this.copyError = false, this.ariaMessage = new accessibility_utils_cjs_namespaceObject.AriaLiveRegionController(this, 'generated-answer', false, true), this.ariaErrorMessage = new accessibility_utils_cjs_namespaceObject.AriaLiveRegionController(this, 'generated-answer-error', true), this.ariaCopyMessage = new accessibility_utils_cjs_namespaceObject.AriaLiveRegionController(this, 'generated-answer-copy', true), this.onGeneratedAnswerStateUpdate = ()=>{
425
431
  if (this.generatedAnswerState.isVisible !== this.controller.data.isVisible) {
426
432
  this.controller.data = {
427
433
  ...this.controller.data,
@@ -429,6 +435,7 @@ class AtomicGeneratedAnswer extends external_lit_namespaceObject.LitElement {
429
435
  };
430
436
  this.controller.writeStoredData(this.controller.data);
431
437
  }
438
+ if (!this.shouldAnnounceAnswerStatus) return;
432
439
  const status = this.controller.getGeneratedAnswerStatus();
433
440
  if (this.controller.isStatusAssertive()) {
434
441
  this.ariaMessage.message = '';
@@ -32,7 +32,7 @@ function getWindow() {
32
32
  }
33
33
  function getAtomicEnvironment(headlessVersion) {
34
34
  return {
35
- version: "3.61.4",
35
+ version: "3.62.0",
36
36
  headlessVersion
37
37
  };
38
38
  }
@@ -32,7 +32,7 @@ __webpack_require__.d(__webpack_exports__, {
32
32
  headlessVersion: ()=>package_json_namespaceObject.version
33
33
  });
34
34
  const package_json_namespaceObject = require("@coveo/headless/package.json");
35
- const atomicVersion = "3.61.4";
35
+ const atomicVersion = "3.62.0";
36
36
  exports.atomicVersion = __webpack_exports__.atomicVersion;
37
37
  exports.headlessVersion = __webpack_exports__.headlessVersion;
38
38
  for(var __rspack_i in __webpack_exports__)if (-1 === [
@@ -18,13 +18,14 @@ import { errorGuard } from "../../../decorators/error-guard.js";
18
18
  import { withTailwindStyles } from "../../../decorators/with-tailwind-styles.js";
19
19
  import { AriaLiveRegionController, FocusTargetController } from "../../../utils/accessibility-utils.js";
20
20
  import { getFieldValueCaption } from "../../../utils/field-utils.js";
21
+ import { renderCategoryFacetActiveValue } from "../../common/facets/category-facet/active-value.js";
21
22
  import { renderCategoryFacetAllCategoryButton } from "../../common/facets/category-facet/all-categories-button.js";
22
23
  import { renderCategoryFacetChildrenAsTreeContainer } from "../../common/facets/category-facet/children-as-tree-container.js";
23
24
  import { renderCategoryFacetParentAsTreeContainer } from "../../common/facets/category-facet/parent-as-tree-container.js";
24
25
  import { renderCategoryFacetParentButton } from "../../common/facets/category-facet/parent-button.js";
25
- import { renderCategoryFacetParentValueLink } from "../../common/facets/category-facet/parent-value-link.js";
26
26
  import { renderCategoryFacetSearchResultsContainer } from "../../common/facets/category-facet/search-results-container.js";
27
27
  import { renderCategoryFacetSearchValue } from "../../common/facets/category-facet/search-value.js";
28
+ import { renderCategoryFacetSelectedValue } from "../../common/facets/category-facet/selected-value.js";
28
29
  import { renderCategoryFacetTreeValueContainer } from "../../common/facets/category-facet/value-as-tree-container.js";
29
30
  import { renderCategoryFacetValueLink } from "../../common/facets/category-facet/value-link.js";
30
31
  import facet_common_tw_css from "../../common/facets/facet-common.tw.css.js";
@@ -93,6 +94,21 @@ class AtomicCommerceCategoryFacet extends LitElement {
93
94
  }
94
95
  });
95
96
  }
97
+ renderSelectedValue() {
98
+ const activeValue = this.facetState.selectedValueAncestry?.at(-1);
99
+ if (!activeValue) return nothing;
100
+ return renderCategoryFacetSelectedValue({
101
+ props: {
102
+ displayValue: getFieldValueCaption(this.facetState.field, activeValue.value, this.bindings.i18n),
103
+ label: this.displayName,
104
+ i18n: this.bindings.i18n,
105
+ onClearFilters: ()=>{
106
+ this.focusTargets.activeValueFocus.focusAfterSearch();
107
+ this.facet.deselectAll();
108
+ }
109
+ }
110
+ });
111
+ }
96
112
  renderValuesTree(parents, isRoot) {
97
113
  if (!this.hasParents) return this.renderChildren();
98
114
  if (isRoot) return renderCategoryFacetTreeValueContainer()(html`
@@ -137,16 +153,12 @@ class AtomicCommerceCategoryFacet extends LitElement {
137
153
  const activeParent = parents[0];
138
154
  const activeParentDisplayValue = getFieldValueCaption(this.facetState?.field, activeParent.value, this.bindings.i18n);
139
155
  return html`
140
- ${renderCategoryFacetParentValueLink({
156
+ ${renderCategoryFacetActiveValue({
141
157
  props: {
142
158
  displayValue: activeParentDisplayValue,
143
159
  numberOfResults: activeParent.numberOfResults,
144
160
  i18n: this.bindings.i18n,
145
161
  isLeafValue: activeParent.isLeafValue,
146
- onClick: ()=>{
147
- this.focusTargets.activeValueFocus.focusAfterSearch();
148
- this.facet.deselectAll();
149
- },
150
162
  searchQuery: this.facetState.facetSearch.query,
151
163
  setRef: (el)=>{
152
164
  this.focusTargets.activeValueFocus.setTarget(el);
@@ -171,7 +183,6 @@ class AtomicCommerceCategoryFacet extends LitElement {
171
183
  this.focusTargets.activeValueFocus.focusAfterSearch();
172
184
  this.facet.toggleSelect(facetValue);
173
185
  },
174
- isParent: false,
175
186
  isSelected,
176
187
  searchQuery: this.facetState.facetSearch.query,
177
188
  isLeafValue: facetValue.isLeafValue,
@@ -246,6 +257,7 @@ class AtomicCommerceCategoryFacet extends LitElement {
246
257
  onClear: ()=>this.facet.facetSearch.clear()
247
258
  }
248
259
  })}
260
+ ${this.renderSelectedValue()}
249
261
  ${when(shouldDisplaySearchResults(facetSearch), ()=>html`
250
262
  ${when(facetSearch.values.length > 0, ()=>renderFacetValuesGroup({
251
263
  props: {
@@ -4,7 +4,9 @@ import { customElement, property, state } from "lit/decorators.js";
4
4
  import { when } from "lit/directives/when.js";
5
5
  import atomic_generated_answer_tw_css from "../../search/atomic-generated-answer/atomic-generated-answer.tw.css.js";
6
6
  import { withTailwindStyles } from "../../../decorators/with-tailwind-styles.js";
7
+ import { AriaLiveRegionController } from "../../../utils/accessibility-utils.js";
7
8
  import "../atomic-agent-stream-of-thought/atomic-agent-stream-of-thought.js";
9
+ import { getAnswerAnnouncement, isSettledAnswer } from "../generated-answer/answer-announcement.js";
8
10
  import { renderGeneratedContentContainer } from "../generated-answer/generated-content-container.js";
9
11
  import { renderFeedbackAndCopyButtons } from "../generated-answer/render-feedback-and-copy-buttons.js";
10
12
  import { renderSourceCitations } from "../generated-answer/source-citations.js";
@@ -58,6 +60,24 @@ class AtomicGeneratedAnswerContent extends LitElement {
58
60
  </div>
59
61
  `;
60
62
  }
63
+ updated() {
64
+ this.announceAnswerStatus();
65
+ }
66
+ announceAnswerStatus() {
67
+ if (!this.i18n || !this.generatedAnswer) return;
68
+ const { message, assertive } = getAnswerAnnouncement(this.generatedAnswer, this.i18n);
69
+ if (!this.hasObservedAnswer) {
70
+ this.hasObservedAnswer = true;
71
+ if (isSettledAnswer(this.generatedAnswer)) {
72
+ this.lastAnnouncedMessage = message;
73
+ return;
74
+ }
75
+ }
76
+ if (message === this.lastAnnouncedMessage) return;
77
+ this.lastAnnouncedMessage = message;
78
+ if (assertive) this.ariaErrorMessage.message = message;
79
+ else this.ariaMessage.message = message;
80
+ }
61
81
  renderFeedbackAndCopyButtons(answerId) {
62
82
  return html`
63
83
  <div class="mt-4" part="feedback-and-copy-buttons">
@@ -88,9 +108,11 @@ class AtomicGeneratedAnswerContent extends LitElement {
88
108
  try {
89
109
  await navigator.clipboard.writeText(answer);
90
110
  this.copyState = 'success';
111
+ this.ariaCopyMessage.message = this.i18n.t('generated-answer-copied');
91
112
  this.onCopyToClipboard(answerId);
92
113
  } catch (error) {
93
114
  this.copyState = 'error';
115
+ this.ariaCopyMessage.message = this.i18n.t('failed-to-copy-generated-answer');
94
116
  console.error(`Failed to copy to clipboard: ${error}`);
95
117
  }
96
118
  this.scheduleCopyReset();
@@ -127,7 +149,7 @@ class AtomicGeneratedAnswerContent extends LitElement {
127
149
  `;
128
150
  }
129
151
  constructor(...args){
130
- super(...args), this.answerId = '', this.renderCitations = ()=>html``, this.onClickLike = ()=>{}, this.onClickDislike = ()=>{}, this.onCopyToClipboard = ()=>{}, this.copyState = 'idle';
152
+ super(...args), this.answerId = '', this.renderCitations = ()=>html``, this.onClickLike = ()=>{}, this.onClickDislike = ()=>{}, this.onCopyToClipboard = ()=>{}, this.copyState = 'idle', this.ariaMessage = new AriaLiveRegionController(this, 'generated-answer', false, true), this.ariaErrorMessage = new AriaLiveRegionController(this, 'generated-answer-error', true), this.ariaCopyMessage = new AriaLiveRegionController(this, 'generated-answer-copy', true), this.hasObservedAnswer = false;
131
153
  }
132
154
  }
133
155
  AtomicGeneratedAnswerContent.styles = [
@@ -106,7 +106,7 @@ class AtomicGeneratedAnswerThread extends LitElement {
106
106
  `;
107
107
  return html`
108
108
  <li class="group/title grid min-w-0 grid-cols-[10px_1fr] gap-x-3">
109
- <div class="row-span-2 flex flex-col items-center">
109
+ <div class="row-span-2 flex flex-col items-center" aria-hidden="true">
110
110
  <div class="flex justify-center items-center text-base h-[1lh] my-1.5">
111
111
  <span class=${timelineDotClasses}></span>
112
112
  </div>
@@ -89,7 +89,7 @@ class AtomicGeneratedAnswerThreadItem extends LitElement {
89
89
  const timelineConnectorClasses = "relative h-full w-px bg-neutral transition-colors duration-150 group-hover:bg-neutral-dark before:absolute before:left-0 before:top-[-8px] before:h-[8px] before:w-px before:bg-neutral before:transition-colors before:duration-150 group-hover:before:bg-neutral-dark before:content-[''] after:absolute after:bottom-[-8px] after:left-0 after:h-[8px] after:w-px after:bg-neutral after:transition-colors after:duration-150 group-hover:after:bg-neutral-dark after:content-['']";
90
90
  return html`
91
91
  <div class="group/title grid min-w-0 grid-cols-[10px_1fr] gap-x-3">
92
- <div class="row-span-2 flex flex-col items-center">
92
+ <div class="row-span-2 flex flex-col items-center" aria-hidden="true">
93
93
  <div class=${timelineDotToggleClasses}>
94
94
  ${when(this.showTimelineDot, ()=>html`
95
95
  <span
@@ -123,8 +123,9 @@ class AtomicGeneratedAnswerThreadItem extends LitElement {
123
123
  >${this.title}</span
124
124
  >`)}
125
125
  </div>
126
- <div id=${this.contentId} class="min-w-0 pl-2 py-1.5">
126
+ <div class="min-w-0 pl-2 py-1.5">
127
127
  <div
128
+ id=${this.contentId}
128
129
  class="mb-2"
129
130
  ?hidden=${!this.isExpanded}
130
131
  aria-hidden=${this.isExpanded ? 'false' : 'true'}
@@ -0,0 +1,31 @@
1
+ import { html } from "lit";
2
+ import { keyed } from "lit/directives/keyed.js";
3
+ import { ref } from "lit/directives/ref.js";
4
+ import { renderFacetValueLabelHighlight } from "../facet-value-label-highlight/facet-value-label-highlight.js";
5
+ const renderCategoryFacetActiveValue = ({ props: { displayValue, numberOfResults, i18n, searchQuery, isLeafValue, setRef } })=>(children)=>{
6
+ const count = numberOfResults.toLocaleString(i18n.language);
7
+ return html`${keyed(displayValue, html`<li class="contents">
8
+ <span
9
+ part="active-parent ${isLeafValue ? 'leaf-value' : 'node-value'}"
10
+ class="text-primary flex w-full items-center truncate px-2 py-2.5 text-left"
11
+ aria-current="true"
12
+ tabindex="-1"
13
+ ${ref(setRef)}
14
+ >
15
+ ${renderFacetValueLabelHighlight({
16
+ props: {
17
+ displayValue,
18
+ searchQuery,
19
+ isSelected: true
20
+ }
21
+ })}
22
+ <span part="value-count" class="value-count">
23
+ ${i18n.t('between-parentheses', {
24
+ text: count
25
+ })}
26
+ </span>
27
+ </span>
28
+ ${children}
29
+ </li>`)}`;
30
+ };
31
+ export { renderCategoryFacetActiveValue };
@@ -0,0 +1,30 @@
1
+ import { html } from "lit";
2
+ import images_close from "../../../../images/close.js";
3
+ import "../../atomic-icon/atomic-icon.js";
4
+ import { renderButton } from "../../button.js";
5
+ const renderCategoryFacetSelectedValue = ({ props: { displayValue, label, i18n, onClearFilters } })=>html`<div part="selected-value" class="mt-3 flex items-center justify-between gap-2">
6
+ <span
7
+ part="selected-value-pill"
8
+ title=${displayValue}
9
+ class="bg-neutral-light truncate rounded-md px-2 py-1 text-sm"
10
+ >${displayValue}</span
11
+ >
12
+ ${renderButton({
13
+ props: {
14
+ part: 'selected-value-clear-button',
15
+ style: 'text-primary',
16
+ class: 'flex shrink-0 items-center p-1 text-sm',
17
+ ariaLabel: i18n.t('clear-filters-for-facet', {
18
+ count: 1,
19
+ label: i18n.t(label)
20
+ }),
21
+ onClick: onClearFilters
22
+ }
23
+ })(html`<atomic-icon
24
+ part="selected-value-clear-button-icon"
25
+ class="mr-1 h-2 w-2"
26
+ .icon=${images_close}
27
+ ></atomic-icon>
28
+ <span>${i18n.t('clear')}</span>`)}
29
+ </div>`;
30
+ export { renderCategoryFacetSelectedValue };
@@ -1,12 +1,11 @@
1
1
  import { html } from "lit";
2
2
  import { renderFacetValueLabelHighlight } from "../facet-value-label-highlight/facet-value-label-highlight.js";
3
3
  import { renderFacetValueLink } from "../facet-value-link/facet-value-link.js";
4
- const renderCategoryFacetValueLink = ({ props: { displayValue, numberOfResults, i18n, onClick, isParent, isSelected, searchQuery, isLeafValue, setRef } })=>(children)=>{
5
- const partNames = [];
6
- if (isParent) partNames.push('active-parent');
7
- else partNames.push(`value-link${isSelected ? ' value-link-selected' : ''}`);
8
- if (isLeafValue) partNames.push('leaf-value');
9
- else partNames.push('node-value');
4
+ const renderCategoryFacetValueLink = ({ props: { displayValue, numberOfResults, i18n, onClick, isSelected, searchQuery, isLeafValue, setRef } })=>(children)=>{
5
+ const partNames = [
6
+ `value-link${isSelected ? ' value-link-selected' : ''}`,
7
+ isLeafValue ? 'leaf-value' : 'node-value'
8
+ ];
10
9
  return html`${renderFacetValueLink({
11
10
  props: {
12
11
  displayValue,
@@ -0,0 +1,32 @@
1
+ import { markdownToPlainText } from "./generated-content/markdown-utils.js";
2
+ const SILENT = {
3
+ message: '',
4
+ assertive: false
5
+ };
6
+ const getAnswerAnnouncement = (answer, i18n)=>{
7
+ if (!answer) return SILENT;
8
+ if (answer.error) {
9
+ const errorMessageKey = answer.error.isSseTurnLimitReachedError?.() ? 'generated-answer-error-turn-limit-reached' : 'generated-answer-error-generic';
10
+ return {
11
+ message: i18n.t(errorMessageKey),
12
+ assertive: true
13
+ };
14
+ }
15
+ if (answer.cannotAnswer) return {
16
+ message: i18n.t('generated-answer-cannot-generate-answer'),
17
+ assertive: true
18
+ };
19
+ if (answer.isStreaming || answer.isLoading) return {
20
+ message: i18n.t('generating-answer'),
21
+ assertive: false
22
+ };
23
+ if (answer.answer?.trim()) return {
24
+ message: i18n.t('answer-generated', {
25
+ answer: markdownToPlainText(answer.answer)
26
+ }),
27
+ assertive: false
28
+ };
29
+ return SILENT;
30
+ };
31
+ const isSettledAnswer = (answer)=>Boolean(answer && !answer.isStreaming && !answer.isLoading && !answer.error && !answer.cannotAnswer && answer.answer?.trim());
32
+ export { getAnswerAnnouncement, isSettledAnswer };
@@ -12,6 +12,7 @@ const renderCopyButton = ({ props })=>{
12
12
  return renderButton({
13
13
  props: {
14
14
  title: props.title,
15
+ ariaLabel: props.title,
15
16
  part: 'copy-button',
16
17
  style: 'text-transparent',
17
18
  class: classes.join(' '),
@@ -19,7 +20,7 @@ const renderCopyButton = ({ props })=>{
19
20
  }
20
21
  })(html`
21
22
  <div class="icon-container text-neutral-dark">
22
- <atomic-icon class="w-5" .icon=${copy}></atomic-icon>
23
+ <atomic-icon class="w-5" .icon=${copy} aria-hidden="true"></atomic-icon>
23
24
  </div>
24
25
  `);
25
26
  };
@@ -26,6 +26,7 @@ const renderFeedbackButton = ({ props })=>{
26
26
  return html`<button
27
27
  type="button"
28
28
  title=${props.title}
29
+ aria-label=${props.title}
29
30
  part="feedback-button"
30
31
  class=${classNames}
31
32
  aria-pressed=${props.active ? 'true' : 'false'}
@@ -34,7 +35,7 @@ const renderFeedbackButton = ({ props })=>{
34
35
  })}
35
36
  @click=${props.onClick}
36
37
  >
37
- <atomic-icon class=${iconClassNames} .icon=${thumbs}></atomic-icon>
38
+ <atomic-icon class=${iconClassNames} .icon=${thumbs} aria-hidden="true"></atomic-icon>
38
39
  </button>`;
39
40
  };
40
41
  export { renderFeedbackButton };
@@ -36,8 +36,7 @@ const customRenderer = {
36
36
  return `<em part="answer-emphasis">${text}</em>`;
37
37
  },
38
38
  heading (text, level) {
39
- const plainText = toInlinePlainText(text);
40
- return `<div part="answer-heading-${level}" aria-label="${plainText}">${text}</div>`;
39
+ return `<div part="answer-heading-${level}" role="heading" aria-level="${level}">${text}</div>`;
41
40
  },
42
41
  html (text) {
43
42
  return escapeHtml(text);
@@ -1,8 +1,13 @@
1
1
  import { html } from "lit";
2
+ import { ifDefined } from "lit/directives/if-defined.js";
2
3
  import { renderGeneratedMarkdownContent } from "./generated-content/generated-markdown-content.js";
3
4
  import { renderGeneratedTextContent } from "./generated-content/generated-text-content.js";
4
5
  const renderGeneratedContentContainer = ({ props })=>(children)=>html`
5
- <div part="generated-container">
6
+ <div
7
+ part="generated-container"
8
+ id=${ifDefined(props.id)}
9
+ aria-busy=${props.isStreaming ? 'true' : 'false'}
10
+ >
6
11
  ${'text/markdown' === props.answerContentFormat ? renderGeneratedMarkdownContent({
7
12
  props: {
8
13
  answer: props.answer,
@@ -6,6 +6,7 @@ import { renderShowButton } from "./show-button.js";
6
6
  import { renderSourceCitations } from "./source-citations.js";
7
7
  import { renderGeneratingAnswerLabel } from "./render-generating-answer-label.js";
8
8
  import "../atomic-icon/atomic-icon.js";
9
+ const GENERATED_CONTENT_ID = 'generated-answer-content';
9
10
  const renderAnswerContent = ({ props })=>{
10
11
  const { i18n, generatedAnswer, collapsible, renderFeedbackAndCopyButtonsSlot, renderCitationsSlot, onRetry, onClickShowButton } = props;
11
12
  const { answer, isStreaming, citations, answerContentFormat, expanded, error } = generatedAnswer;
@@ -23,7 +24,8 @@ const renderAnswerContent = ({ props })=>{
23
24
  props: {
24
25
  answer,
25
26
  answerContentFormat,
26
- isStreaming: !!isStreaming
27
+ isStreaming: !!isStreaming,
28
+ id: collapsible ? GENERATED_CONTENT_ID : void 0
27
29
  }
28
30
  })(html`
29
31
  ${renderSourceCitations({
@@ -51,7 +53,8 @@ const renderAnswerContent = ({ props })=>{
51
53
  props: {
52
54
  i18n,
53
55
  onClick: onClickShowButton,
54
- isCollapsed: !expanded
56
+ isCollapsed: !expanded,
57
+ controlsId: GENERATED_CONTENT_ID
55
58
  }
56
59
  }) : nothing}
57
60
  </div>
@@ -10,6 +10,7 @@ const renderShowButton = ({ props })=>html`${renderButton({
10
10
  part: 'answer-show-button',
11
11
  class: 'hidden items-center hover:bg-transparent',
12
12
  ariaExpanded: props.isCollapsed ? 'false' : 'true',
13
+ ariaControls: props.controlsId,
13
14
  onClick: props.onClick
14
15
  }
15
16
  })(html`
@@ -135,11 +135,13 @@ class AtomicInsightGeneratedAnswer extends LitElement {
135
135
  async copyToClipboard(answer) {
136
136
  await this.controller.copyToClipboard(answer, ()=>{
137
137
  this.copied = true;
138
+ this.ariaCopyMessage.message = this.bindings.i18n.t('generated-answer-copied');
138
139
  setTimeout(()=>{
139
140
  this.copied = false;
140
141
  }, 2000);
141
142
  }, ()=>{
142
143
  this.copyError = true;
144
+ this.ariaCopyMessage.message = this.bindings.i18n.t('failed-to-copy-generated-answer');
143
145
  setTimeout(()=>{
144
146
  this.copyError = false;
145
147
  }, 2000);
@@ -276,7 +278,7 @@ class AtomicInsightGeneratedAnswer extends LitElement {
276
278
  constructor(){
277
279
  super(), this.DEFAULT_COLLAPSED_HEIGHT = 16, this.MAX_COLLAPSED_HEIGHT = 32, this.MIN_COLLAPSED_HEIGHT = 9, this.REQUIRED_FIELDS_TO_INCLUDE_IN_CITATIONS = [
278
280
  'filetype'
279
- ], this.withToggle = false, this.collapsible = false, this.maxCollapsedHeight = this.DEFAULT_COLLAPSED_HEIGHT, this.disableCitationAnchoring = false, this.copied = false, this.copyError = false, this.ariaMessage = new AriaLiveRegionController(this, 'generated-answer', false, true), this.ariaErrorMessage = new AriaLiveRegionController(this, 'generated-answer-error', true), this.onGeneratedAnswerStateUpdate = ()=>{
281
+ ], this.withToggle = false, this.collapsible = false, this.maxCollapsedHeight = this.DEFAULT_COLLAPSED_HEIGHT, this.disableCitationAnchoring = false, this.copied = false, this.copyError = false, this.ariaMessage = new AriaLiveRegionController(this, 'generated-answer', false, true), this.ariaErrorMessage = new AriaLiveRegionController(this, 'generated-answer-error', true), this.ariaCopyMessage = new AriaLiveRegionController(this, 'generated-answer-copy', true), this.onGeneratedAnswerStateUpdate = ()=>{
280
282
  if (this.generatedAnswerState.isVisible !== this.controller.data.isVisible) {
281
283
  this.controller.data = {
282
284
  ...this.controller.data,
@@ -4,13 +4,14 @@ import { LitElement, css, html, nothing } from "lit";
4
4
  import { customElement, property, state } from "lit/decorators.js";
5
5
  import { map } from "lit/directives/map.js";
6
6
  import { when } from "lit/directives/when.js";
7
+ import { renderCategoryFacetActiveValue } from "../../common/facets/category-facet/active-value.js";
7
8
  import { renderCategoryFacetAllCategoryButton } from "../../common/facets/category-facet/all-categories-button.js";
8
9
  import { renderCategoryFacetChildrenAsTreeContainer } from "../../common/facets/category-facet/children-as-tree-container.js";
9
10
  import { renderCategoryFacetParentAsTreeContainer } from "../../common/facets/category-facet/parent-as-tree-container.js";
10
11
  import { renderCategoryFacetParentButton } from "../../common/facets/category-facet/parent-button.js";
11
- import { renderCategoryFacetParentValueLink } from "../../common/facets/category-facet/parent-value-link.js";
12
12
  import { renderCategoryFacetSearchResultsContainer } from "../../common/facets/category-facet/search-results-container.js";
13
13
  import { renderCategoryFacetSearchValue } from "../../common/facets/category-facet/search-value.js";
14
+ import { renderCategoryFacetSelectedValue } from "../../common/facets/category-facet/selected-value.js";
14
15
  import { renderCategoryFacetTreeValueContainer } from "../../common/facets/category-facet/value-as-tree-container.js";
15
16
  import { renderCategoryFacetValueLink } from "../../common/facets/category-facet/value-link.js";
16
17
  import { parseDependsOn } from "../../common/facets/depends-on.js";
@@ -168,6 +169,21 @@ class AtomicCategoryFacet extends LitElement {
168
169
  }
169
170
  });
170
171
  }
172
+ renderSelectedValue() {
173
+ const activeValue = this.facetState.selectedValueAncestry.at(-1);
174
+ if (!activeValue) return nothing;
175
+ return renderCategoryFacetSelectedValue({
176
+ props: {
177
+ displayValue: getFieldValueCaption(this.field, activeValue.value, this.bindings.i18n),
178
+ label: this.label,
179
+ i18n: this.bindings.i18n,
180
+ onClearFilters: ()=>{
181
+ this.focusTargets.activeValueFocus.focusAfterSearch();
182
+ this.facet.deselectAll();
183
+ }
184
+ }
185
+ });
186
+ }
171
187
  renderValuesTree(valuesAsTrees, isRoot) {
172
188
  if (!this.hasParents) return this.renderChildren();
173
189
  if (isRoot) return renderCategoryFacetTreeValueContainer()(html`
@@ -212,16 +228,12 @@ class AtomicCategoryFacet extends LitElement {
212
228
  const activeParent = valuesAsTrees[0];
213
229
  const activeParentDisplayValue = getFieldValueCaption(this.field, activeParent.value, this.bindings.i18n);
214
230
  return html`
215
- ${renderCategoryFacetParentValueLink({
231
+ ${renderCategoryFacetActiveValue({
216
232
  props: {
217
233
  displayValue: activeParentDisplayValue,
218
234
  numberOfResults: activeParent.numberOfResults,
219
235
  i18n: this.bindings.i18n,
220
236
  isLeafValue: activeParent.isLeafValue,
221
- onClick: ()=>{
222
- this.focusTargets.activeValueFocus.focusAfterSearch();
223
- this.facet.deselectAll();
224
- },
225
237
  searchQuery: this.facetState.facetSearch.query,
226
238
  setRef: (el)=>{
227
239
  this.focusTargets.activeValueFocus.setTarget(el);
@@ -244,7 +256,6 @@ class AtomicCategoryFacet extends LitElement {
244
256
  this.focusTargets.activeValueFocus.focusAfterSearch();
245
257
  this.facet.toggleSelect(facetValue);
246
258
  },
247
- isParent: false,
248
259
  isSelected,
249
260
  searchQuery: this.facetState.facetSearch.query,
250
261
  isLeafValue: facetValue.isLeafValue,
@@ -326,7 +337,7 @@ class AtomicCategoryFacet extends LitElement {
326
337
  return renderFacetContainer()(html`
327
338
  ${this.renderHeader()}
328
339
  ${when(!this.isCollapsed, ()=>html`
329
- ${this.renderSearchInput()}
340
+ ${this.renderSearchInput()} ${this.renderSelectedValue()}
330
341
  ${when(shouldDisplaySearchResults(facetSearch), ()=>html`
331
342
  ${when(facetSearch.values.length > 0, ()=>renderFacetValuesGroup({
332
343
  props: {
@@ -132,6 +132,7 @@ class AtomicGeneratedAnswer extends LitElement {
132
132
  'generated-answer-content-background': this.areFollowUpsEnabled,
133
133
  'agent-scrollable': this.areFollowUpsEnabled
134
134
  })}
135
+ tabindex=${this.areFollowUpsEnabled ? '0' : nothing}
135
136
  >
136
137
  <article>${this.renderAnswerContent()}</article>
137
138
  </div>
@@ -156,6 +157,9 @@ class AtomicGeneratedAnswer extends LitElement {
156
157
  </div>
157
158
  `;
158
159
  }
160
+ get shouldAnnounceAnswerStatus() {
161
+ return !this.areFollowUpsEnabled;
162
+ }
159
163
  get hasNoAnswerGenerated() {
160
164
  return this.controller.hasNoAnswerGenerated;
161
165
  }
@@ -180,11 +184,13 @@ class AtomicGeneratedAnswer extends LitElement {
180
184
  async copyToClipboard(answer) {
181
185
  await this.controller.copyToClipboard(answer, ()=>{
182
186
  this.copied = true;
187
+ this.ariaCopyMessage.message = this.bindings.i18n.t('generated-answer-copied');
183
188
  setTimeout(()=>{
184
189
  this.copied = false;
185
190
  }, 2000);
186
191
  }, ()=>{
187
192
  this.copyError = true;
193
+ this.ariaCopyMessage.message = this.bindings.i18n.t('failed-to-copy-generated-answer');
188
194
  setTimeout(()=>{
189
195
  this.copyError = false;
190
196
  }, 2000);
@@ -379,7 +385,7 @@ class AtomicGeneratedAnswer extends LitElement {
379
385
  constructor(){
380
386
  super(), this.DEFAULT_COLLAPSED_HEIGHT = 16, this.MAX_COLLAPSED_HEIGHT = 32, this.MIN_COLLAPSED_HEIGHT = 9, this.REQUIRED_FIELDS_TO_INCLUDE_IN_CITATIONS = [
381
387
  'filetype'
382
- ], this.withToggle = false, this.collapsible = false, this.maxCollapsedHeight = this.DEFAULT_COLLAPSED_HEIGHT, this.disableCitationAnchoring = false, this.tabsIncluded = [], this.tabsExcluded = [], this.copied = false, this.copyError = false, this.ariaMessage = new AriaLiveRegionController(this, 'generated-answer', false, true), this.ariaErrorMessage = new AriaLiveRegionController(this, 'generated-answer-error', true), this.onGeneratedAnswerStateUpdate = ()=>{
388
+ ], this.withToggle = false, this.collapsible = false, this.maxCollapsedHeight = this.DEFAULT_COLLAPSED_HEIGHT, this.disableCitationAnchoring = false, this.tabsIncluded = [], this.tabsExcluded = [], this.copied = false, this.copyError = false, this.ariaMessage = new AriaLiveRegionController(this, 'generated-answer', false, true), this.ariaErrorMessage = new AriaLiveRegionController(this, 'generated-answer-error', true), this.ariaCopyMessage = new AriaLiveRegionController(this, 'generated-answer-copy', true), this.onGeneratedAnswerStateUpdate = ()=>{
383
389
  if (this.generatedAnswerState.isVisible !== this.controller.data.isVisible) {
384
390
  this.controller.data = {
385
391
  ...this.controller.data,
@@ -387,6 +393,7 @@ class AtomicGeneratedAnswer extends LitElement {
387
393
  };
388
394
  this.controller.writeStoredData(this.controller.data);
389
395
  }
396
+ if (!this.shouldAnnounceAnswerStatus) return;
390
397
  const status = this.controller.getGeneratedAnswerStatus();
391
398
  if (this.controller.isStatusAssertive()) {
392
399
  this.ariaMessage.message = '';
@@ -3,7 +3,7 @@ function getWindow() {
3
3
  }
4
4
  function getAtomicEnvironment(headlessVersion) {
5
5
  return {
6
- version: "3.61.4",
6
+ version: "3.62.0",
7
7
  headlessVersion
8
8
  };
9
9
  }
@@ -1,3 +1,3 @@
1
- const atomicVersion = "3.61.4";
1
+ const atomicVersion = "3.62.0";
2
2
  export { version as headlessVersion } from "@coveo/headless/package.json";
3
3
  export { atomicVersion };
@@ -25,12 +25,17 @@ import type { InitializableComponent } from '../../../decorators/types';
25
25
  * @part search-result-path - The facet search result path.
26
26
  * @part search-highlight - The highlighted query inside the facet values.
27
27
  *
28
+ * @part selected-value - The row displaying the currently selected value and the button that clears the facet.
29
+ * @part selected-value-pill - The pill displaying the currently selected value.
30
+ * @part selected-value-clear-button - The button that clears the facet, displayed next to the selected value.
31
+ * @part selected-value-clear-button-icon - The icon of the button that clears the facet.
32
+ *
28
33
  * @part parents - The container surrounding the whole hierarchy of values.
29
34
  * @part sub-parents - The container surrounding a sub-hierarchy of values.
30
35
  * @part values - The container surrounding either the children of the active value or the values at the base.
31
36
  * @part all-categories-button - The "All categories" button displayed first within the parents.
32
37
  * @part parent-button - The clickable parent button displayed first within sub-parents.
33
- * @part active-parent - The clickable active parent displayed first within the last sub-parents.
38
+ * @part active-parent - The currently selected value, displayed last within the parents. Not interactive.
34
39
  * @part value-link - The clickable value displayed first within values.
35
40
  * @part back-arrow - The back arrow displayed before the clickable parents.
36
41
  * @part value-label - The facet value label within a value button.
@@ -77,6 +82,7 @@ export declare class AtomicCommerceCategoryFacet extends LitElement implements I
77
82
  shouldUpdate(changedProperties: Map<string | number | symbol, unknown>): boolean;
78
83
  private get hasParents();
79
84
  private renderHeader;
85
+ private renderSelectedValue;
80
86
  private renderValuesTree;
81
87
  private renderChild;
82
88
  private renderChildren;