UNPKG

coveo-search-ui

Version:

Coveo JavaScript Search Framework

189 lines (164 loc) 5.79 kB
import { $$ } from '../../utils/Dom'; import { l } from '../../strings/Strings'; import { SVGIcons } from '../../utils/SVGIcons'; import { uniqueId } from 'underscore'; const ROOT_CLASSNAME = 'coveo-user-feedback-banner'; const CONTAINER_CLASSNAME = `${ROOT_CLASSNAME}-container`; const LABEL_CLASSNAME = `${ROOT_CLASSNAME}-label`; const BUTTONS_CONTAINER_CLASSNAME = `${ROOT_CLASSNAME}-buttons`; const YES_BUTTON_CLASSNAME = `${ROOT_CLASSNAME}-yes-button`; const NO_BUTTON_CLASSNAME = `${ROOT_CLASSNAME}-no-button`; const BUTTON_ACTIVE_CLASSNAME = `${ROOT_CLASSNAME}-button-active`; const THANK_YOU_BANNER_CLASSNAME = `${ROOT_CLASSNAME}-thanks`; const THANK_YOU_BANNER_ACTIVE_CLASSNAME = `${THANK_YOU_BANNER_CLASSNAME}-active`; const ICON_CLASSNAME = `${THANK_YOU_BANNER_CLASSNAME}-icon`; const EXPLAIN_WHY_CLASSNAME = `${ROOT_CLASSNAME}-explain-why`; const EXPLAIN_WHY_ACTIVE_CLASSNAME = `${EXPLAIN_WHY_CLASSNAME}-active`; enum UsefulState { Unknown, Yes, No } interface IButtonOptions { text: string; className: string; action: Function; icon?: { content: string; className: string; }; attributes?: any; } export const UserFeedbackBannerClassNames = { ROOT_CLASSNAME, CONTAINER_CLASSNAME, LABEL_CLASSNAME, BUTTONS_CONTAINER_CLASSNAME, YES_BUTTON_CLASSNAME, NO_BUTTON_CLASSNAME, BUTTON_ACTIVE_CLASSNAME, THANK_YOU_BANNER_CLASSNAME, THANK_YOU_BANNER_ACTIVE_CLASSNAME, ICON_CLASSNAME, EXPLAIN_WHY_CLASSNAME, EXPLAIN_WHY_ACTIVE_CLASSNAME }; export class UserFeedbackBanner { public explainWhy: HTMLElement; private isUseful = UsefulState.Unknown; private yesButton: HTMLElement; private noButton: HTMLElement; private thankYouBanner: HTMLElement; private labelId: string; constructor(private readonly sendUsefulnessAnalytics: (isUseful: boolean) => void, private readonly onExplainWhyPressed: () => void) { this.labelId = uniqueId(LABEL_CLASSNAME); } public build() { return $$( 'div', { className: ROOT_CLASSNAME, ariaLive: 'polite' }, this.buildContainer(), this.buildThankYouBanner() ).el; } public reset() { this.isUseful = UsefulState.Unknown; $$(this.yesButton).removeClass(BUTTON_ACTIVE_CLASSNAME); $$(this.yesButton).setAttribute('aria-pressed', 'false'); $$(this.noButton).removeClass(BUTTON_ACTIVE_CLASSNAME); $$(this.noButton).setAttribute('aria-pressed', 'false'); $$(this.thankYouBanner).removeClass(THANK_YOU_BANNER_ACTIVE_CLASSNAME); $$(this.explainWhy).removeClass(EXPLAIN_WHY_ACTIVE_CLASSNAME); } private buildContainer() { return $$( 'div', { className: CONTAINER_CLASSNAME, ariaLabelledby: this.labelId }, this.buildLabel(), this.buildButtons() ).el; } private buildLabel() { return $$('span', { className: LABEL_CLASSNAME, id: this.labelId }, l('UsefulnessFeedbackRequest')).el; } private buildThankYouBanner() { this.thankYouBanner = $$('div', { className: THANK_YOU_BANNER_CLASSNAME }).el; const text = $$('span', {}, l('UsefulnessFeedbackThankYou')).el; this.thankYouBanner.appendChild(text); this.explainWhy = this.buildButton({ text: l('UsefulnessFeedbackExplainWhy'), className: EXPLAIN_WHY_CLASSNAME, action: () => this.requestExplaination() }); this.thankYouBanner.appendChild(this.explainWhy); return this.thankYouBanner; } private buildButtons() { const buttonsContainer = $$('div', { className: BUTTONS_CONTAINER_CLASSNAME }).el; this.yesButton = this.buildButton({ text: l('Yes'), className: YES_BUTTON_CLASSNAME, action: () => this.showThankYouBanner(true), icon: { className: ICON_CLASSNAME, content: SVGIcons.icons.checkYes }, attributes: { ariaPressed: false, ariaDescribedby: this.labelId } }); this.yesButton.setAttribute('aria-pressed', 'false'); buttonsContainer.appendChild(this.yesButton); this.noButton = this.buildButton({ text: l('No'), className: NO_BUTTON_CLASSNAME, action: () => this.showThankYouBanner(false), icon: { className: ICON_CLASSNAME, content: SVGIcons.icons.clearSmall }, attributes: { ariaPressed: false, ariaDescribedby: this.labelId } }); buttonsContainer.appendChild(this.noButton); return buttonsContainer; } private buildButton(options: IButtonOptions) { const button = $$('button', { ...(options.attributes || {}), className: options.className, type: 'button' }).el; if (options.icon) { const icon = $$('span', { className: options.icon.className }, options.icon.content).el; button.appendChild(icon); const text = $$('span', {}, options.text).el; button.appendChild(text); } else { button.innerText = options.text; } button.addEventListener('click', () => options.action()); return button; } private showThankYouBanner(isUseful: boolean) { if (this.isUseful !== UsefulState.Unknown && isUseful === (this.isUseful === UsefulState.Yes)) { return; } this.isUseful = isUseful ? UsefulState.Yes : UsefulState.No; $$(this.yesButton).toggleClass(BUTTON_ACTIVE_CLASSNAME, isUseful); $$(this.yesButton).setAttribute('aria-pressed', `${isUseful}`); $$(this.noButton).toggleClass(BUTTON_ACTIVE_CLASSNAME, !isUseful); $$(this.noButton).setAttribute('aria-pressed', `${!isUseful}`); $$(this.thankYouBanner).addClass(THANK_YOU_BANNER_ACTIVE_CLASSNAME); $$(this.explainWhy).toggleClass(EXPLAIN_WHY_ACTIVE_CLASSNAME, !isUseful); this.sendUsefulnessAnalytics(isUseful); } private requestExplaination() { this.onExplainWhyPressed(); } }