Skip to content

Commit 55e9437

Browse files
committed
Lit conversion
1 parent 029135e commit 55e9437

File tree

2 files changed

+77
-67
lines changed

2 files changed

+77
-67
lines changed

packages/atomic/src/components/search/result-template-components/quickview-iframe/quickview-iframe.spec.ts

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import type {SearchEngine} from '@coveo/headless';
2-
import type {VNode} from '@stencil/core';
2+
import {html} from 'lit';
33
import {beforeEach, describe, expect, it, vi} from 'vitest';
4-
import {fixtureWrapper} from '@/vitest-utils/testing-helpers/fixture-wrapper';
5-
import {renderStencilVNode} from '@/vitest-utils/testing-helpers/stencil-vnode-renderer';
6-
import {QuickviewIframe} from './quickview-iframe';
4+
import {renderFunctionFixture} from '@/vitest-utils/testing-helpers/fixture';
5+
import {renderQuickviewIframe} from './quickview-iframe';
76

8-
describe('QuickviewIframe (Stencil)', () => {
7+
describe('#renderQuickviewIframe', () => {
98
let mockOnSetIframeRef: (ref: HTMLIFrameElement) => void;
109
let mockLogger: SearchEngine['logger'];
1110

@@ -16,13 +15,6 @@ describe('QuickviewIframe (Stencil)', () => {
1615
} as unknown as SearchEngine['logger'];
1716
});
1817

19-
/**
20-
* Helper function to render the QuickviewIframe component.
21-
* This calls the actual Stencil functional component and renders its output.
22-
*
23-
* For Lit migration: Replace this with a helper that uses renderFunctionFixture
24-
* with the Lit component.
25-
*/
2618
const renderComponent = async (props: {
2719
title: string;
2820
content?: string;
@@ -32,18 +24,14 @@ describe('QuickviewIframe (Stencil)', () => {
3224
src?: string;
3325
logger?: SearchEngine['logger'];
3426
}): Promise<HTMLIFrameElement> => {
35-
// Call the actual Stencil functional component with required parameters
36-
const vnode = QuickviewIframe(
37-
props,
38-
[], // children
39-
// biome-ignore lint/suspicious/noExplicitAny: Stencil FunctionalComponent requires utils parameter but it's not used
40-
{} as any
41-
) as VNode;
42-
43-
const container = document.createElement('div');
44-
fixtureWrapper(container);
45-
46-
await renderStencilVNode(vnode, container);
27+
const container = await renderFunctionFixture(
28+
html`${renderQuickviewIframe({props})}`
29+
);
30+
31+
// Twice because renderQuickviewIframe has 2 nested async updates
32+
await new Promise((resolve) => setTimeout(resolve));
33+
await new Promise((resolve) => setTimeout(resolve));
34+
4735
return container.firstElementChild as HTMLIFrameElement;
4836
};
4937

packages/atomic/src/components/search/result-template-components/quickview-iframe/quickview-iframe.ts

Lines changed: 65 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
import { eventPromise } from '@/src/utils/event-utils';
2-
import {SearchEngine} from '@coveo/headless';
3-
import {FunctionalComponent, h} from '@stencil/core';
1+
import type {SearchEngine} from '@coveo/headless';
2+
import {html} from 'lit';
3+
import {ifDefined} from 'lit/directives/if-defined.js';
4+
import {ref} from 'lit/directives/ref.js';
5+
import {eventPromise} from '@/src/utils/event-utils';
6+
import type {FunctionalComponent} from '@/src/utils/functional-component-utils';
47

58
const documentIdentifierInIframe = 'CoveoDocIdentifier';
69

@@ -68,64 +71,83 @@ const iframeConnectedEvent = async (
6871
});
6972
};
7073

71-
/**
72-
* @deprecated should only be used for Stencil components.
73-
*/
74-
export const QuickviewIframe: FunctionalComponent<{
74+
export interface QuickviewIframeProps {
7575
title: string;
7676
content?: string;
7777
onSetIframeRef: (ref: HTMLIFrameElement) => void;
7878
uniqueIdentifier?: string;
7979
sandbox?: string;
8080
src?: string;
8181
logger?: SearchEngine['logger'];
82-
}> = ({title, onSetIframeRef, uniqueIdentifier, content, sandbox, src, logger}) => {
82+
}
83+
84+
export const renderQuickviewIframe: FunctionalComponent<
85+
QuickviewIframeProps
86+
> = ({props}) => {
87+
const {
88+
title,
89+
onSetIframeRef,
90+
uniqueIdentifier,
91+
content,
92+
sandbox,
93+
src,
94+
logger,
95+
} = props;
96+
8397
// When a document is written with document.open/document.write/document.close
8498
// it is not synchronous and the content of the iframe is only available to be queried at the end of the current call stack.
8599
// This add a "wait" (setTimeout 0) before calling the `onSetIframeRef` from the parent modal quickview
86100
const waitForIframeContentToBeWritten = () => {
87101
return new Promise((resolve) => setTimeout(resolve));
88102
};
89103

90-
return (
91-
<iframe
92-
title={title}
93-
src="about:blank"
94-
class="h-full w-full"
95-
sandbox={sandbox}
96-
ref={async (el) => {
97-
const iframeRef = el as HTMLIFrameElement;
98-
99-
if (!uniqueIdentifier || !content) {
100-
return;
101-
}
104+
const handleRef = (el: Element | undefined) => {
105+
if (!el) return;
102106

103-
await iframeConnectedEvent(iframeRef, logger);
107+
const iframeRef = el as HTMLIFrameElement;
104108

105-
const documentWriter = iframeRef.contentDocument;
106-
if (!documentWriter) {
107-
if (src) {
108-
warnAboutLimitedUsageQuickview(logger);
109-
iframeRef.src = src;
110-
}
109+
if (!uniqueIdentifier || !content) {
110+
return;
111+
}
111112

112-
return;
113-
}
114-
if (
115-
currentResultAlreadyWrittenToDocument(
116-
documentWriter,
117-
uniqueIdentifier
118-
)
119-
) {
120-
return;
113+
// Wait for iframe to be ready before accessing contentDocument
114+
const writeContentWhenReady = async () => {
115+
await iframeConnectedEvent(iframeRef, logger);
116+
117+
const documentWriter = iframeRef.contentDocument;
118+
if (!documentWriter) {
119+
if (src) {
120+
warnAboutLimitedUsageQuickview(logger);
121+
iframeRef.src = src;
121122
}
122123

123-
writeDocument(documentWriter, content);
124-
ensureSameResultIsNotOverwritten(documentWriter, uniqueIdentifier);
124+
return;
125+
}
126+
if (
127+
currentResultAlreadyWrittenToDocument(documentWriter, uniqueIdentifier)
128+
) {
129+
return;
130+
}
125131

126-
await waitForIframeContentToBeWritten();
127-
onSetIframeRef(iframeRef);
128-
}}
129-
></iframe>
130-
);
132+
writeDocument(documentWriter, content);
133+
ensureSameResultIsNotOverwritten(documentWriter, uniqueIdentifier);
134+
135+
await waitForIframeContentToBeWritten();
136+
onSetIframeRef(iframeRef);
137+
};
138+
139+
// Execute async operation without blocking
140+
writeContentWhenReady();
141+
};
142+
143+
return html`<iframe
144+
title=${title}
145+
src="about:blank"
146+
class="h-full w-full"
147+
sandbox=${
148+
// biome-ignore lint/suspicious/noExplicitAny: ifDefined requires 'any' for optional HTML attributes
149+
ifDefined(sandbox) as any
150+
}
151+
${ref(handleRef)}
152+
></iframe>`;
131153
};

0 commit comments

Comments
 (0)