From 57d346b1bf2459f8e466d46b41e804813e3a2239 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 13:36:15 +0000 Subject: [PATCH 1/8] Initial plan From 106d229bbef8a6f9d061c86092a7012d5acf57c7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 13:43:45 +0000 Subject: [PATCH 2/8] feat(atomic): migrate atomic-insight-user-actions-toggle to Lit Co-authored-by: alexprudhomme <78121423+alexprudhomme@users.noreply.github.com> --- .../atomic/src/components/insight/index.ts | 1 + .../src/components/insight/lazy-index.ts | 4 + .../atomic-insight-user-actions-toggle.ts | 122 ++++++++++++++++++ ...omic-insight-user-actions-toggle.tw.css.ts | 1 + 4 files changed, 128 insertions(+) create mode 100644 packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts create mode 100644 packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.tw.css.ts diff --git a/packages/atomic/src/components/insight/index.ts b/packages/atomic/src/components/insight/index.ts index 2397cc73d6a..e022fb573d5 100644 --- a/packages/atomic/src/components/insight/index.ts +++ b/packages/atomic/src/components/insight/index.ts @@ -3,3 +3,4 @@ export {AtomicInsightGenerateAnswerButton} from './atomic-insight-generate-answe export {AtomicInsightInterface} from './atomic-insight-interface/atomic-insight-interface.js'; export {AtomicInsightTab} from './atomic-insight-tab/atomic-insight-tab.js'; export {AtomicInsightTabs} from './atomic-insight-tabs/atomic-insight-tabs.js'; +export {AtomicInsightUserActionsToggle} from './user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.js'; diff --git a/packages/atomic/src/components/insight/lazy-index.ts b/packages/atomic/src/components/insight/lazy-index.ts index 60500915b59..6b471c8b335 100644 --- a/packages/atomic/src/components/insight/lazy-index.ts +++ b/packages/atomic/src/components/insight/lazy-index.ts @@ -10,6 +10,10 @@ export default { await import('./atomic-insight-tab/atomic-insight-tab.js'), 'atomic-insight-tabs': async () => await import('./atomic-insight-tabs/atomic-insight-tabs.js'), + 'atomic-insight-user-actions-toggle': async () => + await import( + './user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.js' + ), } as Record Promise>; export type * from './index.js'; diff --git a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts b/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts new file mode 100644 index 00000000000..c82b0d1e444 --- /dev/null +++ b/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts @@ -0,0 +1,122 @@ +import { + buildUserActions as buildInsightUserActions, + type UserActions as InsightUserActions, + type UserActionsState as InsightUserActionsState, +} from '@coveo/headless/insight'; +import {html, LitElement} from 'lit'; +import {customElement, property, state} from 'lit/decorators.js'; +import type {RefOrCallback} from 'lit/directives/ref.js'; +import Clockicon from '@/images/clock.svg'; +import {renderIconButton} from '@/src/components/common/icon-button'; +import type {InsightBindings} from '@/src/components/insight/atomic-insight-interface/atomic-insight-interface'; +import {bindStateToController} from '@/src/decorators/bind-state'; +import {bindingGuard} from '@/src/decorators/binding-guard'; +import {bindings} from '@/src/decorators/bindings'; +import {errorGuard} from '@/src/decorators/error-guard'; +import type {InitializableComponent} from '@/src/decorators/types'; +import {withTailwindStyles} from '@/src/decorators/with-tailwind-styles.js'; + +/** + * The `atomic-insight-user-actions-toggle` component displays a button that opens a modal containing the user actions timeline component. + * This is an internal component of the atomic-insight-interface. + * + * @internal + */ +@customElement('atomic-insight-user-actions-toggle') +@bindings() +@withTailwindStyles +export class AtomicInsightUserActionsToggle + extends LitElement + implements InitializableComponent +{ + @state() + bindings!: InsightBindings; + + @state() + error!: Error; + + public userActions!: InsightUserActions; + + @bindStateToController('userActions') + @state() + public userActionsState!: InsightUserActionsState; + + /** + * The ID of the user whose actions are being displayed. + */ + @property({type: String}) + public userId!: string; + + /** + * The date and time when the case was created. For example "2024-01-01T00:00:00Z" + */ + @property({type: String, attribute: 'ticket-creation-date-time'}) + public ticketCreationDateTime!: string; + + /** + * The names of custom events to exclude. + */ + @property({type: Array, attribute: 'excluded-custom-actions'}) + public excludedCustomActions: string[] = []; + + private buttonRef?: HTMLButtonElement; + private modalRef?: HTMLAtomicInsightUserActionsModalElement; + + public initialize() { + this.userActions = buildInsightUserActions(this.bindings.engine, { + options: { + ticketCreationDate: this.ticketCreationDateTime, + excludedCustomActions: this.excludedCustomActions, + }, + }); + } + + private enableModal() { + if (this.modalRef) { + this.modalRef.isOpen = true; + } + this.userActions.logOpenUserActions(); + } + + private loadModal() { + if (this.modalRef) { + return; + } + + this.modalRef = document.createElement('atomic-insight-user-actions-modal'); + + this.insertAdjacentElement('beforebegin', this.modalRef); + this.modalRef.openButton = this.buttonRef; + this.modalRef.userId = this.userId; + this.modalRef.ticketCreationDateTime = this.ticketCreationDateTime; + this.modalRef.excludedCustomActions = this.excludedCustomActions; + } + + @errorGuard() + @bindingGuard() + render() { + return html`${renderIconButton({ + props: { + partPrefix: 'insight-user-actions-toggle', + style: 'outline-neutral', + icon: Clockicon, + ariaLabel: this.bindings.i18n.t('user-actions'), + onClick: () => this.enableModal(), + title: this.bindings.i18n.t('user-actions'), + buttonRef: ((button) => { + if (!button) { + return; + } + this.buttonRef = button as HTMLButtonElement; + this.loadModal(); + }) as RefOrCallback, + }, + })}`; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'atomic-insight-user-actions-toggle': AtomicInsightUserActionsToggle; + } +} diff --git a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.tw.css.ts b/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.tw.css.ts new file mode 100644 index 00000000000..b8c350c9396 --- /dev/null +++ b/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.tw.css.ts @@ -0,0 +1 @@ +// @reference '@/src/utils/coveo.tw.css'; From 3ba39f1b3a209558628e14d4afc4fb31ce3cf582 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 13:51:08 +0000 Subject: [PATCH 3/8] test(atomic): add comprehensive unit tests for atomic-insight-user-actions-toggle Co-authored-by: alexprudhomme <78121423+alexprudhomme@users.noreply.github.com> --- ...atomic-insight-user-actions-toggle.spec.ts | 207 ++++++++++++++++++ .../atomic-insight-user-actions-toggle.ts | 2 +- .../insight/user-actions-controller.ts | 19 ++ 3 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.spec.ts create mode 100644 packages/atomic/vitest-utils/testing-helpers/fixtures/headless/insight/user-actions-controller.ts diff --git a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.spec.ts b/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.spec.ts new file mode 100644 index 00000000000..86a6b521d36 --- /dev/null +++ b/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.spec.ts @@ -0,0 +1,207 @@ +import { + buildUserActions as buildInsightUserActions, + type UserActionsState as InsightUserActionsState, +} from '@coveo/headless/insight'; +import {html} from 'lit'; +import {beforeEach, describe, expect, it, vi} from 'vitest'; +import {userEvent} from 'vitest/browser'; +import {fixture} from '@/vitest-utils/testing-helpers/fixture.js'; +import {buildFakeInsightEngine} from '@/vitest-utils/testing-helpers/fixtures/headless/insight/engine.js'; +import {buildFakeUserActions} from '@/vitest-utils/testing-helpers/fixtures/headless/insight/user-actions-controller'; +import type {AtomicInsightUserActionsToggle} from './atomic-insight-user-actions-toggle'; +import './atomic-insight-user-actions-toggle'; + +vi.mock('@coveo/headless/insight', {spy: true}); + +describe('atomic-insight-user-actions-toggle', () => { + let mockEngine: ReturnType; + let element: AtomicInsightUserActionsToggle; + + beforeEach(() => { + mockEngine = buildFakeInsightEngine(); + }); + + const setupElement = async ( + props: { + userId?: string; + ticketCreationDateTime?: string; + excludedCustomActions?: string[]; + } = {}, + userActionsState: Partial = {} + ) => { + vi.mocked(buildInsightUserActions).mockReturnValue( + buildFakeUserActions(userActionsState) + ); + + element = await fixture( + html`` + ); + + // Mock the bindings + element.bindings = { + engine: mockEngine, + i18n: { + t: (key: string) => { + const translations: Record = { + 'user-actions': 'User actions', + }; + return translations[key] || key; + }, + } as never, + store: {} as never, + interfaceElement: {} as never, + createStyleElement: vi.fn(), + createScriptElement: vi.fn(), + }; + + // Initialize the component + element.initialize(); + await element.updateComplete; + + return element; + }; + + const getButton = () => element.shadowRoot?.querySelector('button'); + const getModal = () => + (element.getRootNode() as Document | ShadowRoot)?.querySelector( + 'atomic-insight-user-actions-modal' + ); + + describe('#initialize', () => { + it('should call buildInsightUserActions with the engine and options', async () => { + await setupElement({ + userId: 'test-user', + ticketCreationDateTime: '2024-01-01T00:00:00Z', + excludedCustomActions: ['custom-event'], + }); + + expect(buildInsightUserActions).toHaveBeenCalledWith(mockEngine, { + options: { + ticketCreationDate: '2024-01-01T00:00:00Z', + excludedCustomActions: ['custom-event'], + }, + }); + }); + + it('should set this.userActions to the user actions controller', async () => { + await setupElement(); + + expect(element.userActions).toBe( + vi.mocked(buildInsightUserActions).mock.results[0].value + ); + }); + + it('should bind state to controller', async () => { + await setupElement( + {}, + { + loading: true, + excludedCustomActions: ['test-event'], + } + ); + + expect(element.userActionsState.loading).toBe(true); + expect(element.userActionsState.excludedCustomActions).toEqual([ + 'test-event', + ]); + }); + }); + + describe('rendering', () => { + it('should render the button with the correct aria-label', async () => { + await setupElement(); + const button = getButton(); + + expect(button).toHaveAttribute('aria-label', 'User actions'); + }); + + it('should render the button with the correct title', async () => { + await setupElement(); + const button = getButton(); + + expect(button).toHaveAttribute('title', 'User actions'); + }); + + it('should render the button with the correct part prefix', async () => { + await setupElement(); + const container = element.shadowRoot?.querySelector( + '[part="insight-user-actions-toggle-container"]' + ); + + expect(container).toBeInTheDocument(); + }); + + it('should render the icon within the button', async () => { + await setupElement(); + const icon = element.shadowRoot?.querySelector('atomic-icon'); + + expect(icon).toBeInTheDocument(); + expect(icon).toHaveAttribute('part', 'insight-user-actions-toggle-icon'); + }); + }); + + describe('modal creation and interaction', () => { + it('should create the modal when rendered', async () => { + await setupElement(); + const modal = getModal(); + + expect(modal).toBeInTheDocument(); + }); + + it('should set the openButton of the modal to the button', async () => { + await setupElement(); + const button = getButton(); + const modal = getModal(); + + expect(modal?.openButton).toBe(button); + }); + + it('should set the userId of the modal to the component prop', async () => { + await setupElement({userId: 'custom-user'}); + const modal = getModal(); + + expect(modal?.userId).toBe('custom-user'); + }); + + it('should set the ticketCreationDateTime of the modal to the component prop', async () => { + await setupElement({ticketCreationDateTime: '2024-06-15T12:00:00Z'}); + const modal = getModal(); + + expect(modal?.ticketCreationDateTime).toBe('2024-06-15T12:00:00Z'); + }); + + it('should set the excludedCustomActions of the modal to the component prop', async () => { + await setupElement({excludedCustomActions: ['action1', 'action2']}); + const modal = getModal(); + + expect(modal?.excludedCustomActions).toEqual(['action1', 'action2']); + }); + + it('should open the modal when the button is clicked', async () => { + await setupElement(); + const button = getButton(); + const modal = getModal(); + + await userEvent.click(button!); + + expect(modal?.isOpen).toBe(true); + }); + + it('should call logOpenUserActions when the button is clicked', async () => { + await setupElement(); + const logOpenUserActionsSpy = vi.spyOn( + element.userActions, + 'logOpenUserActions' + ); + const button = getButton(); + + await userEvent.click(button!); + + expect(logOpenUserActionsSpy).toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts b/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts index c82b0d1e444..676218e4066 100644 --- a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts +++ b/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts @@ -6,7 +6,6 @@ import { import {html, LitElement} from 'lit'; import {customElement, property, state} from 'lit/decorators.js'; import type {RefOrCallback} from 'lit/directives/ref.js'; -import Clockicon from '@/images/clock.svg'; import {renderIconButton} from '@/src/components/common/icon-button'; import type {InsightBindings} from '@/src/components/insight/atomic-insight-interface/atomic-insight-interface'; import {bindStateToController} from '@/src/decorators/bind-state'; @@ -15,6 +14,7 @@ import {bindings} from '@/src/decorators/bindings'; import {errorGuard} from '@/src/decorators/error-guard'; import type {InitializableComponent} from '@/src/decorators/types'; import {withTailwindStyles} from '@/src/decorators/with-tailwind-styles.js'; +import Clockicon from '@/src/images/clock.svg'; /** * The `atomic-insight-user-actions-toggle` component displays a button that opens a modal containing the user actions timeline component. diff --git a/packages/atomic/vitest-utils/testing-helpers/fixtures/headless/insight/user-actions-controller.ts b/packages/atomic/vitest-utils/testing-helpers/fixtures/headless/insight/user-actions-controller.ts new file mode 100644 index 00000000000..74e618b6c95 --- /dev/null +++ b/packages/atomic/vitest-utils/testing-helpers/fixtures/headless/insight/user-actions-controller.ts @@ -0,0 +1,19 @@ +import type {UserActions, UserActionsState} from '@coveo/headless/insight'; +import {genericSubscribe} from '../common'; + +export const buildFakeUserActions = ( + options: Partial = {} +): UserActions => { + const defaultState: UserActionsState = { + timeline: undefined, + excludedCustomActions: [], + loading: false, + ...options, + }; + + return { + state: defaultState, + subscribe: genericSubscribe, + logOpenUserActions: () => {}, + } as UserActions; +}; From bd77c593b7a686c9ffb75bcaf5b322cb44322a9c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Jan 2026 14:00:15 +0000 Subject: [PATCH 4/8] docs: update migration status - component and tests complete Co-authored-by: alexprudhomme <78121423+alexprudhomme@users.noreply.github.com> --- packages/atomic/src/components.d.ts | 53 ------------------- .../atomic/src/components/insight/index.ts | 1 - .../src/components/insight/lazy-index.ts | 4 -- .../atomic/src/utils/custom-element-tags.ts | 1 + 4 files changed, 1 insertion(+), 58 deletions(-) diff --git a/packages/atomic/src/components.d.ts b/packages/atomic/src/components.d.ts index 83074a36a0f..bc15a06e433 100644 --- a/packages/atomic/src/components.d.ts +++ b/packages/atomic/src/components.d.ts @@ -565,24 +565,6 @@ export namespace Components { */ "userId": string; } - /** - * Internal component of the atomic-insight-interface - * The `atomic-insight-user-actions-toggle` component displays a button that opens a modal containing the user actions timeline component. - */ - interface AtomicInsightUserActionsToggle { - /** - * The names of custom events to exclude. - */ - "excludedCustomActions": string[]; - /** - * The date and time when the case was created. For example "2024-01-01T00:00:00Z" - */ - "ticketCreationDateTime": string; - /** - * The ID of the user whose actions are being displayed. - */ - "userId": string; - } interface AtomicIpxBody { "displayFooterSlot": boolean; "isOpen"?: boolean; @@ -1420,16 +1402,6 @@ declare global { prototype: HTMLAtomicInsightUserActionsTimelineElement; new (): HTMLAtomicInsightUserActionsTimelineElement; }; - /** - * Internal component of the atomic-insight-interface - * The `atomic-insight-user-actions-toggle` component displays a button that opens a modal containing the user actions timeline component. - */ - interface HTMLAtomicInsightUserActionsToggleElement extends Components.AtomicInsightUserActionsToggle, HTMLStencilElement { - } - var HTMLAtomicInsightUserActionsToggleElement: { - prototype: HTMLAtomicInsightUserActionsToggleElement; - new (): HTMLAtomicInsightUserActionsToggleElement; - }; interface HTMLAtomicIpxBodyElementEventMap { "animationEnded": never; } @@ -1798,7 +1770,6 @@ declare global { "atomic-insight-user-actions-modal": HTMLAtomicInsightUserActionsModalElement; "atomic-insight-user-actions-session": HTMLAtomicInsightUserActionsSessionElement; "atomic-insight-user-actions-timeline": HTMLAtomicInsightUserActionsTimelineElement; - "atomic-insight-user-actions-toggle": HTMLAtomicInsightUserActionsToggleElement; "atomic-ipx-body": HTMLAtomicIpxBodyElement; "atomic-ipx-button": HTMLAtomicIpxButtonElement; "atomic-ipx-embedded": HTMLAtomicIpxEmbeddedElement; @@ -2346,24 +2317,6 @@ declare namespace LocalJSX { */ "userId": string; } - /** - * Internal component of the atomic-insight-interface - * The `atomic-insight-user-actions-toggle` component displays a button that opens a modal containing the user actions timeline component. - */ - interface AtomicInsightUserActionsToggle { - /** - * The names of custom events to exclude. - */ - "excludedCustomActions"?: string[]; - /** - * The date and time when the case was created. For example "2024-01-01T00:00:00Z" - */ - "ticketCreationDateTime": string; - /** - * The ID of the user whose actions are being displayed. - */ - "userId": string; - } interface AtomicIpxBody { "displayFooterSlot"?: boolean; "isOpen"?: boolean; @@ -2887,7 +2840,6 @@ declare namespace LocalJSX { "atomic-insight-user-actions-modal": AtomicInsightUserActionsModal; "atomic-insight-user-actions-session": AtomicInsightUserActionsSession; "atomic-insight-user-actions-timeline": AtomicInsightUserActionsTimeline; - "atomic-insight-user-actions-toggle": AtomicInsightUserActionsToggle; "atomic-ipx-body": AtomicIpxBody; "atomic-ipx-button": AtomicIpxButton; "atomic-ipx-embedded": AtomicIpxEmbedded; @@ -2970,11 +2922,6 @@ declare module "@stencil/core" { * @example */ "atomic-insight-user-actions-timeline": LocalJSX.AtomicInsightUserActionsTimeline & JSXBase.HTMLAttributes; - /** - * Internal component of the atomic-insight-interface - * The `atomic-insight-user-actions-toggle` component displays a button that opens a modal containing the user actions timeline component. - */ - "atomic-insight-user-actions-toggle": LocalJSX.AtomicInsightUserActionsToggle & JSXBase.HTMLAttributes; "atomic-ipx-body": LocalJSX.AtomicIpxBody & JSXBase.HTMLAttributes; "atomic-ipx-button": LocalJSX.AtomicIpxButton & JSXBase.HTMLAttributes; "atomic-ipx-embedded": LocalJSX.AtomicIpxEmbedded & JSXBase.HTMLAttributes; diff --git a/packages/atomic/src/components/insight/index.ts b/packages/atomic/src/components/insight/index.ts index e022fb573d5..2397cc73d6a 100644 --- a/packages/atomic/src/components/insight/index.ts +++ b/packages/atomic/src/components/insight/index.ts @@ -3,4 +3,3 @@ export {AtomicInsightGenerateAnswerButton} from './atomic-insight-generate-answe export {AtomicInsightInterface} from './atomic-insight-interface/atomic-insight-interface.js'; export {AtomicInsightTab} from './atomic-insight-tab/atomic-insight-tab.js'; export {AtomicInsightTabs} from './atomic-insight-tabs/atomic-insight-tabs.js'; -export {AtomicInsightUserActionsToggle} from './user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.js'; diff --git a/packages/atomic/src/components/insight/lazy-index.ts b/packages/atomic/src/components/insight/lazy-index.ts index 6b471c8b335..60500915b59 100644 --- a/packages/atomic/src/components/insight/lazy-index.ts +++ b/packages/atomic/src/components/insight/lazy-index.ts @@ -10,10 +10,6 @@ export default { await import('./atomic-insight-tab/atomic-insight-tab.js'), 'atomic-insight-tabs': async () => await import('./atomic-insight-tabs/atomic-insight-tabs.js'), - 'atomic-insight-user-actions-toggle': async () => - await import( - './user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.js' - ), } as Record Promise>; export type * from './index.js'; diff --git a/packages/atomic/src/utils/custom-element-tags.ts b/packages/atomic/src/utils/custom-element-tags.ts index 85485a08de3..33f884e2665 100644 --- a/packages/atomic/src/utils/custom-element-tags.ts +++ b/packages/atomic/src/utils/custom-element-tags.ts @@ -61,6 +61,7 @@ export const ATOMIC_CUSTOM_ELEMENT_TAGS = new Set([ 'atomic-insight-interface', 'atomic-insight-tab', 'atomic-insight-tabs', + 'atomic-insight-user-actions-toggle', 'atomic-ipx-tab', 'atomic-ipx-tabs', 'atomic-layout-section', From 7a2f25306efeae76d04ce5cc521a55e467459ad4 Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Fri, 2 Jan 2026 09:59:31 -0500 Subject: [PATCH 5/8] done --- .../atomic-insight-user-actions-toggle.mdx | 31 +++ ...nsight-user-actions-toggle.new.stories.tsx | 10 +- ...atomic-insight-user-actions-toggle.spec.ts | 177 +++++++++++++++ .../atomic-insight-user-actions-toggle.ts | 4 +- .../atomic-insight-user-actions-toggle.e2e.ts | 0 .../e2e/fixture.ts | 0 .../e2e/page-object.ts | 0 .../atomic/src/components/insight/index.ts | 1 + .../src/components/insight/lazy-index.ts | 4 + .../atomic-insight-user-actions-toggle.pcss | 1 - ...atomic-insight-user-actions-toggle.spec.ts | 207 ------------------ .../atomic-insight-user-actions-toggle.tsx | 101 --------- ...omic-insight-user-actions-toggle.tw.css.ts | 1 - 13 files changed, 220 insertions(+), 317 deletions(-) create mode 100644 packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.mdx rename packages/atomic/src/components/insight/{user-actions => }/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.new.stories.tsx (74%) create mode 100644 packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.spec.ts rename packages/atomic/src/components/insight/{user-actions => }/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts (98%) rename packages/atomic/src/components/insight/{user-actions => }/atomic-insight-user-actions-toggle/e2e/atomic-insight-user-actions-toggle.e2e.ts (100%) rename packages/atomic/src/components/insight/{user-actions => }/atomic-insight-user-actions-toggle/e2e/fixture.ts (100%) rename packages/atomic/src/components/insight/{user-actions => }/atomic-insight-user-actions-toggle/e2e/page-object.ts (100%) delete mode 100644 packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.pcss delete mode 100644 packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.spec.ts delete mode 100644 packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.tsx delete mode 100644 packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.tw.css.ts diff --git a/packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.mdx b/packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.mdx new file mode 100644 index 00000000000..98c2e60bb56 --- /dev/null +++ b/packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.mdx @@ -0,0 +1,31 @@ +import { Meta } from '@storybook/addon-docs/blocks'; +import * as AtomicInsightUserActionsToggleStories from './atomic-insight-user-actions-toggle.new.stories'; +import { AtomicDocTemplate } from '@/storybook-utils/documentation/atomic-doc-template'; + + + + + +The `atomic-insight-user-actions-toggle` component displays a button that, when clicked, opens a modal containing a user actions timeline. This component is designed for Insight Panel interfaces where agents need to view customer activity history related to a support case. + +When this component is clicked, an `atomic-insight-user-actions-modal` is created and displayed, showing the timeline of user actions. + +This component should be placed within the `atomic-insight-interface`. You must provide the `user-id` and `ticket-creation-date-time` properties to identify the user and case context: + +```html + + + +``` + + + + diff --git a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.new.stories.tsx b/packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.new.stories.tsx similarity index 74% rename from packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.new.stories.tsx rename to packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.new.stories.tsx index 70d4a538192..91b78ff3799 100644 --- a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.new.stories.tsx +++ b/packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.new.stories.tsx @@ -1,8 +1,11 @@ import type {Meta, StoryObj as Story} from '@storybook/web-components-vite'; import {getStorybookHelpers} from '@wc-toolkit/storybook-helpers'; +import {MockMachineLearningApi} from '@/storybook-utils/api/machinelearning/mock'; import {parameters} from '@/storybook-utils/common/common-meta-parameters'; import {wrapInInsightInterface} from '@/storybook-utils/insight/insight-interface-wrapper'; +const mockMachineLearningApi = new MockMachineLearningApi(); + const {decorator, play} = wrapInInsightInterface(); const {events, args, argTypes, template} = getStorybookHelpers( 'atomic-insight-user-actions-toggle', @@ -11,7 +14,7 @@ const {events, args, argTypes, template} = getStorybookHelpers( const meta: Meta = { component: 'atomic-insight-user-actions-toggle', - title: 'Insight/UserActionsToggle', + title: 'Insight/User Actions Toggle', id: 'atomic-insight-user-actions-toggle', render: (args) => template(args), @@ -21,6 +24,7 @@ const meta: Meta = { actions: { handles: events, }, + msw: {handlers: [...mockMachineLearningApi.handlers]}, }, args, argTypes, @@ -29,6 +33,4 @@ const meta: Meta = { export default meta; -export const Default: Story = { - name: 'atomic-insight-user-actions-toggle', -}; +export const Default: Story = {}; diff --git a/packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.spec.ts b/packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.spec.ts new file mode 100644 index 00000000000..53766b62de7 --- /dev/null +++ b/packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.spec.ts @@ -0,0 +1,177 @@ +import { + buildUserActions as buildInsightUserActions, + type UserActionsState as InsightUserActionsState, +} from '@coveo/headless/insight'; +import {html} from 'lit'; +import {describe, expect, it, vi} from 'vitest'; +import {userEvent} from 'vitest/browser'; +import { + defaultBindings, + renderInAtomicInsightInterface, +} from '@/vitest-utils/testing-helpers/fixtures/atomic/insight/atomic-insight-interface-fixture'; +import {buildFakeUserActions} from '@/vitest-utils/testing-helpers/fixtures/headless/insight/user-actions-controller'; +import type {AtomicInsightUserActionsToggle} from './atomic-insight-user-actions-toggle'; +import './atomic-insight-user-actions-toggle'; + +vi.mock('@coveo/headless/insight', {spy: true}); + +describe('atomic-insight-user-actions-toggle', () => { + const renderUserActionsToggle = async ({ + userId = 'test-user', + ticketCreationDateTime = '2024-01-01T00:00:00Z', + excludedCustomActions = [] as string[], + userActionsState = {} as Partial, + } = {}) => { + vi.mocked(buildInsightUserActions).mockReturnValue( + buildFakeUserActions(userActionsState) + ); + + const {element, atomicInterface} = + await renderInAtomicInsightInterface({ + template: html` + + `, + selector: 'atomic-insight-user-actions-toggle', + bindings: defaultBindings, + }); + + const getButton = () => element.shadowRoot?.querySelector('button'); + const getModal = () => + atomicInterface.querySelector('atomic-insight-user-actions-modal'); + + return {element, atomicInterface, getButton, getModal}; + }; + + describe('#initialize', () => { + it('should call buildInsightUserActions with the engine and options', async () => { + const {atomicInterface} = await renderUserActionsToggle({ + userId: 'test-user', + ticketCreationDateTime: '2024-01-01T00:00:00Z', + excludedCustomActions: ['custom-event'], + }); + + expect(buildInsightUserActions).toHaveBeenCalledWith( + atomicInterface.bindings.engine, + { + options: { + ticketCreationDate: '2024-01-01T00:00:00Z', + excludedCustomActions: ['custom-event'], + }, + } + ); + }); + + it('should set this.userActions to the user actions controller', async () => { + const {element} = await renderUserActionsToggle(); + + expect(element.userActions).toBe( + vi.mocked(buildInsightUserActions).mock.results[0].value + ); + }); + + it('should bind state to controller', async () => { + const {element} = await renderUserActionsToggle({ + userActionsState: { + loading: true, + excludedCustomActions: ['test-event'], + }, + }); + + expect(element.userActionsState.loading).toBe(true); + expect(element.userActionsState.excludedCustomActions).toEqual([ + 'test-event', + ]); + }); + }); + + describe('when rendering', () => { + it('should render the button with the correct aria-label', async () => { + const {getButton} = await renderUserActionsToggle(); + + expect(getButton()).toHaveAttribute('aria-label', 'User actions'); + }); + + it('should render the button with the correct title', async () => { + const {getButton} = await renderUserActionsToggle(); + + expect(getButton()).toHaveAttribute('title', 'User actions'); + }); + + it('should render the button with the correct part prefix', async () => { + const {element} = await renderUserActionsToggle(); + const container = element.shadowRoot?.querySelector( + '[part="insight-user-actions-toggle-container"]' + ); + + expect(container).toBeInTheDocument(); + }); + + it('should render the icon within the button', async () => { + const {element} = await renderUserActionsToggle(); + const icon = element.shadowRoot?.querySelector('atomic-icon'); + + expect(icon).toBeInTheDocument(); + expect(icon).toHaveAttribute('part', 'insight-user-actions-toggle-icon'); + }); + }); + + describe('modal creation and interaction', () => { + it('should create the modal when rendered', async () => { + const {getModal} = await renderUserActionsToggle(); + + expect(getModal()).toBeInTheDocument(); + }); + + it('should set the openButton of the modal to the button', async () => { + const {getButton, getModal} = await renderUserActionsToggle(); + + expect(getModal()?.openButton).toBe(getButton()); + }); + + it('should set the userId of the modal to the component prop', async () => { + const {getModal} = await renderUserActionsToggle({userId: 'custom-user'}); + + expect(getModal()?.userId).toBe('custom-user'); + }); + + it('should set the ticketCreationDateTime of the modal to the component prop', async () => { + const {getModal} = await renderUserActionsToggle({ + ticketCreationDateTime: '2024-06-15T12:00:00Z', + }); + + expect(getModal()?.ticketCreationDateTime).toBe('2024-06-15T12:00:00Z'); + }); + + it('should set the excludedCustomActions of the modal to the component prop', async () => { + const {getModal} = await renderUserActionsToggle({ + excludedCustomActions: ['action1', 'action2'], + }); + + expect(getModal()?.excludedCustomActions).toEqual(['action1', 'action2']); + }); + + it('should open the modal when the button is clicked', async () => { + const {getButton, getModal} = await renderUserActionsToggle(); + + await userEvent.click(getButton()!); + + expect(getModal()?.isOpen).toBe(true); + }); + + it('should call logOpenUserActions when the button is clicked', async () => { + const {element, getButton} = await renderUserActionsToggle(); + const logOpenUserActionsSpy = vi.spyOn( + element.userActions, + 'logOpenUserActions' + ); + + await userEvent.click(getButton()!); + + expect(logOpenUserActionsSpy).toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts b/packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts similarity index 98% rename from packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts rename to packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts index 676218e4066..5948e106d50 100644 --- a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts +++ b/packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts @@ -19,8 +19,6 @@ import Clockicon from '@/src/images/clock.svg'; /** * The `atomic-insight-user-actions-toggle` component displays a button that opens a modal containing the user actions timeline component. * This is an internal component of the atomic-insight-interface. - * - * @internal */ @customElement('atomic-insight-user-actions-toggle') @bindings() @@ -44,7 +42,7 @@ export class AtomicInsightUserActionsToggle /** * The ID of the user whose actions are being displayed. */ - @property({type: String}) + @property({type: String, attribute: 'user-id'}) public userId!: string; /** diff --git a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/e2e/atomic-insight-user-actions-toggle.e2e.ts b/packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/e2e/atomic-insight-user-actions-toggle.e2e.ts similarity index 100% rename from packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/e2e/atomic-insight-user-actions-toggle.e2e.ts rename to packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/e2e/atomic-insight-user-actions-toggle.e2e.ts diff --git a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/e2e/fixture.ts b/packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/e2e/fixture.ts similarity index 100% rename from packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/e2e/fixture.ts rename to packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/e2e/fixture.ts diff --git a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/e2e/page-object.ts b/packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/e2e/page-object.ts similarity index 100% rename from packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/e2e/page-object.ts rename to packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/e2e/page-object.ts diff --git a/packages/atomic/src/components/insight/index.ts b/packages/atomic/src/components/insight/index.ts index 2397cc73d6a..5aa0a4220d2 100644 --- a/packages/atomic/src/components/insight/index.ts +++ b/packages/atomic/src/components/insight/index.ts @@ -3,3 +3,4 @@ export {AtomicInsightGenerateAnswerButton} from './atomic-insight-generate-answe export {AtomicInsightInterface} from './atomic-insight-interface/atomic-insight-interface.js'; export {AtomicInsightTab} from './atomic-insight-tab/atomic-insight-tab.js'; export {AtomicInsightTabs} from './atomic-insight-tabs/atomic-insight-tabs.js'; +export {AtomicInsightUserActionsToggle} from './atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.js'; diff --git a/packages/atomic/src/components/insight/lazy-index.ts b/packages/atomic/src/components/insight/lazy-index.ts index 60500915b59..f6f9799b9e5 100644 --- a/packages/atomic/src/components/insight/lazy-index.ts +++ b/packages/atomic/src/components/insight/lazy-index.ts @@ -10,6 +10,10 @@ export default { await import('./atomic-insight-tab/atomic-insight-tab.js'), 'atomic-insight-tabs': async () => await import('./atomic-insight-tabs/atomic-insight-tabs.js'), + 'atomic-insight-user-actions-toggle': async () => + await import( + './atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.js' + ), } as Record Promise>; export type * from './index.js'; diff --git a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.pcss b/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.pcss deleted file mode 100644 index 7a0133e5e82..00000000000 --- a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.pcss +++ /dev/null @@ -1 +0,0 @@ -@import '../../../../global/global.pcss'; diff --git a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.spec.ts b/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.spec.ts deleted file mode 100644 index 86a6b521d36..00000000000 --- a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.spec.ts +++ /dev/null @@ -1,207 +0,0 @@ -import { - buildUserActions as buildInsightUserActions, - type UserActionsState as InsightUserActionsState, -} from '@coveo/headless/insight'; -import {html} from 'lit'; -import {beforeEach, describe, expect, it, vi} from 'vitest'; -import {userEvent} from 'vitest/browser'; -import {fixture} from '@/vitest-utils/testing-helpers/fixture.js'; -import {buildFakeInsightEngine} from '@/vitest-utils/testing-helpers/fixtures/headless/insight/engine.js'; -import {buildFakeUserActions} from '@/vitest-utils/testing-helpers/fixtures/headless/insight/user-actions-controller'; -import type {AtomicInsightUserActionsToggle} from './atomic-insight-user-actions-toggle'; -import './atomic-insight-user-actions-toggle'; - -vi.mock('@coveo/headless/insight', {spy: true}); - -describe('atomic-insight-user-actions-toggle', () => { - let mockEngine: ReturnType; - let element: AtomicInsightUserActionsToggle; - - beforeEach(() => { - mockEngine = buildFakeInsightEngine(); - }); - - const setupElement = async ( - props: { - userId?: string; - ticketCreationDateTime?: string; - excludedCustomActions?: string[]; - } = {}, - userActionsState: Partial = {} - ) => { - vi.mocked(buildInsightUserActions).mockReturnValue( - buildFakeUserActions(userActionsState) - ); - - element = await fixture( - html`` - ); - - // Mock the bindings - element.bindings = { - engine: mockEngine, - i18n: { - t: (key: string) => { - const translations: Record = { - 'user-actions': 'User actions', - }; - return translations[key] || key; - }, - } as never, - store: {} as never, - interfaceElement: {} as never, - createStyleElement: vi.fn(), - createScriptElement: vi.fn(), - }; - - // Initialize the component - element.initialize(); - await element.updateComplete; - - return element; - }; - - const getButton = () => element.shadowRoot?.querySelector('button'); - const getModal = () => - (element.getRootNode() as Document | ShadowRoot)?.querySelector( - 'atomic-insight-user-actions-modal' - ); - - describe('#initialize', () => { - it('should call buildInsightUserActions with the engine and options', async () => { - await setupElement({ - userId: 'test-user', - ticketCreationDateTime: '2024-01-01T00:00:00Z', - excludedCustomActions: ['custom-event'], - }); - - expect(buildInsightUserActions).toHaveBeenCalledWith(mockEngine, { - options: { - ticketCreationDate: '2024-01-01T00:00:00Z', - excludedCustomActions: ['custom-event'], - }, - }); - }); - - it('should set this.userActions to the user actions controller', async () => { - await setupElement(); - - expect(element.userActions).toBe( - vi.mocked(buildInsightUserActions).mock.results[0].value - ); - }); - - it('should bind state to controller', async () => { - await setupElement( - {}, - { - loading: true, - excludedCustomActions: ['test-event'], - } - ); - - expect(element.userActionsState.loading).toBe(true); - expect(element.userActionsState.excludedCustomActions).toEqual([ - 'test-event', - ]); - }); - }); - - describe('rendering', () => { - it('should render the button with the correct aria-label', async () => { - await setupElement(); - const button = getButton(); - - expect(button).toHaveAttribute('aria-label', 'User actions'); - }); - - it('should render the button with the correct title', async () => { - await setupElement(); - const button = getButton(); - - expect(button).toHaveAttribute('title', 'User actions'); - }); - - it('should render the button with the correct part prefix', async () => { - await setupElement(); - const container = element.shadowRoot?.querySelector( - '[part="insight-user-actions-toggle-container"]' - ); - - expect(container).toBeInTheDocument(); - }); - - it('should render the icon within the button', async () => { - await setupElement(); - const icon = element.shadowRoot?.querySelector('atomic-icon'); - - expect(icon).toBeInTheDocument(); - expect(icon).toHaveAttribute('part', 'insight-user-actions-toggle-icon'); - }); - }); - - describe('modal creation and interaction', () => { - it('should create the modal when rendered', async () => { - await setupElement(); - const modal = getModal(); - - expect(modal).toBeInTheDocument(); - }); - - it('should set the openButton of the modal to the button', async () => { - await setupElement(); - const button = getButton(); - const modal = getModal(); - - expect(modal?.openButton).toBe(button); - }); - - it('should set the userId of the modal to the component prop', async () => { - await setupElement({userId: 'custom-user'}); - const modal = getModal(); - - expect(modal?.userId).toBe('custom-user'); - }); - - it('should set the ticketCreationDateTime of the modal to the component prop', async () => { - await setupElement({ticketCreationDateTime: '2024-06-15T12:00:00Z'}); - const modal = getModal(); - - expect(modal?.ticketCreationDateTime).toBe('2024-06-15T12:00:00Z'); - }); - - it('should set the excludedCustomActions of the modal to the component prop', async () => { - await setupElement({excludedCustomActions: ['action1', 'action2']}); - const modal = getModal(); - - expect(modal?.excludedCustomActions).toEqual(['action1', 'action2']); - }); - - it('should open the modal when the button is clicked', async () => { - await setupElement(); - const button = getButton(); - const modal = getModal(); - - await userEvent.click(button!); - - expect(modal?.isOpen).toBe(true); - }); - - it('should call logOpenUserActions when the button is clicked', async () => { - await setupElement(); - const logOpenUserActionsSpy = vi.spyOn( - element.userActions, - 'logOpenUserActions' - ); - const button = getButton(); - - await userEvent.click(button!); - - expect(logOpenUserActionsSpy).toHaveBeenCalled(); - }); - }); -}); diff --git a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.tsx b/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.tsx deleted file mode 100644 index 622cd87a245..00000000000 --- a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { - buildUserActions as buildInsightUserActions, - UserActions as InsightUserActions, - UserActionsState as InsightUserActionsState, -} from '@coveo/headless/insight'; -import {Component, h, Prop, Element, State} from '@stencil/core'; -import Clockicon from '../../../../images/clock.svg'; -import { - BindStateToController, - InitializableComponent, - InitializeBindings, -} from '../../../../utils/initialization-utils'; -import {IconButton} from '../../../common/stencil-iconButton'; -import {InsightBindings} from '../../atomic-insight-interface/atomic-insight-interface'; - -/** - * Internal component of the atomic-insight-interface - * The `atomic-insight-user-actions-toggle` component displays a button that opens a modal containing the user actions timeline component. - */ -@Component({ - tag: 'atomic-insight-user-actions-toggle', - styleUrl: 'atomic-insight-user-actions-toggle.pcss', - shadow: true, -}) -export class AtomicInsightUserActionsToggle - implements InitializableComponent -{ - @Element() public host!: HTMLElement; - @InitializeBindings() public bindings!: InsightBindings; - public userActions!: InsightUserActions; - @BindStateToController('userActions') - @State() - public userActionsState!: InsightUserActionsState; - @State() public error!: Error; - - /** - * The ID of the user whose actions are being displayed. - */ - @Prop() public userId!: string; - /** - * The date and time when the case was created. For example "2024-01-01T00:00:00Z" - */ - @Prop() public ticketCreationDateTime!: string; - /** - * The names of custom events to exclude. - */ - @Prop() public excludedCustomActions: string[] = []; - - public initialize() { - this.userActions = buildInsightUserActions(this.bindings.engine, { - options: { - ticketCreationDate: this.ticketCreationDateTime, - excludedCustomActions: this.excludedCustomActions, - }, - }); - } - - private buttonRef?: HTMLButtonElement; - private modalRef?: HTMLAtomicInsightUserActionsModalElement; - - private enableModal() { - this.modalRef && (this.modalRef.isOpen = true); - this.userActions.logOpenUserActions(); - } - - private loadModal() { - if (this.modalRef) { - return; - } - - this.modalRef = document.createElement('atomic-insight-user-actions-modal'); - - this.host.insertAdjacentElement('beforebegin', this.modalRef); - this.modalRef.openButton = this.buttonRef; - this.modalRef.userId = this.userId; - this.modalRef.ticketCreationDateTime = this.ticketCreationDateTime; - this.modalRef.excludedCustomActions = this.excludedCustomActions; - } - - public render() { - return ( - { - this.enableModal(); - }} - title={this.bindings.i18n.t('user-actions')} - buttonRef={(button?: HTMLButtonElement) => { - if (!button) { - return; - } - this.buttonRef = button; - this.loadModal(); - }} - /> - ); - } -} diff --git a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.tw.css.ts b/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.tw.css.ts deleted file mode 100644 index b8c350c9396..00000000000 --- a/packages/atomic/src/components/insight/user-actions/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.tw.css.ts +++ /dev/null @@ -1 +0,0 @@ -// @reference '@/src/utils/coveo.tw.css'; From fee61a8a2f13a83ad26f43b5b5dacf9db8425b79 Mon Sep 17 00:00:00 2001 From: "developer-experience-bot[bot]" <91079284+developer-experience-bot[bot]@users.noreply.github.com> Date: Fri, 2 Jan 2026 15:04:56 +0000 Subject: [PATCH 6/8] Add generated files --- .../atomic-react/src/components/commerce/components.ts | 7 +++++++ packages/atomic-react/src/components/search/components.ts | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/packages/atomic-react/src/components/commerce/components.ts b/packages/atomic-react/src/components/commerce/components.ts index 6bcfecff5f8..63ea567f89f 100644 --- a/packages/atomic-react/src/components/commerce/components.ts +++ b/packages/atomic-react/src/components/commerce/components.ts @@ -31,6 +31,7 @@ import { AtomicIcon as LitAtomicIcon, AtomicInsightGenerateAnswerButton as LitAtomicInsightGenerateAnswerButton, AtomicInsightInterface as LitAtomicInsightInterface, + AtomicInsightUserActionsToggle as LitAtomicInsightUserActionsToggle, AtomicLayoutSection as LitAtomicLayoutSection, AtomicNumericRange as LitAtomicNumericRange, AtomicProduct as LitAtomicProduct, @@ -251,6 +252,12 @@ export const AtomicInsightInterface = createComponent({ elementClass: LitAtomicInsightInterface, }); +export const AtomicInsightUserActionsToggle = createComponent({ + tagName: 'atomic-insight-user-actions-toggle', + react: React, + elementClass: LitAtomicInsightUserActionsToggle, +}); + export const AtomicLayoutSection = createComponent({ tagName: 'atomic-layout-section', react: React, diff --git a/packages/atomic-react/src/components/search/components.ts b/packages/atomic-react/src/components/search/components.ts index db3743b8a99..b054d83976f 100644 --- a/packages/atomic-react/src/components/search/components.ts +++ b/packages/atomic-react/src/components/search/components.ts @@ -20,6 +20,7 @@ import { AtomicIcon as LitAtomicIcon, AtomicInsightGenerateAnswerButton as LitAtomicInsightGenerateAnswerButton, AtomicInsightInterface as LitAtomicInsightInterface, + AtomicInsightUserActionsToggle as LitAtomicInsightUserActionsToggle, AtomicLayoutSection as LitAtomicLayoutSection, AtomicLoadMoreResults as LitAtomicLoadMoreResults, AtomicNoResults as LitAtomicNoResults, @@ -207,6 +208,12 @@ export const AtomicInsightInterface = createComponent({ elementClass: LitAtomicInsightInterface, }); +export const AtomicInsightUserActionsToggle = createComponent({ + tagName: 'atomic-insight-user-actions-toggle', + react: React, + elementClass: LitAtomicInsightUserActionsToggle, +}); + export const AtomicLayoutSection = createComponent({ tagName: 'atomic-layout-section', react: React, From cc3b2fc39322833bddca3d9e72f438017f027591 Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Thu, 8 Jan 2026 10:51:50 -0500 Subject: [PATCH 7/8] Update atomic-insight-user-actions-toggle.ts --- .../atomic-insight-user-actions-toggle.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts b/packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts index 5948e106d50..8b4c473e5c1 100644 --- a/packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts +++ b/packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts @@ -14,7 +14,7 @@ import {bindings} from '@/src/decorators/bindings'; import {errorGuard} from '@/src/decorators/error-guard'; import type {InitializableComponent} from '@/src/decorators/types'; import {withTailwindStyles} from '@/src/decorators/with-tailwind-styles.js'; -import Clockicon from '@/src/images/clock.svg'; +import ClockIcon from '../../../images/clock.svg'; /** * The `atomic-insight-user-actions-toggle` component displays a button that opens a modal containing the user actions timeline component. @@ -97,7 +97,7 @@ export class AtomicInsightUserActionsToggle props: { partPrefix: 'insight-user-actions-toggle', style: 'outline-neutral', - icon: Clockicon, + icon: ClockIcon, ariaLabel: this.bindings.i18n.t('user-actions'), onClick: () => this.enableModal(), title: this.bindings.i18n.t('user-actions'), From ffc796f3f80cd5653a7eef38d6e6661d8aec95db Mon Sep 17 00:00:00 2001 From: Alex Prudhomme <78121423+alexprudhomme@users.noreply.github.com> Date: Thu, 8 Jan 2026 10:55:37 -0500 Subject: [PATCH 8/8] merge --- .../atomic-react/src/components/commerce/components.ts | 7 +++++++ packages/atomic-react/src/components/search/components.ts | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/packages/atomic-react/src/components/commerce/components.ts b/packages/atomic-react/src/components/commerce/components.ts index ba0198924fc..08b5e38a2f1 100644 --- a/packages/atomic-react/src/components/commerce/components.ts +++ b/packages/atomic-react/src/components/commerce/components.ts @@ -39,6 +39,7 @@ import { AtomicInsightQueryError as LitAtomicInsightQueryError, AtomicInsightQuerySummary as LitAtomicInsightQuerySummary, AtomicInsightResultTemplate as LitAtomicInsightResultTemplate, + AtomicInsightUserActionsToggle as LitAtomicInsightUserActionsToggle, AtomicLayoutSection as LitAtomicLayoutSection, AtomicNumericRange as LitAtomicNumericRange, AtomicProduct as LitAtomicProduct, @@ -307,6 +308,12 @@ export const AtomicInsightResultTemplate = createComponent({ elementClass: LitAtomicInsightResultTemplate, }); +export const AtomicInsightUserActionsToggle = createComponent({ + tagName: 'atomic-insight-user-actions-toggle', + react: React, + elementClass: LitAtomicInsightUserActionsToggle, +}); + export const AtomicLayoutSection = createComponent({ tagName: 'atomic-layout-section', react: React, diff --git a/packages/atomic-react/src/components/search/components.ts b/packages/atomic-react/src/components/search/components.ts index 4e05d9c4a27..c3f65d0f431 100644 --- a/packages/atomic-react/src/components/search/components.ts +++ b/packages/atomic-react/src/components/search/components.ts @@ -28,6 +28,7 @@ import { AtomicInsightQueryError as LitAtomicInsightQueryError, AtomicInsightQuerySummary as LitAtomicInsightQuerySummary, AtomicInsightResultTemplate as LitAtomicInsightResultTemplate, + AtomicInsightUserActionsToggle as LitAtomicInsightUserActionsToggle, AtomicLayoutSection as LitAtomicLayoutSection, AtomicLoadMoreResults as LitAtomicLoadMoreResults, AtomicNoResults as LitAtomicNoResults, @@ -267,6 +268,12 @@ export const AtomicInsightResultTemplate = createComponent({ elementClass: LitAtomicInsightResultTemplate, }); +export const AtomicInsightUserActionsToggle = createComponent({ + tagName: 'atomic-insight-user-actions-toggle', + react: React, + elementClass: LitAtomicInsightUserActionsToggle, +}); + export const AtomicLayoutSection = createComponent({ tagName: 'atomic-layout-section', react: React,