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, diff --git a/packages/atomic/src/components.d.ts b/packages/atomic/src/components.d.ts index 6e7868abd35..514743ea412 100644 --- a/packages/atomic/src/components.d.ts +++ b/packages/atomic/src/components.d.ts @@ -506,24 +506,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; @@ -1200,16 +1182,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; } @@ -1478,7 +1450,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; @@ -1967,24 +1938,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; @@ -2405,7 +2358,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; @@ -2475,11 +2427,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/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/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 new file mode 100644 index 00000000000..8b4c473e5c1 --- /dev/null +++ b/packages/atomic/src/components/insight/atomic-insight-user-actions-toggle/atomic-insight-user-actions-toggle.ts @@ -0,0 +1,120 @@ +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 {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'; +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. + * This is an internal component of the atomic-insight-interface. + */ +@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, attribute: 'user-id'}) + 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/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 3f74a335dd9..9b77a60d325 100644 --- a/packages/atomic/src/components/insight/index.ts +++ b/packages/atomic/src/components/insight/index.ts @@ -11,3 +11,4 @@ export {AtomicInsightQuerySummary} from './atomic-insight-query-summary/atomic-i export {AtomicInsightResultTemplate} from './atomic-insight-result-template/atomic-insight-result-template.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 23c36789b0f..e2f6deed708 100644 --- a/packages/atomic/src/components/insight/lazy-index.ts +++ b/packages/atomic/src/components/insight/lazy-index.ts @@ -32,6 +32,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.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/utils/custom-element-tags.ts b/packages/atomic/src/utils/custom-element-tags.ts index aff90a8ebef..e189f799854 100644 --- a/packages/atomic/src/utils/custom-element-tags.ts +++ b/packages/atomic/src/utils/custom-element-tags.ts @@ -69,6 +69,7 @@ export const ATOMIC_CUSTOM_ELEMENT_TAGS = new Set([ 'atomic-insight-result-template', 'atomic-insight-tab', 'atomic-insight-tabs', + 'atomic-insight-user-actions-toggle', 'atomic-ipx-tab', 'atomic-ipx-tabs', 'atomic-layout-section', 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; +};