@akinon/pz-theme 2.0.37-rc.0 → 2.0.38-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -31
- package/package.json +4 -4
- package/src/__tests__/__snapshots__/facet-filters-block.test.tsx.snap +11 -0
- package/src/__tests__/custom-css.test.ts +181 -0
- package/src/__tests__/designer-chrome-clipping.test.tsx +497 -0
- package/src/__tests__/facet-filters-block.test.tsx +707 -0
- package/src/__tests__/item-template.test.tsx +184 -0
- package/src/__tests__/listing-controls.test.tsx +961 -0
- package/src/__tests__/part-style-scope.test.tsx +229 -0
- package/src/__tests__/part-styles.test.ts +311 -0
- package/src/__tests__/registry-parity.test.ts +42 -0
- package/src/__tests__/row-visibility.test.ts +672 -0
- package/src/__tests__/sort-select-page-cursor.test.tsx +336 -0
- package/src/__tests__/theme-block-html-id.test.tsx +167 -0
- package/src/blocks/__tests__/__snapshots__/product-gallery-block.test.tsx.snap +15 -0
- package/src/blocks/__tests__/accordion-block-state.test.tsx +201 -0
- package/src/blocks/__tests__/facet-filters-block.test.tsx +190 -0
- package/src/blocks/__tests__/listing-context-card-model.test.tsx +230 -0
- package/src/blocks/__tests__/listing-lookup-hardening.test.ts +80 -0
- package/src/blocks/__tests__/product-gallery-block.test.tsx +974 -0
- package/src/blocks/__tests__/product-grid-composition.test.tsx +570 -0
- package/src/blocks/__tests__/product-grid-lookups.test.tsx +230 -0
- package/src/blocks/__tests__/product-price-block.test.tsx +248 -0
- package/src/blocks/__tests__/product-recommendations-block.test.tsx +702 -0
- package/src/blocks/__tests__/share-block.test.tsx +392 -0
- package/src/blocks/__tests__/store-locator-block.test.tsx +691 -0
- package/src/blocks/__tests__/variant-selector-block.test.tsx +817 -0
- package/src/blocks/__tests__/variant-selector-golden.json +20 -0
- package/src/blocks/account-menu-block.tsx +264 -0
- package/src/blocks/add-to-cart-block.tsx +417 -0
- package/src/blocks/address-list-block.tsx +348 -0
- package/src/blocks/applied-filters-block.tsx +288 -0
- package/src/blocks/auth-form-block.tsx +295 -0
- package/src/blocks/basket-items-block.tsx +457 -0
- package/src/blocks/basket-summary-block.tsx +404 -0
- package/src/blocks/block-renderer-registry.tsx +94 -0
- package/src/blocks/change-password-form-block.tsx +240 -0
- package/src/blocks/checkout-steps-block.tsx +288 -0
- package/src/blocks/checkout-summary-block.tsx +449 -0
- package/src/blocks/countdown-block.tsx +182 -0
- package/src/blocks/coupon-list-block.tsx +292 -0
- package/src/blocks/facet-filters-block.tsx +1686 -0
- package/src/blocks/faq-accordion-block.tsx +358 -0
- package/src/blocks/favorite-button-block.tsx +252 -0
- package/src/blocks/favourites-grid-block.tsx +510 -0
- package/src/blocks/forgot-password-block.tsx +155 -0
- package/src/blocks/item-template-utils.ts +131 -0
- package/src/blocks/item-template.tsx +164 -0
- package/src/blocks/listing-breadcrumb-block.tsx +175 -0
- package/src/blocks/listing-context.ts +850 -0
- package/src/blocks/listing-pagination-block.tsx +923 -0
- package/src/blocks/listing-result-count-block.tsx +126 -0
- package/src/blocks/listing-title-block.tsx +115 -0
- package/src/blocks/oauth-buttons-block.tsx +205 -0
- package/src/blocks/order-detail-block.tsx +474 -0
- package/src/blocks/order-list-block.tsx +274 -0
- package/src/blocks/order-success-block.tsx +641 -0
- package/src/blocks/order-tracking-block.tsx +533 -0
- package/src/blocks/product-attributes-block.tsx +307 -0
- package/src/blocks/product-block-utils.tsx +639 -0
- package/src/blocks/product-breadcrumb-block.tsx +215 -0
- package/src/blocks/product-description-block.tsx +169 -0
- package/src/blocks/product-gallery-block.tsx +1523 -0
- package/src/blocks/product-grid-block.tsx +966 -0
- package/src/blocks/product-price-block.tsx +373 -0
- package/src/blocks/product-recommendations-block.tsx +1433 -0
- package/src/blocks/product-title-block.tsx +129 -0
- package/src/blocks/profile-form-block.tsx +297 -0
- package/src/blocks/recently-viewed-store.ts +201 -0
- package/src/blocks/register-form-block.tsx +362 -0
- package/src/blocks/share-block.tsx +667 -0
- package/src/blocks/size-guide-block.tsx +313 -0
- package/src/blocks/sort-select-block.tsx +636 -0
- package/src/blocks/stock-status-block.tsx +168 -0
- package/src/blocks/store-locator-block.tsx +1184 -0
- package/src/blocks/variant-selector-block.tsx +1110 -0
- package/src/sections/__tests__/tablet-raw-bucket-readers.test.tsx +186 -0
- package/src/utils/__tests__/part-ownership-guard.test.tsx +29 -4
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { act, fireEvent, render, waitFor } from '@testing-library/react';
|
|
3
|
+
import ShareBlock from '../share-block';
|
|
4
|
+
import { ThemePageContextProvider } from '../../theme-page-context';
|
|
5
|
+
|
|
6
|
+
const props = (
|
|
7
|
+
properties: Record<string, unknown> = {},
|
|
8
|
+
over: Record<string, unknown> = {}
|
|
9
|
+
) =>
|
|
10
|
+
({
|
|
11
|
+
block: {
|
|
12
|
+
id: 'b1',
|
|
13
|
+
type: 'share',
|
|
14
|
+
label: 'Share',
|
|
15
|
+
styles: {},
|
|
16
|
+
properties,
|
|
17
|
+
order: 0,
|
|
18
|
+
hidden: false
|
|
19
|
+
},
|
|
20
|
+
placeholderId: 'p',
|
|
21
|
+
sectionId: 's',
|
|
22
|
+
isDesigner: false,
|
|
23
|
+
...over
|
|
24
|
+
} as any);
|
|
25
|
+
|
|
26
|
+
const PRODUCT = {
|
|
27
|
+
name: 'Striped Polo',
|
|
28
|
+
absolute_url: '/striped-polo',
|
|
29
|
+
image: '/media/polo.jpg'
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const renderWithProduct = (
|
|
33
|
+
properties: Record<string, unknown> = {},
|
|
34
|
+
over: Record<string, unknown> = {}
|
|
35
|
+
) =>
|
|
36
|
+
render(
|
|
37
|
+
<ThemePageContextProvider value={{ product: PRODUCT }}>
|
|
38
|
+
<ShareBlock {...props(properties, over)} />
|
|
39
|
+
</ThemePageContextProvider>
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const originalClipboard = Object.getOwnPropertyDescriptor(
|
|
43
|
+
window.navigator,
|
|
44
|
+
'clipboard'
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const setClipboard = (value: unknown) => {
|
|
48
|
+
Object.defineProperty(window.navigator, 'clipboard', {
|
|
49
|
+
value,
|
|
50
|
+
configurable: true
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
afterEach(() => {
|
|
55
|
+
if (originalClipboard) {
|
|
56
|
+
Object.defineProperty(window.navigator, 'clipboard', originalClipboard);
|
|
57
|
+
} else {
|
|
58
|
+
delete (window.navigator as unknown as Record<string, unknown>).clipboard;
|
|
59
|
+
}
|
|
60
|
+
delete (window.navigator as unknown as Record<string, unknown>).share;
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('ShareBlock', () => {
|
|
64
|
+
it('renders the inline icon row with the default networks', () => {
|
|
65
|
+
const { container } = renderWithProduct();
|
|
66
|
+
|
|
67
|
+
expect(container.querySelector('[data-share]')).not.toBeNull();
|
|
68
|
+
expect(container.querySelector('[data-share-trigger]')).toBeNull();
|
|
69
|
+
expect(
|
|
70
|
+
Array.from(container.querySelectorAll('[data-share-item]')).map((node) =>
|
|
71
|
+
node.getAttribute('data-share-item')
|
|
72
|
+
)
|
|
73
|
+
).toEqual(['whatsapp', 'x', 'facebook', 'pinterest', 'email', 'copy']);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('drops a network whose toggle is off', () => {
|
|
77
|
+
const { container } = renderWithProduct({ showFacebook: false });
|
|
78
|
+
|
|
79
|
+
expect(container.querySelector('[data-share-item="facebook"]')).toBeNull();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('builds absolute, escaped share targets from the ambient product', async () => {
|
|
83
|
+
const { container } = renderWithProduct();
|
|
84
|
+
|
|
85
|
+
await waitFor(() => {
|
|
86
|
+
const facebook = container.querySelector(
|
|
87
|
+
'[data-share-item="facebook"]'
|
|
88
|
+
) as HTMLAnchorElement;
|
|
89
|
+
expect(facebook.getAttribute('href')).toBe(
|
|
90
|
+
'https://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Flocalhost%2Fstriped-polo'
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const pinterest = container.querySelector(
|
|
95
|
+
'[data-share-item="pinterest"]'
|
|
96
|
+
) as HTMLAnchorElement;
|
|
97
|
+
// Pinterest needs the product image, and every value is encoded.
|
|
98
|
+
expect(pinterest.getAttribute('href')).toContain(
|
|
99
|
+
'media=http%3A%2F%2Flocalhost%2Fmedia%2Fpolo.jpg'
|
|
100
|
+
);
|
|
101
|
+
expect(pinterest.getAttribute('href')).toContain(
|
|
102
|
+
'description=Striped%20Polo'
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const email = container.querySelector(
|
|
106
|
+
'[data-share-item="email"]'
|
|
107
|
+
) as HTMLAnchorElement;
|
|
108
|
+
expect(email.getAttribute('href')).toBe(
|
|
109
|
+
'mailto:?subject=Striped%20Polo&body=Striped%20Polo%20http%3A%2F%2Flocalhost%2Fstriped-polo'
|
|
110
|
+
);
|
|
111
|
+
// A mailto must not be torn out into a second tab.
|
|
112
|
+
expect(email.getAttribute('target')).toBeNull();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('escapes a title that would otherwise break out of the query string', async () => {
|
|
116
|
+
const { container } = render(
|
|
117
|
+
<ThemePageContextProvider
|
|
118
|
+
value={{ product: { ...PRODUCT, name: 'Polo &top=1 #2' } }}
|
|
119
|
+
>
|
|
120
|
+
<ShareBlock {...props()} />
|
|
121
|
+
</ThemePageContextProvider>
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
await waitFor(() => {
|
|
125
|
+
const x = container.querySelector(
|
|
126
|
+
'[data-share-item="x"]'
|
|
127
|
+
) as HTMLAnchorElement;
|
|
128
|
+
expect(x.getAttribute('href')).toContain('text=Polo%20%26top%3D1%20%232');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('falls back to the current page URL when there is no product', async () => {
|
|
133
|
+
const { container } = render(<ShareBlock {...props()} />);
|
|
134
|
+
|
|
135
|
+
await waitFor(() => {
|
|
136
|
+
const facebook = container.querySelector(
|
|
137
|
+
'[data-share-item="facebook"]'
|
|
138
|
+
) as HTMLAnchorElement;
|
|
139
|
+
expect(facebook.getAttribute('href')).toBe(
|
|
140
|
+
`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(
|
|
141
|
+
window.location.href
|
|
142
|
+
)}`
|
|
143
|
+
);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('hides the list behind a trigger in the trigger layout', () => {
|
|
148
|
+
const { container } = renderWithProduct({ layout: 'trigger' });
|
|
149
|
+
|
|
150
|
+
expect(container.querySelector('[data-share-list]')).toBeNull();
|
|
151
|
+
|
|
152
|
+
fireEvent.click(
|
|
153
|
+
container.querySelector('[data-share-trigger]') as HTMLElement
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
expect(container.querySelector('[data-share-list]')).not.toBeNull();
|
|
157
|
+
expect(
|
|
158
|
+
container
|
|
159
|
+
.querySelector('[data-share-trigger]')
|
|
160
|
+
?.getAttribute('aria-expanded')
|
|
161
|
+
).toBe('true');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
describe('nativeShare gate', () => {
|
|
165
|
+
it('renders the plain inline row and never touches navigator by default', () => {
|
|
166
|
+
const share = jest.fn();
|
|
167
|
+
(window.navigator as unknown as Record<string, unknown>).share = share;
|
|
168
|
+
|
|
169
|
+
const { container } = renderWithProduct();
|
|
170
|
+
|
|
171
|
+
expect(container.querySelector('[data-share-trigger]')).toBeNull();
|
|
172
|
+
expect(container.querySelector('[data-share-list]')).not.toBeNull();
|
|
173
|
+
expect(share).not.toHaveBeenCalled();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('calls navigator.share from the trigger once switched on', async () => {
|
|
177
|
+
const share = jest.fn().mockResolvedValue(undefined);
|
|
178
|
+
(window.navigator as unknown as Record<string, unknown>).share = share;
|
|
179
|
+
|
|
180
|
+
const { container } = renderWithProduct({ nativeShare: true });
|
|
181
|
+
const trigger = container.querySelector(
|
|
182
|
+
'[data-share-trigger]'
|
|
183
|
+
) as HTMLElement;
|
|
184
|
+
|
|
185
|
+
await act(async () => {
|
|
186
|
+
fireEvent.click(trigger);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
expect(share).toHaveBeenCalledWith(
|
|
190
|
+
expect.objectContaining({ title: 'Striped Polo' })
|
|
191
|
+
);
|
|
192
|
+
// The sheet handled it, so the icon row stays shut.
|
|
193
|
+
expect(container.querySelector('[data-share-list]')).toBeNull();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('reveals the icon row when the browser has no native share', async () => {
|
|
197
|
+
const { container } = renderWithProduct({ nativeShare: true });
|
|
198
|
+
|
|
199
|
+
await act(async () => {
|
|
200
|
+
fireEvent.click(
|
|
201
|
+
container.querySelector('[data-share-trigger]') as HTMLElement
|
|
202
|
+
);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
expect(container.querySelector('[data-share-list]')).not.toBeNull();
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('keeps the panel shut when the shopper dismisses the sheet', async () => {
|
|
209
|
+
const abort = Object.assign(new Error('cancelled'), {
|
|
210
|
+
name: 'AbortError'
|
|
211
|
+
});
|
|
212
|
+
(window.navigator as unknown as Record<string, unknown>).share = jest
|
|
213
|
+
.fn()
|
|
214
|
+
.mockRejectedValue(abort);
|
|
215
|
+
|
|
216
|
+
const { container } = renderWithProduct({ nativeShare: true });
|
|
217
|
+
|
|
218
|
+
await act(async () => {
|
|
219
|
+
fireEvent.click(
|
|
220
|
+
container.querySelector('[data-share-trigger]') as HTMLElement
|
|
221
|
+
);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
expect(container.querySelector('[data-share-list]')).toBeNull();
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
describe('copy link', () => {
|
|
229
|
+
it('confirms with the authored text and clears it after the delay', async () => {
|
|
230
|
+
jest.useFakeTimers();
|
|
231
|
+
const writeText = jest.fn().mockResolvedValue(undefined);
|
|
232
|
+
setClipboard({ writeText });
|
|
233
|
+
|
|
234
|
+
const { container } = renderWithProduct({
|
|
235
|
+
copyConfirmLabel: 'Kopyalandı',
|
|
236
|
+
confirmDuration: 3
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
await act(async () => {
|
|
240
|
+
fireEvent.click(
|
|
241
|
+
container.querySelector('[data-share-item="copy"]') as HTMLElement
|
|
242
|
+
);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
expect(writeText).toHaveBeenCalledWith('http://localhost/striped-polo');
|
|
246
|
+
expect(
|
|
247
|
+
container.querySelector('[data-share-feedback="copied"]')?.textContent
|
|
248
|
+
).toBe('Kopyalandı');
|
|
249
|
+
|
|
250
|
+
await act(async () => {
|
|
251
|
+
jest.advanceTimersByTime(3000);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
expect(container.querySelector('[data-share-feedback]')).toBeNull();
|
|
255
|
+
jest.useRealTimers();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('offers a selectable input instead of pretending when there is no clipboard', async () => {
|
|
259
|
+
setClipboard(undefined);
|
|
260
|
+
|
|
261
|
+
const { container } = renderWithProduct({
|
|
262
|
+
copyFailLabel: 'Copy it yourself'
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
await act(async () => {
|
|
266
|
+
fireEvent.click(
|
|
267
|
+
container.querySelector('[data-share-item="copy"]') as HTMLElement
|
|
268
|
+
);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
expect(
|
|
272
|
+
container.querySelector('[data-share-feedback="failed"]')?.textContent
|
|
273
|
+
).toBe('Copy it yourself');
|
|
274
|
+
const input = container.querySelector(
|
|
275
|
+
'[data-share-fallback-input]'
|
|
276
|
+
) as HTMLInputElement;
|
|
277
|
+
expect(input.value).toBe('http://localhost/striped-polo');
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('does the same when writeText rejects on a non-secure origin', async () => {
|
|
281
|
+
setClipboard({ writeText: jest.fn().mockRejectedValue(new Error('no')) });
|
|
282
|
+
|
|
283
|
+
const { container } = renderWithProduct();
|
|
284
|
+
|
|
285
|
+
await act(async () => {
|
|
286
|
+
fireEvent.click(
|
|
287
|
+
container.querySelector('[data-share-item="copy"]') as HTMLElement
|
|
288
|
+
);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
expect(
|
|
292
|
+
container.querySelector('[data-share-fallback-input]')
|
|
293
|
+
).not.toBeNull();
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('removes the option entirely when the author chose to hide it', async () => {
|
|
297
|
+
setClipboard(undefined);
|
|
298
|
+
|
|
299
|
+
const { container } = renderWithProduct({ copyFallback: 'hide' });
|
|
300
|
+
|
|
301
|
+
await waitFor(() => {
|
|
302
|
+
expect(container.querySelector('[data-share-item="copy"]')).toBeNull();
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
describe('designer', () => {
|
|
308
|
+
it('makes the links inert', () => {
|
|
309
|
+
const { container } = renderWithProduct({}, { isDesigner: true });
|
|
310
|
+
const facebook = container.querySelector(
|
|
311
|
+
'[data-share-item="facebook"]'
|
|
312
|
+
) as HTMLAnchorElement;
|
|
313
|
+
|
|
314
|
+
expect(facebook.getAttribute('href')).toBe('#');
|
|
315
|
+
expect(facebook.getAttribute('target')).toBeNull();
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('writes nothing to the clipboard and starts no timer', async () => {
|
|
319
|
+
const writeText = jest.fn();
|
|
320
|
+
setClipboard({ writeText });
|
|
321
|
+
|
|
322
|
+
const { container } = renderWithProduct({}, { isDesigner: true });
|
|
323
|
+
|
|
324
|
+
await act(async () => {
|
|
325
|
+
fireEvent.click(
|
|
326
|
+
container.querySelector('[data-share-item="copy"]') as HTMLElement
|
|
327
|
+
);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
expect(writeText).not.toHaveBeenCalled();
|
|
331
|
+
expect(container.querySelector('[data-share-feedback]')).toBeNull();
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it('never opens the native sheet', async () => {
|
|
335
|
+
const share = jest.fn();
|
|
336
|
+
(window.navigator as unknown as Record<string, unknown>).share = share;
|
|
337
|
+
|
|
338
|
+
const { container } = renderWithProduct(
|
|
339
|
+
{ nativeShare: true },
|
|
340
|
+
{ isDesigner: true }
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
await act(async () => {
|
|
344
|
+
fireEvent.click(
|
|
345
|
+
container.querySelector('[data-share-trigger]') as HTMLElement
|
|
346
|
+
);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
expect(share).not.toHaveBeenCalled();
|
|
350
|
+
expect(container.querySelector('[data-share-list]')).not.toBeNull();
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('shows a placeholder when every network is switched off', () => {
|
|
354
|
+
const { container } = renderWithProduct(
|
|
355
|
+
{
|
|
356
|
+
showWhatsapp: false,
|
|
357
|
+
showX: false,
|
|
358
|
+
showFacebook: false,
|
|
359
|
+
showPinterest: false,
|
|
360
|
+
showEmail: false,
|
|
361
|
+
showCopy: false,
|
|
362
|
+
emptyLabel: 'Nothing enabled'
|
|
363
|
+
},
|
|
364
|
+
{ isDesigner: true }
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
expect(container.textContent).toContain('Nothing enabled');
|
|
368
|
+
expect(container.querySelector('[data-share]')).not.toBeNull();
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it('names every visually distinct element as a part', () => {
|
|
373
|
+
const { container } = renderWithProduct({
|
|
374
|
+
layout: 'trigger',
|
|
375
|
+
showLabels: true
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
fireEvent.click(
|
|
379
|
+
container.querySelector('[data-share-trigger]') as HTMLElement
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
const parts = new Set(
|
|
383
|
+
Array.from(container.querySelectorAll('[data-part]')).map((node) =>
|
|
384
|
+
node.getAttribute('data-part')
|
|
385
|
+
)
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
['root', 'trigger', 'list', 'item', 'icon', 'label'].forEach((part) =>
|
|
389
|
+
expect(parts.has(part)).toBe(true)
|
|
390
|
+
);
|
|
391
|
+
});
|
|
392
|
+
});
|