Skip to content

Commit d49afed

Browse files
committed
Lit conversion
1 parent 8f5b305 commit d49afed

File tree

2 files changed

+76
-66
lines changed

2 files changed

+76
-66
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: 64 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
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 type {FunctionalComponent} from '@/src/utils/functional-component-utils';
16
import { eventPromise } from '@/src/utils/event-utils';
2-
import {SearchEngine} from '@coveo/headless';
3-
import {FunctionalComponent, h} from '@stencil/core';
47

58
const documentIdentifierInIframe = 'CoveoDocIdentifier';
69

@@ -65,64 +68,83 @@ const iframeConnectedEvent = async (iframe: HTMLIFrameElement, logger?: SearchEn
6568
});
6669
};
6770

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

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

100-
await iframeConnectedEvent(iframeRef, logger);
104+
const iframeRef = el as HTMLIFrameElement;
101105

102-
const documentWriter = iframeRef.contentDocument;
103-
if (!documentWriter) {
104-
if (src) {
105-
warnAboutLimitedUsageQuickview(logger);
106-
iframeRef.src = src;
107-
}
106+
if (!uniqueIdentifier || !content) {
107+
return;
108+
}
108109

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

120-
writeDocument(documentWriter, content);
121-
ensureSameResultIsNotOverwritten(documentWriter, uniqueIdentifier);
121+
return;
122+
}
123+
if (
124+
currentResultAlreadyWrittenToDocument(documentWriter, uniqueIdentifier)
125+
) {
126+
return;
127+
}
122128

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

0 commit comments

Comments
 (0)