diff --git a/package.json b/package.json index 37a16496..feba70be 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,8 @@ "module": "./bin/es6/Index.js", "types": "./bin/typings", "sideEffects": [ + "./bin/es6/components/AttachResult/Strings.js", + "./bin/es6/components/ResultsFilter/Strings.js", "./bin/es6/components/UserActions/Strings.js", "./bin/es6/components/ViewedByCustomer/Strings.js" ], diff --git a/src/components/AttachResult/AttachResult.ts b/src/components/AttachResult/AttachResult.ts index 1de00a09..2af70ddb 100644 --- a/src/components/AttachResult/AttachResult.ts +++ b/src/components/AttachResult/AttachResult.ts @@ -8,10 +8,16 @@ import { IAnalyticsCaseAttachMeta, IAnalyticsCaseDetachMeta, analyticsActionCauseList, - IAnalyticsActionCause + IAnalyticsActionCause, + l } from 'coveo-search-ui'; import { paperclipIcon } from '../../utils/icons'; +import { AttachResultEvents, IAttachResultEventArgs } from './Events'; +import './Strings'; +/** + * Possible options to configure the **AttachResult** component. + */ export interface IAttachResultOptions { /** Specifies the tooltip displayed when the result is not attached. */ attachCaption?: string; @@ -37,12 +43,12 @@ export class AttachResult extends Component { private buttonElement: HTMLElement; private tooltipElement: HTMLElement; - static options: IAttachResultOptions = { + public static readonly options: IAttachResultOptions = { attachCaption: ComponentOptions.buildStringOption({ - defaultValue: 'Attach Result' + defaultValue: l(`${AttachResult.ID}_Attach`) }), detachCaption: ComponentOptions.buildStringOption({ - defaultValue: 'Detach Result' + defaultValue: l(`${AttachResult.ID}_Detach`) }), attach: ComponentOptions.buildCustomOption( name => (result: IQueryResult) => @@ -111,6 +117,7 @@ export class AttachResult extends Component { .then(() => { this.attached = true; this.logAnalyticsCaseEvent(analyticsActionCauseList.caseAttach); + Coveo.$$(this.root).trigger(AttachResultEvents.Attach, { queryResult: this.queryResult } as IAttachResultEventArgs); }) .finally(() => { this.setLoading(false); @@ -131,6 +138,7 @@ export class AttachResult extends Component { .then(() => { this.attached = false; this.logAnalyticsCaseEvent(analyticsActionCauseList.caseDetach); + Coveo.$$(this.root).trigger(AttachResultEvents.Detach, { queryResult: this.queryResult } as IAttachResultEventArgs); }) .finally(() => { this.setLoading(false); diff --git a/src/components/AttachResult/Events.ts b/src/components/AttachResult/Events.ts new file mode 100644 index 00000000..e4a07fea --- /dev/null +++ b/src/components/AttachResult/Events.ts @@ -0,0 +1,20 @@ +import { IQueryResult } from 'coveo-search-ui'; + +/** + * List of possible event types triggered by the **AttachResult** component + * when a user interacts with it. + */ +export enum AttachResultEvents { + Attach = 'attach', + Detach = 'detach' +} + +/** + * Arguments sent with the events coming from the **AttachResult** component. + */ +export interface IAttachResultEventArgs { + /** + * The **IQueryResult** that is attached to the **AttachResult** component. + */ + queryResult: IQueryResult; +} diff --git a/src/components/AttachResult/Strings.ts b/src/components/AttachResult/Strings.ts new file mode 100644 index 00000000..3cd215e1 --- /dev/null +++ b/src/components/AttachResult/Strings.ts @@ -0,0 +1,6 @@ +import { Translation, Language } from '../../utils/translation'; + +Translation.register(Language.English, { + AttachResult_Attach: 'Attach Result', + AttachResult_Detach: 'Detach Result' +}); diff --git a/src/components/ResultsFilter/Events.ts b/src/components/ResultsFilter/Events.ts new file mode 100644 index 00000000..3f08028c --- /dev/null +++ b/src/components/ResultsFilter/Events.ts @@ -0,0 +1,16 @@ +/** + * Events triggered by the **ResultsFilter** component. + */ +export enum ResultsFilterEvents { + Click = 'click' +} + +/** + * Arguments sent with the events coming from the **ResultsFilter** component. + */ +export interface IResultsFilterEventArgs { + /** + * Whether the filter is currently checked or not. + */ + checked: boolean; +} diff --git a/src/components/ResultsFilter/ResultsFilter.ts b/src/components/ResultsFilter/ResultsFilter.ts index f8942500..b47945f8 100644 --- a/src/components/ResultsFilter/ResultsFilter.ts +++ b/src/components/ResultsFilter/ResultsFilter.ts @@ -9,13 +9,22 @@ import { IBuildingQueryEventArgs, QueryStateModel, load, - IAttributesChangedEventArg + IAttributesChangedEventArg, + l } from 'coveo-search-ui'; +import { ResultsFilterEvents, IResultsFilterEventArgs } from './Events'; +import './Strings'; +/** + * Metadata sent when an analytics event is sent. + */ export interface IAnalyticsFilteredResultsMeta { filteredResults: boolean; } +/** + * Possible options to configure the **ResultsFilter** component. + */ export interface IResultsFilterOptions { /** Specifies the text displayed next to the checkbox. */ text?: string; @@ -36,7 +45,7 @@ export class ResultsFilter extends Component { static options: IResultsFilterOptions = { text: ComponentOptions.buildStringOption({ - defaultValue: 'Filter Results' + defaultValue: l(`${ResultsFilter.ID}_Label`) }), field: ComponentOptions.buildStringOption({ defaultValue: 'urihash' @@ -97,6 +106,7 @@ export class ResultsFilter extends Component { private handleCheckboxChange(checkbox: Checkbox) { this.queryStateModel.set(QueryStateModel.getFacetId(ResultsFilter.ID), this.checkbox.isSelected()); this.triggerQuery(); + Coveo.$$(this.root).trigger(ResultsFilterEvents.Click, { checked: this.checkbox.isSelected() } as IResultsFilterEventArgs); } private triggerQuery() { diff --git a/src/components/ResultsFilter/Strings.ts b/src/components/ResultsFilter/Strings.ts new file mode 100644 index 00000000..0e89ee77 --- /dev/null +++ b/src/components/ResultsFilter/Strings.ts @@ -0,0 +1,5 @@ +import { Translation, Language } from '../../utils/translation'; + +Translation.register(Language.English, { + ResultsFilter_Label: 'Filter Results' +}); diff --git a/tests/components/AttachResult/AttachResult.spec.ts b/tests/components/AttachResult/AttachResult.spec.ts index e5002f4b..e4a4557f 100644 --- a/tests/components/AttachResult/AttachResult.spec.ts +++ b/tests/components/AttachResult/AttachResult.spec.ts @@ -1,6 +1,7 @@ import { AttachResult, IAttachResultOptions } from '../../../src/components/AttachResult/AttachResult'; import { Mock, Fake } from 'coveo-search-ui-tests'; import { IQueryResult, Logger } from 'coveo-search-ui'; +import { AttachResultEvents, IAttachResultEventArgs } from '../../../src/components/AttachResult/Events'; describe('AttachResult', () => { let attachResult: Mock.IBasicComponentSetup; @@ -55,6 +56,13 @@ describe('AttachResult', () => { }, 50); }); + it('tooltip should contain the default localized string', async () => { + await attachResult.cmp.attach(); + expect(attachResult.cmp.element.querySelector('.coveo-caption-for-icon').textContent).toBe('Detach Result'); + await attachResult.cmp.detach(); + expect(attachResult.cmp.element.querySelector('.coveo-caption-for-icon').textContent).toBe('Attach Result'); + }); + it('should be detached when isAttached returns false', done => { isAttachedSpy.and.returnValue(Promise.resolve(false)); attachResult = Mock.optionsResultComponentSetup(AttachResult, { isAttached: faker.isAttached }, fakeResult); @@ -129,6 +137,14 @@ describe('AttachResult', () => { await attachResult.cmp.attach(); expect(attachResult.cmp.element.querySelector('.coveo-caption-for-icon').innerHTML).toBe('detach me'); }); + + it('should trigger the attach event', done => { + Coveo.$$(attachResult.env.root).on(AttachResultEvents.Attach, (evt: Event, args: IAttachResultEventArgs) => { + expect(args.queryResult).not.toBeNull(); + done(); + }); + attachResult.cmp.attach(); + }); }); describe('detach', () => { @@ -186,6 +202,14 @@ describe('AttachResult', () => { await attachResult.cmp.detach(); expect(attachResult.cmp.element.querySelector('.coveo-caption-for-icon').innerHTML).toBe('attach me'); }); + + it('should trigger the detach event', done => { + Coveo.$$(attachResult.env.root).on(AttachResultEvents.Detach, (evt: Event, args: IAttachResultEventArgs) => { + expect(args.queryResult).not.toBeNull(); + done(); + }); + attachResult.cmp.detach(); + }); }); describe('click', () => { diff --git a/tests/components/ResultsFilter/ResultsFilter.spec.ts b/tests/components/ResultsFilter/ResultsFilter.spec.ts index 5b1b94ff..8f7aa3fc 100644 --- a/tests/components/ResultsFilter/ResultsFilter.spec.ts +++ b/tests/components/ResultsFilter/ResultsFilter.spec.ts @@ -1,6 +1,8 @@ import { ResultsFilter, IResultsFilterOptions } from '../../../src/components/ResultsFilter/ResultsFilter'; import { Mock, Simulate } from 'coveo-search-ui-tests'; import { QueryStateModel } from 'coveo-search-ui'; +import { ResultsFilterEvents, IResultsFilterEventArgs } from '../../../src/components/ResultsFilter/Events'; +import { Translation, Language } from '../../../src/utils/translation'; describe('ResultsFilter', () => { let filter: Mock.IBasicComponentSetup; @@ -42,6 +44,31 @@ describe('ResultsFilter', () => { expect(filter.cmp.isSelected()).toBeFalsy(); }); + it('should trigger event with checked true when first toggling', () => { + return new Promise(resolve => { + Coveo.$$(filter.env.root).on(ResultsFilterEvents.Click, (evt: Event, args: IResultsFilterEventArgs) => { + expect(args.checked).toBeTruthy(); + resolve(); + }); + filter.cmp.toggle(); + }); + }); + + it('should trigger event with checked false when toggling twice', () => { + return new Promise(resolve => { + filter.cmp.toggle(); + Coveo.$$(filter.env.root).on(ResultsFilterEvents.Click, (evt: Event, args: IResultsFilterEventArgs) => { + expect(args.checked).toBeFalsy(); + resolve(); + }); + filter.cmp.toggle(); + }); + }); + + it('should contain the default label string', () => { + expect(filter.cmp.element.querySelector('span').innerText).toBe('Filter Results'); + }); + describe('when setting options', () => { beforeEach(() => { filter = Mock.optionsComponentSetup(ResultsFilter, {