diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/feedback-options.spec.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/feedback-options.spec.ts
new file mode 100644
index 00000000000..8503bcc20b5
--- /dev/null
+++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/feedback-options.spec.ts
@@ -0,0 +1,69 @@
+import {describe, expect, it} from 'vitest';
+import {feedbackOptions} from './feedback-options';
+
+describe('feedbackOptions', () => {
+ it('should export feedback options array', () => {
+ expect(feedbackOptions).toBeDefined();
+ expect(Array.isArray(feedbackOptions)).toBe(true);
+ });
+
+ it('should contain 4 feedback options', () => {
+ expect(feedbackOptions).toHaveLength(4);
+ });
+
+ it('should have correct structure for each option', () => {
+ feedbackOptions.forEach((option) => {
+ expect(option).toHaveProperty('id');
+ expect(option).toHaveProperty('localeKey');
+ expect(option).toHaveProperty('correspondingAnswer');
+ expect(typeof option.id).toBe('string');
+ expect(typeof option.localeKey).toBe('string');
+ });
+ });
+
+ it('should include "does_not_answer" option', () => {
+ const option = feedbackOptions.find(
+ (opt) => opt.correspondingAnswer === 'does_not_answer'
+ );
+
+ expect(option).toBeDefined();
+ expect(option?.id).toBe('does-not-answer');
+ expect(option?.localeKey).toBe(
+ 'smart-snippet-feedback-reason-does-not-answer'
+ );
+ });
+
+ it('should include "partially_answers" option', () => {
+ const option = feedbackOptions.find(
+ (opt) => opt.correspondingAnswer === 'partially_answers'
+ );
+
+ expect(option).toBeDefined();
+ expect(option?.id).toBe('partially-answers');
+ expect(option?.localeKey).toBe(
+ 'smart-snippet-feedback-reason-partially-answers'
+ );
+ });
+
+ it('should include "was_not_a_question" option', () => {
+ const option = feedbackOptions.find(
+ (opt) => opt.correspondingAnswer === 'was_not_a_question'
+ );
+
+ expect(option).toBeDefined();
+ expect(option?.id).toBe('was-not-a-question');
+ expect(option?.localeKey).toBe(
+ 'smart-snippet-feedback-reason-was-not-a-question'
+ );
+ });
+
+ it('should include "other" option', () => {
+ const option = feedbackOptions.find(
+ (opt) => opt.correspondingAnswer === 'other'
+ );
+
+ expect(option).toBeDefined();
+ expect(option?.id).toBe('other');
+ expect(option?.localeKey).toBe('smart-snippet-feedback-reason-other');
+ });
+});
diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/feedback-options.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/feedback-options.ts
new file mode 100644
index 00000000000..9d8e0cf6af7
--- /dev/null
+++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/feedback-options.ts
@@ -0,0 +1,28 @@
+import type {SmartSnippetFeedback} from '@coveo/headless';
+
+export const feedbackOptions: {
+ id: string;
+ localeKey: string;
+ correspondingAnswer: SmartSnippetFeedback | 'other';
+}[] = [
+ {
+ id: 'does-not-answer',
+ localeKey: 'smart-snippet-feedback-reason-does-not-answer',
+ correspondingAnswer: 'does_not_answer',
+ },
+ {
+ id: 'partially-answers',
+ localeKey: 'smart-snippet-feedback-reason-partially-answers',
+ correspondingAnswer: 'partially_answers',
+ },
+ {
+ id: 'was-not-a-question',
+ localeKey: 'smart-snippet-feedback-reason-was-not-a-question',
+ correspondingAnswer: 'was_not_a_question',
+ },
+ {
+ id: 'other',
+ localeKey: 'smart-snippet-feedback-reason-other',
+ correspondingAnswer: 'other',
+ },
+];
diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-body.spec.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-body.spec.ts
new file mode 100644
index 00000000000..2cea8e82220
--- /dev/null
+++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-body.spec.ts
@@ -0,0 +1,48 @@
+import {html} from 'lit';
+import {describe, expect, it, vi} from 'vitest';
+import {renderFunctionFixture} from '@/vitest-utils/testing-helpers/fixture';
+import {renderModalBody} from './modal-body';
+
+describe('#renderModalBody', () => {
+ const formId = 'test-form-id';
+ const onSubmit = vi.fn((e: Event) => e.preventDefault());
+
+ const renderComponent = async () => {
+ return await renderFunctionFixture(
+ html`${renderModalBody({
+ props: {formId, onSubmit},
+ })(html`
Child content
`)}`
+ );
+ };
+
+ it('should render with valid props', async () => {
+ const element = await renderComponent();
+ expect(element).toBeDefined();
+ });
+
+ it('should render form element with correct attributes', async () => {
+ const element = await renderComponent();
+ const form = element.querySelector('form');
+
+ expect(form).not.toBeNull();
+ expect(form?.getAttribute('id')).toBe(formId);
+ expect(form?.getAttribute('slot')).toBe('body');
+ expect(form?.part).toContain('form');
+ });
+
+ it('should render children inside form', async () => {
+ const element = await renderComponent();
+ const form = element.querySelector('form');
+
+ expect(form).toHaveTextContent('Child content');
+ });
+
+ it('should call onSubmit when form is submitted', async () => {
+ const element = await renderComponent();
+ const form = element.querySelector('form') as HTMLFormElement;
+
+ form.dispatchEvent(new Event('submit'));
+
+ expect(onSubmit).toHaveBeenCalled();
+ });
+});
diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-body.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-body.ts
new file mode 100644
index 00000000000..6751d93afb8
--- /dev/null
+++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-body.ts
@@ -0,0 +1,20 @@
+import {html} from 'lit';
+import type {FunctionalComponentWithChildren} from '@/src/utils/functional-component-utils';
+
+export interface ModalBodyProps {
+ formId: string;
+ onSubmit: (e: Event) => void;
+}
+
+export const renderModalBody: FunctionalComponentWithChildren =
+ ({props: {formId, onSubmit}}) =>
+ (children) =>
+ html``;
diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-details.spec.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-details.spec.ts
new file mode 100644
index 00000000000..f51fb9c7be6
--- /dev/null
+++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-details.spec.ts
@@ -0,0 +1,89 @@
+import type {SmartSnippetFeedback} from '@coveo/headless';
+import type {i18n} from 'i18next';
+import {html} from 'lit';
+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 {renderModalDetails} from './modal-details';
+
+describe('#renderModalDetails', () => {
+ let i18n: i18n;
+
+ beforeAll(async () => {
+ i18n = await createTestI18n();
+ });
+
+ const renderComponent = async (
+ currentAnswer?: SmartSnippetFeedback | 'other',
+ detailsInputRef?: (el: Element | undefined) => void
+ ) => {
+ return await renderFunctionFixture(
+ html`${renderModalDetails({
+ props: {currentAnswer, i18n, detailsInputRef},
+ })}`
+ );
+ };
+
+ it('should render with valid props when currentAnswer is "other"', async () => {
+ const element = await renderComponent('other');
+ expect(element).toBeDefined();
+ });
+
+ it('should not render when currentAnswer is not "other"', async () => {
+ const element = await renderComponent('does_not_answer');
+ const fieldset = element.querySelector('fieldset');
+
+ expect(fieldset).toBeNull();
+ });
+
+ it('should not render when currentAnswer is undefined', async () => {
+ const element = await renderComponent();
+ const fieldset = element.querySelector('fieldset');
+
+ expect(fieldset).toBeNull();
+ });
+
+ it('should render fieldset when currentAnswer is "other"', async () => {
+ const element = await renderComponent('other');
+ const fieldset = element.querySelector('fieldset');
+
+ expect(fieldset).not.toBeNull();
+ expect(fieldset?.tagName).toBe('FIELDSET');
+ });
+
+ it('should render legend with correct attributes and classes', async () => {
+ const element = await renderComponent('other');
+ const legend = element.querySelector('legend');
+
+ expect(legend).not.toBeNull();
+ expect(legend?.part).toContain('details-title');
+ expect(legend).toHaveClass('text-on-background', 'text-lg', 'font-bold');
+ });
+
+ it('should render translated legend text', async () => {
+ const element = await renderComponent('other');
+ const legend = element.querySelector('legend');
+
+ expect(legend).toHaveTextContent(i18n.t('details'));
+ });
+
+ it('should render textarea with correct attributes', async () => {
+ const element = await renderComponent('other');
+ const textarea = element.querySelector('textarea');
+
+ expect(textarea).not.toBeNull();
+ expect(textarea?.getAttribute('name')).toBe('answer-details');
+ expect(textarea?.part).toContain('details-input');
+ expect(textarea?.getAttribute('rows')).toBe('4');
+ expect(textarea?.hasAttribute('required')).toBe(true);
+ });
+
+ it('should apply ref when detailsInputRef is provided', async () => {
+ const detailsInputRef = vi.fn();
+
+ const element = await renderComponent('other', detailsInputRef);
+ const textarea = element.querySelector('textarea');
+
+ expect(detailsInputRef).toHaveBeenCalledWith(textarea);
+ });
+});
diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-details.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-details.ts
new file mode 100644
index 00000000000..e193f7bb753
--- /dev/null
+++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-details.ts
@@ -0,0 +1,36 @@
+import type {SmartSnippetFeedback} from '@coveo/headless';
+import type {i18n} from 'i18next';
+import {html} from 'lit';
+import {type RefOrCallback, ref} from 'lit/directives/ref.js';
+import {when} from 'lit/directives/when.js';
+import type {FunctionalComponent} from '@/src/utils/functional-component-utils';
+
+export interface ModalDetailsProps {
+ currentAnswer: SmartSnippetFeedback | 'other';
+ i18n: i18n;
+ detailsInputRef: RefOrCallback;
+}
+
+export const renderModalDetails: FunctionalComponent = ({
+ props: {currentAnswer, i18n, detailsInputRef},
+}) =>
+ when(
+ currentAnswer === 'other',
+ () =>
+ html``
+ );
diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-footer.spec.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-footer.spec.ts
new file mode 100644
index 00000000000..cd33951922f
--- /dev/null
+++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-footer.spec.ts
@@ -0,0 +1,85 @@
+import type {i18n} from 'i18next';
+import {html} from 'lit';
+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 {renderModalFooter} from './modal-footer';
+
+vi.mock('@/src/utils/ripple-utils', {spy: true});
+
+describe('#renderModalFooter', () => {
+ let i18n: i18n;
+
+ beforeAll(async () => {
+ i18n = await createTestI18n();
+ });
+
+ const formId = 'test-form-id';
+ const onClick = vi.fn();
+
+ const renderComponent = async () => {
+ return await renderFunctionFixture(
+ html`${renderModalFooter({
+ props: {formId, i18n, onClick},
+ })}`
+ );
+ };
+
+ it('should render with valid props', async () => {
+ const element = await renderComponent();
+ expect(element).toBeDefined();
+ });
+
+ it('should render container div with correct attributes', async () => {
+ const element = await renderComponent();
+ const container = element.querySelector('[part="buttons"]');
+
+ expect(container).not.toBeNull();
+ expect(container?.tagName).toBe('DIV');
+ expect(container?.getAttribute('slot')).toBe('footer');
+ expect(container).toHaveClass('flex', 'justify-end', 'gap-2');
+ });
+
+ it('should render cancel button with correct part', async () => {
+ const element = await renderComponent();
+ const cancelButton = element.querySelector('[part="cancel-button"]');
+
+ expect(cancelButton).not.toBeNull();
+ expect(cancelButton?.tagName).toBe('BUTTON');
+ });
+
+ it('should render cancel button with translated text', async () => {
+ const element = await renderComponent();
+ const cancelButton = element.querySelector('[part="cancel-button"]');
+
+ expect(cancelButton).toHaveTextContent(i18n.t('cancel'));
+ });
+
+ it('should call onClick when cancel button is clicked', async () => {
+ const element = await renderComponent();
+ const cancelButton = element.querySelector(
+ '[part="cancel-button"]'
+ ) as HTMLButtonElement;
+
+ cancelButton.click();
+
+ expect(onClick).toHaveBeenCalled();
+ });
+
+ it('should render submit button with correct part and attributes', async () => {
+ const element = await renderComponent();
+ const submitButton = element.querySelector('[part="submit-button"]');
+
+ expect(submitButton).not.toBeNull();
+ expect(submitButton?.tagName).toBe('BUTTON');
+ expect(submitButton?.getAttribute('type')).toBe('submit');
+ expect(submitButton?.getAttribute('form')).toBe(formId);
+ });
+
+ it('should render submit button with translated text', async () => {
+ const element = await renderComponent();
+ const submitButton = element.querySelector('[part="submit-button"]');
+
+ expect(submitButton).toHaveTextContent(i18n.t('feedback-send'));
+ });
+});
diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-footer.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-footer.ts
new file mode 100644
index 00000000000..5e7cb35a0fd
--- /dev/null
+++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-footer.ts
@@ -0,0 +1,32 @@
+import type {i18n} from 'i18next';
+import {html} from 'lit';
+import {renderButton} from '@/src/components/common/button';
+import type {FunctionalComponent} from '@/src/utils/functional-component-utils';
+
+export interface ModalFooterProps {
+ formId: string;
+ i18n: i18n;
+ onClick: (e: MouseEvent) => void;
+}
+
+export const renderModalFooter: FunctionalComponent = ({
+ props: {formId, i18n, onClick},
+}) =>
+ html`
+ ${renderButton({
+ props: {
+ part: 'cancel-button',
+ style: 'outline-neutral',
+ class: 'text-primary',
+ onClick,
+ },
+ })(html`${i18n.t('cancel')}`)}
+ ${renderButton({
+ props: {
+ part: 'submit-button',
+ style: 'primary',
+ type: 'submit',
+ form: formId,
+ },
+ })(html`${i18n.t('feedback-send')}`)}
+
`;
diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-header.spec.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-header.spec.ts
new file mode 100644
index 00000000000..be27f19c65e
--- /dev/null
+++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-header.spec.ts
@@ -0,0 +1,44 @@
+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 {renderModalHeader} from './modal-header';
+
+describe('#renderModalHeader', () => {
+ let i18n: i18n;
+
+ beforeAll(async () => {
+ i18n = await createTestI18n();
+ });
+
+ const renderComponent = async () => {
+ return await renderFunctionFixture(
+ html`${renderModalHeader({
+ props: {i18n},
+ })}`
+ );
+ };
+
+ it('should render with valid props', async () => {
+ const element = await renderComponent();
+ expect(element).toBeDefined();
+ });
+
+ it('should render h1 element with slot="header"', async () => {
+ const element = await renderComponent();
+ const header = element.querySelector('h1[slot="header"]');
+
+ expect(header).not.toBeNull();
+ expect(header?.tagName).toBe('H1');
+ });
+
+ it('should render translated header text', async () => {
+ const element = await renderComponent();
+ const header = element.querySelector('h1[slot="header"]');
+
+ expect(header).toHaveTextContent(
+ i18n.t('smart-snippet-feedback-explain-why')
+ );
+ });
+});
diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-header.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-header.ts
new file mode 100644
index 00000000000..3c7d115a86b
--- /dev/null
+++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-header.ts
@@ -0,0 +1,12 @@
+import type {i18n} from 'i18next';
+import {html} from 'lit';
+import type {FunctionalComponent} from '@/src/utils/functional-component-utils';
+
+export interface ModalHeaderProps {
+ i18n: i18n;
+}
+
+export const renderModalHeader: FunctionalComponent = ({
+ props: {i18n},
+}) =>
+ html`${i18n.t('smart-snippet-feedback-explain-why')}
`;
diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-option.spec.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-option.spec.ts
new file mode 100644
index 00000000000..230ce7c5d04
--- /dev/null
+++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-option.spec.ts
@@ -0,0 +1,108 @@
+import type {SmartSnippetFeedback} from '@coveo/headless';
+import type {i18n} from 'i18next';
+import {html} from 'lit';
+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 {renderModalOption} from './modal-option';
+
+describe('#renderModalOption', () => {
+ let i18n: i18n;
+
+ beforeAll(async () => {
+ i18n = await createTestI18n();
+ });
+
+ const correspondingAnswer: SmartSnippetFeedback = 'does_not_answer';
+ const id = 'test-option-id';
+ const localeKey = 'smart-snippet-feedback-reason-does-not-answer';
+ const onChange = vi.fn();
+
+ const renderComponent = async (
+ currentAnswer?: SmartSnippetFeedback | 'other'
+ ) => {
+ return await renderFunctionFixture(
+ html`${renderModalOption({
+ props: {
+ correspondingAnswer,
+ currentAnswer,
+ i18n,
+ id,
+ localeKey,
+ onChange,
+ },
+ })}`
+ );
+ };
+
+ it('should render with valid props', async () => {
+ const element = await renderComponent();
+ expect(element).toBeDefined();
+ });
+
+ it('should render container div with correct part', async () => {
+ const element = await renderComponent();
+ const container = element.querySelector('[part="reason"]');
+
+ expect(container).not.toBeNull();
+ expect(container?.tagName).toBe('DIV');
+ expect(container).toHaveClass('flex', 'items-center');
+ });
+
+ it('should render radio input with correct attributes', async () => {
+ const element = await renderComponent();
+ const radio = element.querySelector('input[type="radio"]');
+
+ expect(radio).not.toBeNull();
+ expect(radio?.getAttribute('id')).toBe(id);
+ expect(radio?.getAttribute('name')).toBe('answer');
+ expect(radio?.part).toContain('reason-radio');
+ expect(radio).toHaveClass('mr-2', 'h-4', 'w-4');
+ expect(radio?.hasAttribute('required')).toBe(true);
+ });
+
+ it('should render label with correct attributes', async () => {
+ const element = await renderComponent();
+ const label = element.querySelector('label');
+
+ expect(label).not.toBeNull();
+ expect(label?.getAttribute('for')).toBe(id);
+ expect(label?.part).toContain('reason-label');
+ });
+
+ it('should render translated label text', async () => {
+ const element = await renderComponent();
+ const label = element.querySelector('label');
+
+ expect(label).toHaveTextContent(i18n.t(localeKey));
+ });
+
+ it('should not be checked when currentAnswer does not match', async () => {
+ const element = await renderComponent('other');
+ const radio = element.querySelector(
+ 'input[type="radio"]'
+ ) as HTMLInputElement;
+
+ expect(radio.checked).toBe(false);
+ });
+
+ it('should be checked when currentAnswer matches correspondingAnswer', async () => {
+ const element = await renderComponent(correspondingAnswer);
+ const radio = element.querySelector(
+ 'input[type="radio"]'
+ ) as HTMLInputElement;
+
+ expect(radio.checked).toBe(true);
+ });
+
+ it('should call onChange when radio is changed', async () => {
+ const element = await renderComponent();
+ const radio = element.querySelector(
+ 'input[type="radio"]'
+ ) as HTMLInputElement;
+
+ radio.dispatchEvent(new Event('change'));
+
+ expect(onChange).toHaveBeenCalled();
+ });
+});
diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-option.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-option.ts
new file mode 100644
index 00000000000..f8c8e211df8
--- /dev/null
+++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-option.ts
@@ -0,0 +1,30 @@
+import type {SmartSnippetFeedback} from '@coveo/headless';
+import type {i18n} from 'i18next';
+import {html} from 'lit';
+import type {FunctionalComponent} from '@/src/utils/functional-component-utils';
+
+export interface ModalOptionProps {
+ correspondingAnswer: SmartSnippetFeedback | 'other';
+ currentAnswer?: SmartSnippetFeedback | 'other';
+ i18n: i18n;
+ id: string;
+ localeKey: string;
+ onChange: (e: Event) => void;
+}
+
+export const renderModalOption: FunctionalComponent = ({
+ props: {correspondingAnswer, currentAnswer, i18n, id, localeKey, onChange},
+}) =>
+ html`
+
+
+
`;
diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-options.spec.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-options.spec.ts
new file mode 100644
index 00000000000..cd44c8ec57f
--- /dev/null
+++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-options.spec.ts
@@ -0,0 +1,60 @@
+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 {renderModalOptions} from './modal-options';
+
+describe('#renderModalOptions', () => {
+ let i18n: i18n;
+
+ beforeAll(async () => {
+ i18n = await createTestI18n();
+ });
+
+ const renderComponent = async () => {
+ return await renderFunctionFixture(
+ html`${renderModalOptions({
+ props: {i18n},
+ })(html`Child options
`)}`
+ );
+ };
+
+ it('should render with valid props', async () => {
+ const element = await renderComponent();
+ expect(element).toBeDefined();
+ });
+
+ it('should render fieldset element', async () => {
+ const element = await renderComponent();
+ const fieldset = element.querySelector('fieldset');
+
+ expect(fieldset).not.toBeNull();
+ expect(fieldset?.tagName).toBe('FIELDSET');
+ });
+
+ it('should render legend with correct attributes and classes', async () => {
+ const element = await renderComponent();
+ const legend = element.querySelector('legend');
+
+ expect(legend).not.toBeNull();
+ expect(legend?.part).toContain('reason-title');
+ expect(legend).toHaveClass('text-on-background', 'text-lg', 'font-bold');
+ });
+
+ it('should render translated legend text', async () => {
+ const element = await renderComponent();
+ const legend = element.querySelector('legend');
+
+ expect(legend).toHaveTextContent(
+ i18n.t('smart-snippet-feedback-select-reason')
+ );
+ });
+
+ it('should render children inside fieldset', async () => {
+ const element = await renderComponent();
+ const fieldset = element.querySelector('fieldset');
+
+ expect(fieldset).toHaveTextContent('Child options');
+ });
+});
diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-options.ts b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-options.ts
new file mode 100644
index 00000000000..d09876d421a
--- /dev/null
+++ b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/modal-options.ts
@@ -0,0 +1,19 @@
+import type {i18n} from 'i18next';
+import {html} from 'lit';
+import type {FunctionalComponentWithChildren} from '@/src/utils/functional-component-utils';
+
+export interface ModalOptionsProps {
+ i18n: i18n;
+}
+
+export const renderModalOptions: FunctionalComponentWithChildren<
+ ModalOptionsProps
+> =
+ ({props: {i18n}}) =>
+ (children) =>
+ html``;
diff --git a/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/smart-snippet-feedback-modal-common.tsx b/packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/stencil-smart-snippet-feedback-modal-common.tsx
similarity index 100%
rename from packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/smart-snippet-feedback-modal-common.tsx
rename to packages/atomic/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/stencil-smart-snippet-feedback-modal-common.tsx
diff --git a/packages/atomic/src/components/insight/smart-snippets/atomic-insight-smart-snippet-feedback-modal/atomic-insight-smart-snippet-feedback-modal.tsx b/packages/atomic/src/components/insight/smart-snippets/atomic-insight-smart-snippet-feedback-modal/atomic-insight-smart-snippet-feedback-modal.tsx
index 5304ad00199..db746c12e7a 100644
--- a/packages/atomic/src/components/insight/smart-snippets/atomic-insight-smart-snippet-feedback-modal/atomic-insight-smart-snippet-feedback-modal.tsx
+++ b/packages/atomic/src/components/insight/smart-snippets/atomic-insight-smart-snippet-feedback-modal/atomic-insight-smart-snippet-feedback-modal.tsx
@@ -7,7 +7,7 @@ import {
SmartSnippetFeedbackModalHeader,
SmartSnippetFeedbackModalOption,
smartSnippetFeedbackOptions,
-} from '@/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/smart-snippet-feedback-modal-common';
+} from '@/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/stencil-smart-snippet-feedback-modal-common';
import {updateBreakpoints} from '@/src/utils/replace-breakpoint-utils';
import {randomID} from '@/src/utils/utils';
import {
diff --git a/packages/atomic/src/components/search/smart-snippets/atomic-smart-snippet-feedback-modal/atomic-smart-snippet-feedback-modal.tsx b/packages/atomic/src/components/search/smart-snippets/atomic-smart-snippet-feedback-modal/atomic-smart-snippet-feedback-modal.tsx
index 72e7dba5cd0..6d5dde15f55 100644
--- a/packages/atomic/src/components/search/smart-snippets/atomic-smart-snippet-feedback-modal/atomic-smart-snippet-feedback-modal.tsx
+++ b/packages/atomic/src/components/search/smart-snippets/atomic-smart-snippet-feedback-modal/atomic-smart-snippet-feedback-modal.tsx
@@ -7,7 +7,7 @@ import {
SmartSnippetFeedbackModalHeader,
SmartSnippetFeedbackModalOption,
smartSnippetFeedbackOptions,
-} from '@/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/smart-snippet-feedback-modal-common';
+} from '@/src/components/common/smart-snippets/atomic-smart-snippet-feedback-modal/stencil-smart-snippet-feedback-modal-common';
import {updateBreakpoints} from '@/src/utils/replace-breakpoint-utils';
import {randomID} from '@/src/utils/utils';
import {