Skip to content

Commit 4e72f84

Browse files
committed
Lit conversion
1 parent bef5e3f commit 4e72f84

File tree

2 files changed

+79
-65
lines changed

2 files changed

+79
-65
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 './stencil-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

Lines changed: 67 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
import {SearchEngine} from '@coveo/headless';
2-
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 type {FunctionalComponent} from '@/src/utils/functional-component-utils';
36

47
const documentIdentifierInIframe = 'CoveoDocIdentifier';
58

@@ -44,62 +47,85 @@ const warnAboutLimitedUsageQuickview = (logger?: SearchEngine['logger']) => {
4447
);
4548
};
4649

47-
/**
48-
* @deprecated should only be used for Stencil components.
49-
*/
50-
export const QuickviewIframe: FunctionalComponent<{
50+
export interface QuickviewIframeProps {
5151
title: string;
5252
content?: string;
5353
onSetIframeRef: (ref: HTMLIFrameElement) => void;
5454
uniqueIdentifier?: string;
5555
sandbox?: string;
5656
src?: string;
5757
logger?: SearchEngine['logger'];
58-
}> = ({title, onSetIframeRef, uniqueIdentifier, content, sandbox, src, logger}) => {
58+
}
59+
60+
export const renderQuickviewIframe: FunctionalComponent<
61+
QuickviewIframeProps
62+
> = ({props}) => {
63+
const {
64+
title,
65+
onSetIframeRef,
66+
uniqueIdentifier,
67+
content,
68+
sandbox,
69+
src,
70+
logger,
71+
} = props;
72+
5973
// When a document is written with document.open/document.write/document.close
6074
// it is not synchronous and the content of the iframe is only available to be queried at the end of the current call stack.
6175
// This add a "wait" (setTimeout 0) before calling the `onSetIframeRef` from the parent modal quickview
6276
const waitForIframeContentToBeWritten = () => {
6377
return new Promise((resolve) => setTimeout(resolve));
6478
};
6579

66-
return (
67-
<iframe
68-
title={title}
69-
src="about:blank"
70-
class="h-full w-full"
71-
sandbox={sandbox}
72-
ref={async (el) => {
73-
const iframeRef = el as HTMLIFrameElement;
74-
75-
if (!uniqueIdentifier || !content) {
76-
return;
77-
}
80+
const handleRef = (el: Element | undefined) => {
81+
if (!el) return;
7882

79-
const documentWriter = iframeRef.contentDocument;
80-
if (!documentWriter) {
81-
if (src) {
82-
warnAboutLimitedUsageQuickview(logger);
83-
iframeRef.src = src;
84-
}
83+
const iframeRef = el as HTMLIFrameElement;
8584

86-
return;
87-
}
88-
if (
89-
currentResultAlreadyWrittenToDocument(
90-
documentWriter,
91-
uniqueIdentifier
92-
)
93-
) {
94-
return;
85+
if (!uniqueIdentifier || !content) {
86+
return;
87+
}
88+
89+
// Wait for iframe to be ready before accessing contentDocument
90+
const writeContentWhenReady = async () => {
91+
// In some environments, contentDocument might not be immediately available
92+
// Wait a tick to ensure iframe is ready
93+
await waitForIframeContentToBeWritten();
94+
95+
const documentWriter = iframeRef.contentDocument;
96+
if (!documentWriter) {
97+
if (src) {
98+
warnAboutLimitedUsageQuickview(logger);
99+
iframeRef.src = src;
95100
}
96101

97-
writeDocument(documentWriter, content);
98-
ensureSameResultIsNotOverwritten(documentWriter, uniqueIdentifier);
102+
return;
103+
}
104+
if (
105+
currentResultAlreadyWrittenToDocument(documentWriter, uniqueIdentifier)
106+
) {
107+
return;
108+
}
99109

100-
await waitForIframeContentToBeWritten();
101-
onSetIframeRef(iframeRef);
102-
}}
103-
></iframe>
104-
);
110+
writeDocument(documentWriter, content);
111+
ensureSameResultIsNotOverwritten(documentWriter, uniqueIdentifier);
112+
113+
await waitForIframeContentToBeWritten();
114+
onSetIframeRef(iframeRef);
115+
};
116+
117+
// Execute async operation without blocking
118+
writeContentWhenReady();
119+
};
120+
121+
return html`<iframe
122+
title=${title}
123+
src="about:blank"
124+
class="h-full w-full"
125+
sandbox=${
126+
// biome-ignore lint/suspicious/noExplicitAny: ifDefined requires 'any' for optional HTML attributes
127+
ifDefined(sandbox) as any
128+
}
129+
${ref(handleRef)}
130+
></iframe>`;
105131
};

0 commit comments

Comments
 (0)