-
Notifications
You must be signed in to change notification settings - Fork 39
[WIP] Migrate atomic-result-image component to Lit #6671
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- Convert Stencil component to Lit with @CustomElement decorator - Preserve imageAltField property for accessibility improvement - Add unit tests for the new Lit component - Export component from index.ts and lazy-index.ts - Remove legacy Stencil .tsx and .pcss files Co-authored-by: alexprudhomme <78121423+alexprudhomme@users.noreply.github.com>
Co-authored-by: alexprudhomme <78121423+alexprudhomme@users.noreply.github.com>
|
@copilot Try again Keep going |
- Add export to index.ts and lazy-index.ts - Add MDX documentation with examples for imageAltField - Update Storybook stories with alt text and fallback examples - Remove Cypress tests (functionality covered by unit tests) - Build and tests verified Co-authored-by: alexprudhomme <78121423+alexprudhomme@users.noreply.github.com>
Continued and completed the migration in commit 5809d51. The component is now fully migrated to Lit with:
Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
alexprudhomme
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot Move this to the search folder instead of the result-template-components
alexprudhomme
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot Move this to the search folder instead of result template components. Also make sure the mdx and stories are standard. Look at field condition for an exmaple.
- Moved component from result-template-components to search folder - Updated exports in index.ts and lazy-index.ts - Updated MDX documentation to match atomic-field-condition pattern - Updated stories to use MSW API mocking pattern - All 15 unit tests passing Co-authored-by: alexprudhomme <78121423+alexprudhomme@users.noreply.github.com>
Moved the component to the search folder and updated the MDX/stories to match the Changes:
Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR migrates the atomic-result-image component from Stencil to Lit as part of the ongoing framework migration effort. The component renders images from result fields with support for fallback images, custom alt text, and error handling. The migration includes comprehensive unit tests, Storybook stories with MSW API mocking, E2E tests, and documentation.
Key changes:
- Converted component implementation from Stencil decorators to Lit element with Light DOM
- Migrated styles from PostCSS to Lit CSS tagged template literals
- Updated test suite from Cypress to Playwright E2E tests and Vitest unit tests
- Created Storybook stories with proper MSW API harness mocking
Reviewed changes
Copilot reviewed 17 out of 19 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
packages/atomic/src/components/search/atomic-result-image/atomic-result-image.ts |
New Lit component implementation with light DOM, result context controller integration, and XSS protection |
packages/atomic/src/components/search/atomic-result-image/atomic-result-image.spec.ts |
Comprehensive unit tests covering rendering, alt text, fallback images, and error scenarios |
packages/atomic/src/components/search/atomic-result-image/atomic-result-image.new.stories.tsx |
Storybook stories with MSW mocking for default, alt text field, and fallback scenarios |
packages/atomic/src/components/search/atomic-result-image/atomic-result-image.mdx |
Documentation with usage examples for basic, fallback, and custom alt text scenarios |
packages/atomic/src/components/search/atomic-result-image/e2e/atomic-result-image.e2e.ts |
Basic E2E test for image visibility (minimal coverage) |
packages/atomic/src/components/search/atomic-result-image/e2e/page-object.ts |
Playwright page object extending Lit base page object |
packages/atomic/src/components/search/atomic-result-image/e2e/fixture.ts |
Playwright test fixture setup |
packages/atomic/src/components/search/index.ts |
Export of new Lit component class |
packages/atomic/src/components/search/lazy-index.ts |
Lazy loading registration for the component |
packages/atomic/src/utils/custom-element-tags.ts |
Registration of component tag name |
packages/atomic/src/components.d.ts |
Removal of old Stencil type definitions |
packages/atomic-react/src/components/search/components.ts |
React wrapper for the Lit component |
| Old Stencil files (removed) | Cleanup of .tsx, .pcss, old E2E tests, and Cypress test files |
π‘ Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (!this.useFallback) { | ||
| url = this.validateUrl(url as string); | ||
| if (!url) { | ||
| return html`${nothing}`; |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use of html\${nothing}`is redundant. Thenothing` directive should be used directly without wrapping it in a template literal.
Change:
return html`${nothing}`;To:
return nothing;| return html`${nothing}`; | |
| return nothing; |
| render() { | ||
| if (!this.result) { | ||
| return html`${nothing}`; | ||
| } | ||
|
|
||
| let url = this.useFallback ? this.fallback : this.url; | ||
|
|
||
| if (!this.useFallback) { | ||
| url = this.validateUrl(url as string); | ||
| if (!url) { | ||
| return html`${nothing}`; | ||
| } | ||
| } | ||
|
|
||
| return html` | ||
| <img | ||
| alt=${this.altText} | ||
| src=${filterProtocol(url as string)} | ||
| @error=${() => this.handleImageError()} | ||
| loading="lazy" | ||
| /> | ||
| `; | ||
| } |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Consider using the when directive for cleaner conditional rendering, similar to how atomic-result-html handles its render method. This is more idiomatic for Lit components and improves readability.
Current approach with early returns:
render() {
if (!this.result) {
return nothing;
}
let url = this.useFallback ? this.fallback : this.url;
// ... more logic
}More idiomatic Lit pattern:
render() {
return html`
${when(
this.result,
() => this.renderImage(),
() => nothing
)}
`;
}
private renderImage() {
let url = this.useFallback ? this.fallback : this.url;
// ... rest of rendering logic
}This would also require importing when from lit/directives/when.js.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a bad suggestion, but maybe a tad risky for the migration. The current pattern, while less idiomatic for Lit, is functionally equivalent to the Stencil original.
Your call @alexprudhomme π
| /** | ||
| * The result field which the component should use. This will look for the field in the Result object first, then in the Result.raw object. It is important to include the necessary field in the `atomic-search-interface` component. | ||
| */ | ||
| @property({type: String, reflect: true}) field!: string; |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The field property is required but lacks validation using ValidatePropsController. Similar result components like atomic-result-text and atomic-result-html use ValidatePropsController in the constructor to validate required properties.
Add validation in the constructor:
constructor() {
super();
new ValidatePropsController(
this,
() => ({field: this.field}),
new Schema({
field: new StringValue({required: true, emptyAllowed: false}),
})
);
}You'll also need to add these imports:
import {Schema, StringValue} from '@coveo/bueno';
import {ValidatePropsController} from '@/src/components/common/validate-props-controller/validate-props-controller';There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but let's ensure that we use throwOnError: false in the ValidateProps controller, otherwise prop validation becomes a breaking change. Setting throwOnError to false ensures that we only console.warn when there are validation errors. This is the right thing to do when there was no prop validation in the original Stencil component.
We have what I think is a good pattern of testing prop validation in atomic-result-list.spec.ts BTW if you want to check. It even has the TODO with the Jira to rework the tests in V4 (when we remove the throwOnError thing).
| test.describe('atomic-result-image', () => { | ||
| test('should render default image', async ({resultImage}) => { | ||
| await resultImage.load(); | ||
| await resultImage.hydrated.waitFor(); | ||
|
|
||
| const img = resultImage.hydrated.locator('img'); | ||
| await expect(img).toBeVisible(); | ||
| }); | ||
| }); |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The E2E tests are insufficient for proper component validation. Currently, only basic visibility is tested. Based on patterns from similar components and the comprehensive unit tests, the E2E tests should cover:
- Alt text verification - Verify that images render with correct alt text (default and custom)
- Fallback image handling - Test scenarios where the primary image field is missing and fallback is used
- Accessibility - Include
toMatchAriaSnapshotassertions to verify the accessibility tree
Example structure based on atomic-result-badge.e2e.ts:
test.describe('atomic-result-image', () => {
test.describe('when rendering with default alt text', () => {
test.beforeEach(async ({resultImage}) => {
await resultImage.load({story: 'default'});
await resultImage.hydrated.first().waitFor();
});
test('should render the image', async ({resultImage}) => {
const img = resultImage.hydrated.locator('img');
await expect(img).toBeVisible();
});
test('should have alt text', async ({resultImage}) => {
const img = resultImage.hydrated.locator('img');
await expect(img).toHaveAttribute('alt');
});
});
test.describe('when rendering with custom alt text field', () => {
test.beforeEach(async ({resultImage}) => {
await resultImage.load({story: 'with-alt-text-field'});
await resultImage.hydrated.first().waitFor();
});
test('should use custom alt text', async ({resultImage}) => {
const img = resultImage.hydrated.locator('img');
await expect(img).toHaveAttribute('alt', 'A beautiful placeholder image');
});
});
test.describe('when rendering with fallback', () => {
test.beforeEach(async ({resultImage}) => {
await resultImage.load({story: 'with-fallback'});
await resultImage.hydrated.first().waitFor();
});
test('should render fallback image', async ({resultImage}) => {
const img = resultImage.hydrated.locator('img');
await expect(img).toBeVisible();
await expect(img).toHaveAttribute('src', /picsum/);
});
});
});There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree with Copilot here.
We already cover accessibility in Storybook tests, so that part is covered.
With unit tests, we have pretty good coverage overal.
What's missing IMO:
- visual regression test (e2e)
- test that if you pass a bad field value (e.g., a number) it fallsback correctly (could be a unit test).
| @errorGuard() | ||
| render() { | ||
| if (!this.result) { | ||
| return html`${nothing}`; |
Copilot
AI
Nov 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use of html\${nothing}`is redundant. Thenothing` directive should be used directly without wrapping it in a template literal.
Change:
return html`${nothing}`;To:
return nothing;This pattern appears on lines 148 and 156.
fbeaudoincoveo
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
β
| } | ||
| } | ||
|
|
||
| private get url() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Visibility reduction here (used to be public in Stencil).
I think it's actually correct to make it private, but maybe we should do it in v4 to be extra safe?
β Checklist
.mdxfileindex.tsandlazy-index.tsfiles.Fixes #6385
KIT-4986