diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-footer.spec.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-footer.spec.ts new file mode 100644 index 00000000000..f6eaf8ce4c8 --- /dev/null +++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-footer.spec.ts @@ -0,0 +1,53 @@ +import type {i18n} from 'i18next'; +import {html} from 'lit'; +import {beforeAll, describe, expect, it} from 'vitest'; +import {renderFunctionFixture} from '@/vitest-utils/testing-helpers/fixture'; +import {createTestI18n} from '@/vitest-utils/testing-helpers/i18n-utils'; +import {renderSnippetFooter, type SnippetFooterProps} from './snippet-footer'; + +describe('#renderSnippetFooter', () => { + let i18n: i18n; + + beforeAll(async () => { + i18n = await createTestI18n(); + }); + + const renderComponent = async ( + props: Partial = {}, + children = html`` + ) => { + const element = await renderFunctionFixture( + html`${renderSnippetFooter({ + props: { + i18n, + ...props, + }, + })(children)}` + ); + + return element.querySelector('footer') as HTMLElement; + }; + + it('should render a footer element', async () => { + const footer = await renderComponent({}); + expect(footer).toBeInTheDocument(); + }); + + it('should render with footer part', async () => { + const footer = await renderComponent({}); + expect(footer.getAttribute('part')).toBe('footer'); + }); + + it('should render with aria-label', async () => { + const footer = await renderComponent({}); + expect(footer.getAttribute('aria-label')).toBe( + i18n.t('smart-snippet-source') + ); + }); + + it('should render children inside the footer', async () => { + const children = html`

Footer Content

`; + const footer = await renderComponent({}, children); + expect(footer?.textContent).toContain('Footer Content'); + }); +}); diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-footer.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-footer.ts new file mode 100644 index 00000000000..5f9cb4bcb90 --- /dev/null +++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-footer.ts @@ -0,0 +1,20 @@ +import type {i18n} from 'i18next'; +import {html} from 'lit'; +import type {FunctionalComponentWithChildren} from '@/src/utils/functional-component-utils'; + +export interface SnippetFooterProps { + i18n: i18n; +} + +export const renderSnippetFooter: FunctionalComponentWithChildren< + SnippetFooterProps +> = + ({props}) => + (children) => { + return html``; + }; diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-question.spec.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-question.spec.ts new file mode 100644 index 00000000000..edc14542933 --- /dev/null +++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-question.spec.ts @@ -0,0 +1,61 @@ +import {html} from 'lit'; +import {describe, expect, it} from 'vitest'; +import {renderFunctionFixture} from '@/vitest-utils/testing-helpers/fixture'; +import { + renderSnippetQuestion, + type SnippetQuestionProps, +} from './snippet-question'; + +describe('#renderSnippetQuestion', () => { + const renderComponent = async (props: Partial = {}) => { + const element = await renderFunctionFixture( + html`${renderSnippetQuestion({ + props: { + question: 'What is a smart snippet?', + headingLevel: undefined, + ...props, + }, + })}` + ); + + return element.firstElementChild as HTMLElement; + }; + + it('should render the question text', async () => { + const question = await renderComponent({ + question: 'What is a smart snippet?', + }); + expect(question.textContent?.trim()).toBe('What is a smart snippet?'); + }); + + it('should render with text-xl and font-bold classes', async () => { + const question = await renderComponent({}); + expect(question).toHaveClass('text-xl'); + expect(question).toHaveClass('font-bold'); + }); + + it('should render with the question part', async () => { + const question = await renderComponent({}); + expect(question.getAttribute('part')).toBe('question'); + }); + + it('should render as a div when headingLevel is undefined', async () => { + const question = await renderComponent({headingLevel: undefined}); + expect(question.tagName.toLowerCase()).toBe('div'); + }); + + it('should render as a div when headingLevel is 0', async () => { + const question = await renderComponent({headingLevel: 0}); + expect(question.tagName.toLowerCase()).toBe('div'); + }); + + it('should render with incremented heading level', async () => { + const question = await renderComponent({headingLevel: 2}); + expect(question.tagName.toLowerCase()).toBe('h3'); + }); + + it('should render as h2 when headingLevel is 1', async () => { + const question = await renderComponent({headingLevel: 1}); + expect(question.tagName.toLowerCase()).toBe('h2'); + }); +}); diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-question.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-question.ts new file mode 100644 index 00000000000..5e795eaedc2 --- /dev/null +++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-question.ts @@ -0,0 +1,20 @@ +import {html} from 'lit'; +import type {FunctionalComponent} from '@/src/utils/functional-component-utils'; +import {renderHeading} from '../../heading'; + +export interface SnippetQuestionProps { + headingLevel?: number; + question: string; +} + +export const renderSnippetQuestion: FunctionalComponent< + SnippetQuestionProps +> = ({props}) => { + return renderHeading({ + props: { + level: props.headingLevel ? props.headingLevel + 1 : 0, + class: 'text-xl font-bold', + part: 'question', + }, + })(html`${props.question}`); +}; diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-truncated-answer.spec.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-truncated-answer.spec.ts new file mode 100644 index 00000000000..0169bdb726f --- /dev/null +++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-truncated-answer.spec.ts @@ -0,0 +1,67 @@ +import {html} from 'lit'; +import {describe, expect, it} from 'vitest'; +import {renderFunctionFixture} from '@/vitest-utils/testing-helpers/fixture'; +import { + renderSnippetTruncatedAnswer, + type SnippetTruncatedAnswerProps, +} from './snippet-truncated-answer'; + +describe('#renderSnippetTruncatedAnswer', () => { + const renderComponent = async ( + props: Partial = {} + ) => { + const element = await renderFunctionFixture( + html`${renderSnippetTruncatedAnswer({ + props: { + answer: '

This is the answer

', + style: undefined, + ...props, + }, + })}` + ); + + return element.firstElementChild as HTMLElement; + }; + + it('should render a div with truncated-answer part', async () => { + const answer = await renderComponent({}); + expect(answer).toBeInTheDocument(); + expect(answer.getAttribute('part')).toBe('truncated-answer'); + }); + + it('should render atomic-smart-snippet-answer component', async () => { + const answer = await renderComponent({}); + const answerElement = answer.querySelector('atomic-smart-snippet-answer'); + expect(answerElement).toBeInTheDocument(); + }); + + it('should set htmlContent property on atomic-smart-snippet-answer', async () => { + const answer = await renderComponent({answer: '

Test answer

'}); + const answerElement = answer.querySelector( + 'atomic-smart-snippet-answer' + // biome-ignore lint/suspicious/noExplicitAny: testing property access on custom element + ) as any; + expect(answerElement.htmlContent).toBe('

Test answer

'); + }); + + it('should set innerStyle property when style is provided', async () => { + const answer = await renderComponent({style: 'color: red;'}); + const answerElement = answer.querySelector( + 'atomic-smart-snippet-answer' + // biome-ignore lint/suspicious/noExplicitAny: testing property access on custom element + ) as any; + expect(answerElement.innerStyle).toBe('color: red;'); + }); + + it('should set exportparts attribute', async () => { + const answer = await renderComponent({}); + const answerElement = answer.querySelector('atomic-smart-snippet-answer'); + expect(answerElement?.getAttribute('exportparts')).toBe('answer'); + }); + + it('should set part attribute to body', async () => { + const answer = await renderComponent({}); + const answerElement = answer.querySelector('atomic-smart-snippet-answer'); + expect(answerElement?.getAttribute('part')).toBe('body'); + }); +}); diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-truncated-answer.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-truncated-answer.ts new file mode 100644 index 00000000000..0e0546a2d0a --- /dev/null +++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-truncated-answer.ts @@ -0,0 +1,21 @@ +import {html} from 'lit'; +import {ifDefined} from 'lit/directives/if-defined.js'; +import type {FunctionalComponent} from '@/src/utils/functional-component-utils'; + +export interface SnippetTruncatedAnswerProps { + answer: string; + style?: string; +} + +export const renderSnippetTruncatedAnswer: FunctionalComponent< + SnippetTruncatedAnswerProps +> = ({props}) => { + return html`
+ +
`; +}; diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-wrapper.spec.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-wrapper.spec.ts new file mode 100644 index 00000000000..dff0e794112 --- /dev/null +++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-wrapper.spec.ts @@ -0,0 +1,82 @@ +import type {i18n} from 'i18next'; +import {html} from 'lit'; +import {beforeAll, describe, expect, it} from 'vitest'; +import {renderFunctionFixture} from '@/vitest-utils/testing-helpers/fixture'; +import {createTestI18n} from '@/vitest-utils/testing-helpers/i18n-utils'; +import { + renderSnippetWrapper, + type SnippetWrapperProps, +} from './snippet-wrapper'; + +describe('#renderSnippetWrapper', () => { + let i18n: i18n; + + beforeAll(async () => { + i18n = await createTestI18n(); + }); + + const renderComponent = async ( + props: Partial = {}, + children = html`` + ) => { + const element = await renderFunctionFixture( + html`${renderSnippetWrapper({ + props: { + i18n, + headingLevel: undefined, + ...props, + }, + })(children)}` + ); + + return element.querySelector('aside') as HTMLElement; + }; + + it('should render an aside element with aria-label', async () => { + const aside = await renderComponent({}); + expect(aside).toBeInTheDocument(); + expect(aside.getAttribute('aria-label')).toBe(i18n.t('smart-snippet')); + }); + + it('should render an article element with the smart-snippet part', async () => { + const aside = await renderComponent({}); + const article = aside.querySelector('article[part="smart-snippet"]'); + expect(article).toBeInTheDocument(); + }); + + it('should render with background and border classes', async () => { + const aside = await renderComponent({}); + const article = aside.querySelector('article'); + expect(article).toHaveClass('bg-background'); + expect(article).toHaveClass('border-neutral'); + expect(article).toHaveClass('text-on-background'); + expect(article).toHaveClass('rounded-lg'); + expect(article).toHaveClass('border'); + }); + + it('should render a heading with sr-only class', async () => { + const aside = await renderComponent({}); + const heading = aside.querySelector('.sr-only'); + expect(heading).toBeInTheDocument(); + expect(heading?.textContent?.trim()).toBe(i18n.t('smart-snippet')); + }); + + it('should render heading with level 0 when headingLevel is undefined', async () => { + const aside = await renderComponent({headingLevel: undefined}); + const heading = aside.querySelector('div.sr-only'); + expect(heading).toBeInTheDocument(); + }); + + it('should render heading with specified level', async () => { + const aside = await renderComponent({headingLevel: 2}); + const heading = aside.querySelector('h2.sr-only'); + expect(heading).toBeInTheDocument(); + }); + + it('should render children inside the article', async () => { + const children = html`

Test Content

`; + const aside = await renderComponent({}, children); + const article = aside.querySelector('article'); + expect(article?.textContent).toContain('Test Content'); + }); +}); diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-wrapper.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-wrapper.ts new file mode 100644 index 00000000000..2c83db78e44 --- /dev/null +++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/snippet-wrapper.ts @@ -0,0 +1,27 @@ +import type {i18n} from 'i18next'; +import {html} from 'lit'; +import type {FunctionalComponentWithChildren} from '@/src/utils/functional-component-utils'; +import {renderHeading} from '../../heading'; + +export interface SnippetWrapperProps { + headingLevel?: number; + i18n: i18n; +} + +export const renderSnippetWrapper: FunctionalComponentWithChildren< + SnippetWrapperProps +> = + ({props}) => + (children) => { + return html``; + }; diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/smart-snippet-common.tsx b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/stencil-smart-snippet-common.tsx similarity index 100% rename from packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/smart-snippet-common.tsx rename to packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet/stencil-smart-snippet-common.tsx diff --git a/packages/atomic/src/components/common/smart-snippets/smart-snippet-feedback-banner.spec.ts b/packages/atomic/src/components/common/smart-snippets/smart-snippet-feedback-banner.spec.ts new file mode 100644 index 00000000000..4120afb79db --- /dev/null +++ b/packages/atomic/src/components/common/smart-snippets/smart-snippet-feedback-banner.spec.ts @@ -0,0 +1,250 @@ +import type {i18n} from 'i18next'; +import {html} from 'lit'; +import {fireEvent, within} from 'storybook/test'; +import {beforeAll, describe, expect, it, vi} from 'vitest'; +import {renderFunctionFixture} from '@/vitest-utils/testing-helpers/fixture'; +import {createTestI18n} from '@/vitest-utils/testing-helpers/i18n-utils'; +import { + renderSmartSnippetFeedbackBanner, + type SmartSnippetFeedbackBannerProps, +} from './smart-snippet-feedback-banner'; + +describe('#renderSmartSnippetFeedbackBanner', () => { + let i18n: i18n; + + beforeAll(async () => { + i18n = await createTestI18n(); + }); + + const renderFeedbackBanner = async ( + props: Partial = {} + ) => { + const defaultProps: SmartSnippetFeedbackBannerProps = { + i18n, + id: 'test-id', + liked: false, + disliked: false, + feedbackSent: false, + onLike: vi.fn(), + onDislike: vi.fn(), + onPressExplainWhy: vi.fn(), + ...props, + }; + return await renderFunctionFixture( + html`${renderSmartSnippetFeedbackBanner({props: defaultProps})}` + ); + }; + + it('should render the feedback banner', async () => { + const container = await renderFeedbackBanner({}); + const banner = container.querySelector('[part="feedback-banner"]'); + expect(banner).toBeInTheDocument(); + }); + + it('should render with feedback-banner part', async () => { + const container = await renderFeedbackBanner({}); + const banner = container.querySelector('[part="feedback-banner"]'); + expect(banner?.getAttribute('part')).toBe('feedback-banner'); + }); + + it('should render the feedback inquiry text', async () => { + const container = await renderFeedbackBanner({}); + const inquiry = container.querySelector('[part="feedback-inquiry"]'); + expect(inquiry?.textContent?.trim()).toBe( + i18n.t('smart-snippet-feedback-inquiry') + ); + }); + + it('should render the feedback inquiry with correct id', async () => { + const container = await renderFeedbackBanner({id: 'custom-id'}); + const inquiry = container.querySelector('[part="feedback-inquiry"]'); + expect(inquiry?.getAttribute('id')).toBe('feedback-inquiry-custom-id'); + }); + + it('should render like and dislike buttons', async () => { + const container = await renderFeedbackBanner({}); + const likeButton = container.querySelector('[part="feedback-like-button"]'); + const dislikeButton = container.querySelector( + '[part="feedback-dislike-button"]' + ); + expect(likeButton).toBeInTheDocument(); + expect(dislikeButton).toBeInTheDocument(); + }); + + it('should render checkmark icon for like button', async () => { + const container = await renderFeedbackBanner({}); + const likeButton = container.querySelector('[part="feedback-like-button"]'); + const icon = likeButton?.querySelector('atomic-icon'); + expect(icon).toBeInTheDocument(); + }); + + it('should render cross icon for dislike button', async () => { + const container = await renderFeedbackBanner({}); + const dislikeButton = container.querySelector( + '[part="feedback-dislike-button"]' + ); + const icon = dislikeButton?.querySelector('atomic-icon'); + expect(icon).toBeInTheDocument(); + }); + + it('should render radio buttons with correct group name', async () => { + const container = await renderFeedbackBanner({id: 'test-id'}); + const radioButtons = container.querySelectorAll('input[type="radio"]'); + expect(radioButtons.length).toBe(2); + radioButtons.forEach((radio) => { + expect(radio.getAttribute('name')).toBe('feedback-options-test-id'); + }); + }); + + it('should call onLike when like radio button is clicked', async () => { + const onLike = vi.fn(); + const container = await renderFeedbackBanner({onLike}); + const radioButtons = container.querySelectorAll('input[type="radio"]'); + const likeRadio = Array.from(radioButtons).find( + (radio) => (radio as HTMLInputElement).value === i18n.t('yes') + ) as HTMLInputElement; + await fireEvent.click(likeRadio); + expect(onLike).toHaveBeenCalled(); + }); + + it('should call onDislike when dislike radio button is clicked', async () => { + const onDislike = vi.fn(); + const container = await renderFeedbackBanner({onDislike}); + const radioButtons = container.querySelectorAll('input[type="radio"]'); + const dislikeRadio = Array.from(radioButtons).find( + (radio) => (radio as HTMLInputElement).value === i18n.t('no') + ) as HTMLInputElement; + await fireEvent.click(dislikeRadio); + expect(onDislike).toHaveBeenCalled(); + }); + + it('should apply text-success class when liked', async () => { + const container = await renderFeedbackBanner({liked: true}); + const likeButton = container.querySelector('[part="feedback-like-button"]'); + expect(likeButton).toHaveClass('text-success'); + }); + + it('should apply text-error class when disliked', async () => { + const container = await renderFeedbackBanner({disliked: true}); + const dislikeButton = container.querySelector( + '[part="feedback-dislike-button"]' + ); + expect(dislikeButton).toHaveClass('text-error'); + }); + + it('should not show thank you message when neither liked nor disliked', async () => { + const container = await renderFeedbackBanner({ + liked: false, + disliked: false, + }); + const thankYou = container.querySelector('[part="feedback-thank-you"]'); + expect(thankYou).not.toBeInTheDocument(); + }); + + it('should show thank you message when liked', async () => { + const container = await renderFeedbackBanner({liked: true}); + const thankYou = container.querySelector('[part="feedback-thank-you"]'); + expect(thankYou).toBeInTheDocument(); + expect(thankYou?.textContent?.trim()).toBe( + i18n.t('smart-snippet-feedback-thanks') + ); + }); + + it('should show thank you message when disliked', async () => { + const container = await renderFeedbackBanner({disliked: true}); + const thankYou = container.querySelector('[part="feedback-thank-you"]'); + expect(thankYou).toBeInTheDocument(); + }); + + it('should show explain why button when disliked and feedback not sent', async () => { + const container = await renderFeedbackBanner({ + disliked: true, + feedbackSent: false, + }); + const explainWhyButton = within(container).queryByText( + i18n.t('smart-snippet-feedback-explain-why') + ); + expect(explainWhyButton).toBeInTheDocument(); + }); + + it('should not show explain why button when liked', async () => { + const container = await renderFeedbackBanner({ + liked: true, + disliked: false, + feedbackSent: false, + }); + const explainWhyButton = within(container).queryByText( + i18n.t('smart-snippet-feedback-explain-why') + ); + expect(explainWhyButton).not.toBeInTheDocument(); + }); + + it('should not show explain why button when feedback sent', async () => { + const container = await renderFeedbackBanner({ + disliked: true, + feedbackSent: true, + }); + const explainWhyButton = within(container).queryByText( + i18n.t('smart-snippet-feedback-explain-why') + ); + expect(explainWhyButton).not.toBeInTheDocument(); + }); + + it('should call onPressExplainWhy when explain why button is clicked', async () => { + const onPressExplainWhy = vi.fn(); + const container = await renderFeedbackBanner({ + disliked: true, + feedbackSent: false, + onPressExplainWhy, + }); + const explainWhyButton = within(container).getByText( + i18n.t('smart-snippet-feedback-explain-why') + ); + await fireEvent.click(explainWhyButton); + expect(onPressExplainWhy).toHaveBeenCalled(); + }); + + it('should render radiogroup with correct aria-labelledby', async () => { + const container = await renderFeedbackBanner({id: 'test-id'}); + const radiogroup = container.querySelector('[role="radiogroup"]'); + expect(radiogroup?.getAttribute('aria-labelledby')).toBe( + 'feedback-inquiry-test-id' + ); + }); + + it('should render thank you wrapper when visible', async () => { + const container = await renderFeedbackBanner({liked: true}); + const wrapper = container.querySelector( + '[part="feedback-thank-you-wrapper"]' + ); + expect(wrapper).toBeInTheDocument(); + }); + + it('should set explainWhyRef when provided', async () => { + const ref = vi.fn(); + await renderFeedbackBanner({ + disliked: true, + feedbackSent: false, + explainWhyRef: ref, + }); + expect(ref).toHaveBeenCalled(); + }); + + it('should apply correct classes to like button when not liked', async () => { + const container = await renderFeedbackBanner({liked: false}); + const likeButton = container.querySelector('[part="feedback-like-button"]'); + expect(likeButton).toHaveClass('cursor-pointer'); + expect(likeButton).toHaveClass('hover:underline'); + expect(likeButton).not.toHaveClass('text-success'); + }); + + it('should apply correct classes to dislike button when not disliked', async () => { + const container = await renderFeedbackBanner({disliked: false}); + const dislikeButton = container.querySelector( + '[part="feedback-dislike-button"]' + ); + expect(dislikeButton).toHaveClass('cursor-pointer'); + expect(dislikeButton).toHaveClass('hover:underline'); + expect(dislikeButton).not.toHaveClass('text-error'); + }); +}); diff --git a/packages/atomic/src/components/common/smart-snippets/smart-snippet-feedback-banner.ts b/packages/atomic/src/components/common/smart-snippets/smart-snippet-feedback-banner.ts new file mode 100644 index 00000000000..91879ab8ae7 --- /dev/null +++ b/packages/atomic/src/components/common/smart-snippets/smart-snippet-feedback-banner.ts @@ -0,0 +1,125 @@ +import type {i18n} from 'i18next'; +import {html} from 'lit'; +import type {RefOrCallback} from 'lit/directives/ref.js'; +import {when} from 'lit/directives/when.js'; +import {multiClassMap} from '@/src/directives/multi-class-map'; +import type {FunctionalComponent} from '@/src/utils/functional-component-utils'; +import Checkmark from '../../../images/checkmark.svg'; +import Cross from '../../../images/cross.svg'; +import {renderButton} from '../button'; +import {renderRadioButton} from '../radio-button'; + +export interface SmartSnippetFeedbackBannerProps { + i18n: i18n; + id: string; + liked: boolean; + disliked: boolean; + feedbackSent: boolean; + onLike(): void; + onDislike(): void; + onPressExplainWhy(): void; + explainWhyRef?: RefOrCallback; +} + +export const renderSmartSnippetFeedbackBanner: FunctionalComponent< + SmartSnippetFeedbackBannerProps +> = ({props}) => { + const inquiryId = `feedback-inquiry-${props.id}`; + const thankYouId = `feedback-thank-you-${props.id}`; + const radioGroupName = `feedback-options-${props.id}`; + + const renderInquiry = () => + html` + ${props.i18n.t('smart-snippet-feedback-inquiry')} + `; + + const renderButtons = () => + html`
+ + +
`; + + const renderThankYouMessage = () => + html` + ${props.i18n.t('smart-snippet-feedback-thanks')} + `; + + const renderExplainWhyButton = () => + renderButton({ + props: { + style: 'text-primary', + part: 'feedback-explain-why-button', + onClick: () => props.onPressExplainWhy(), + ref: props.explainWhyRef, + }, + })(html`${props.i18n.t('smart-snippet-feedback-explain-why')}`); + + const renderThankYouContainer = (visible: boolean) => + when( + visible, + () => + html`
+ ${renderThankYouMessage()} + ${when(props.disliked && !props.feedbackSent, () => + renderExplainWhyButton() + )} +
` + ); + + return html`
+
+ ${renderInquiry()} ${renderButtons()} +
+ ${renderThankYouContainer(props.liked || props.disliked)} +
`; +}; diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-banner.tsx b/packages/atomic/src/components/common/smart-snippets/stencil-smart-snippet-feedback-banner.tsx similarity index 100% rename from packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-banner.tsx rename to packages/atomic/src/components/common/smart-snippets/stencil-smart-snippet-feedback-banner.tsx diff --git a/packages/atomic/src/components/insight/smart-snippets/atomic-insight-smart-snippet/atomic-insight-smart-snippet.tsx b/packages/atomic/src/components/insight/smart-snippets/atomic-insight-smart-snippet/atomic-insight-smart-snippet.tsx index 69015c53772..bc3f650de77 100644 --- a/packages/atomic/src/components/insight/smart-snippets/atomic-insight-smart-snippet/atomic-insight-smart-snippet.tsx +++ b/packages/atomic/src/components/insight/smart-snippets/atomic-insight-smart-snippet/atomic-insight-smart-snippet.tsx @@ -1,10 +1,10 @@ import {getAttributesFromLinkSlotContent} from '@/src/components/common/item-link/attributes-slot'; -import {SmartSnippetFeedbackBanner} from '@/src/components/common/smart-snippets/atomic-smart-snippet-feedback-banner'; +import {SmartSnippetFeedbackBanner} from '@/src/components/common/smart-snippets/stencil-smart-snippet-feedback-banner'; import { SmartSnippetFooter, SmartSnippetQuestion, SmartSnippetWrapper, -} from '@/src/components/common/smart-snippets/atomic-smart-snippet/smart-snippet-common'; +} from '@/src/components/common/smart-snippets/atomic-smart-snippet/stencil-smart-snippet-common'; import {Hidden} from '@/src/components/common/stencil-hidden'; import {randomID} from '@/src/utils/utils'; import { diff --git a/packages/atomic/src/components/search/smart-snippets/atomic-smart-snippet/atomic-smart-snippet.tsx b/packages/atomic/src/components/search/smart-snippets/atomic-smart-snippet/atomic-smart-snippet.tsx index f20dc39f0c9..e6dbc8dcc22 100644 --- a/packages/atomic/src/components/search/smart-snippets/atomic-smart-snippet/atomic-smart-snippet.tsx +++ b/packages/atomic/src/components/search/smart-snippets/atomic-smart-snippet/atomic-smart-snippet.tsx @@ -1,10 +1,10 @@ -import {SmartSnippetFeedbackBanner} from '@/src/components/common/smart-snippets/atomic-smart-snippet-feedback-banner'; +import {SmartSnippetFeedbackBanner} from '@/src/components/common/smart-snippets/stencil-smart-snippet-feedback-banner'; import { SmartSnippetTruncatedAnswer, SmartSnippetWrapper, SmartSnippetFooter, SmartSnippetQuestion, -} from '@/src/components/common/smart-snippets/atomic-smart-snippet/smart-snippet-common'; +} from '@/src/components/common/smart-snippets/atomic-smart-snippet/stencil-smart-snippet-common'; import {randomID} from '@/src/utils/utils'; import { buildSmartSnippet,