@akinon/pz-theme 2.0.164-rc.1 → 2.0.165-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 +16 -58
- package/package.json +4 -4
- package/src/__tests__/editor-locale-bridge.test.tsx +1 -32
- package/src/__tests__/theme-settings-defaults.test.tsx +395 -0
- package/src/__tests__/theme-text-defaults-code-owned.test.tsx +340 -0
- package/src/blocks/__tests__/breadcrumb-theme-separator.test.tsx +148 -0
- package/src/blocks/__tests__/social-icons-theme-fallback.test.tsx +268 -0
- package/src/blocks/__tests__/text-block.test.tsx +6 -34
- package/src/blocks/accordion-block.tsx +10 -0
- package/src/blocks/hotspot-block.tsx +6 -1
- package/src/blocks/listing-breadcrumb-block.tsx +10 -1
- package/src/blocks/order-detail-block.tsx +95 -113
- package/src/blocks/product-breadcrumb-block.tsx +9 -1
- package/src/blocks/social-glyphs.tsx +31 -0
- package/src/blocks/social-icons-list-block.tsx +72 -8
- package/src/blocks/text-block.tsx +15 -6
- package/src/chrome/__tests__/theme-page-up.test.tsx +522 -0
- package/src/chrome/theme-page-up.tsx +303 -0
- package/src/chrome/use-editor-theme-settings.ts +104 -0
- package/src/components/with-designer-features.tsx +27 -0
- package/src/custom/README.md +219 -100
- package/src/custom/__tests__/use-theme-setting.test.tsx +0 -97
- package/src/custom/collect.ts +9 -4
- package/src/custom/property-types.ts +15 -15
- package/src/custom/use-theme-setting.ts +5 -21
- package/src/editor-locale-bridge.tsx +11 -39
- package/src/index.ts +15 -0
- package/src/overlays/theme-modals.tsx +10 -0
- package/src/sections/__tests__/section-wrapper.test.tsx +76 -0
- package/src/sections/coupon-banner-section.tsx +15 -1
- package/src/sections/section-wrapper.tsx +7 -0
- package/src/sections/tabs-section.tsx +13 -0
- package/src/theme-placeholder-wrapper.tsx +50 -3
- package/src/theme-section.tsx +15 -0
- package/src/utils/__tests__/svg-image-source.test.ts +123 -0
- package/src/utils/__tests__/theme-settings-css.test.ts +801 -0
- package/src/utils/__tests__/theme-settings-readers.test.ts +213 -0
- package/src/utils/__tests__/theme-settings-resolver.test.ts +232 -0
- package/src/utils/index.ts +36 -1
- package/src/utils/svg-image-source.ts +84 -0
- package/src/utils/theme-settings-css.ts +717 -0
- package/src/utils/theme-settings-readers.ts +205 -0
- package/src/blocks/__tests__/order-line-actions.test.tsx +0 -54
- package/src/blocks/order-line-actions.ts +0 -45
- package/src/utils/__tests__/sanitize-rich-text.test.ts +0 -80
- package/src/utils/sanitize-rich-text.ts +0 -215
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { MAX_INLINE_SVG_LENGTH, toSafeImageSource } from '../svg-image-source';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Settings SVG (page-up icon, social icons) reaches the storefront
|
|
5
|
+
* only as an `<img src>`. These pin which values may become a source and how
|
|
6
|
+
* markup is encoded.
|
|
7
|
+
*/
|
|
8
|
+
const SVG_NS = 'http://www.w3.org/2000/svg';
|
|
9
|
+
|
|
10
|
+
describe('toSafeImageSource', () => {
|
|
11
|
+
const SVG =
|
|
12
|
+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4.5 15l7.5-7.5 7.5 7.5"/></svg>';
|
|
13
|
+
|
|
14
|
+
it('wraps SVG markup in a percent-encoded data URI', () => {
|
|
15
|
+
const source = toSafeImageSource(SVG);
|
|
16
|
+
|
|
17
|
+
expect(source).toBe(
|
|
18
|
+
`data:image/svg+xml;charset=utf-8,${encodeURIComponent(SVG)}`
|
|
19
|
+
);
|
|
20
|
+
// Nothing that could close the attribute or start markup survives raw.
|
|
21
|
+
expect(source).not.toMatch(/[<>"#]/);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('wraps markup that starts with an XML declaration, trimmed', () => {
|
|
25
|
+
const markup = `<?xml version="1.0"?>${SVG}`;
|
|
26
|
+
|
|
27
|
+
expect(toSafeImageSource(` ${markup}\n`)).toBe(
|
|
28
|
+
`data:image/svg+xml;charset=utf-8,${encodeURIComponent(markup)}`
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('keeps hostile markup inert by encoding it, never by passing it through', () => {
|
|
33
|
+
const hostile =
|
|
34
|
+
'<svg onload="alert(1)"><script>alert(2)</script><foreignObject><img src=x onerror=alert(3)></foreignObject></svg>';
|
|
35
|
+
const source = toSafeImageSource(hostile)!;
|
|
36
|
+
|
|
37
|
+
expect(source.startsWith('data:image/svg+xml;charset=utf-8,')).toBe(true);
|
|
38
|
+
expect(source).not.toContain('<script');
|
|
39
|
+
expect(decodeURIComponent(source.split(',')[1])).toBe(
|
|
40
|
+
hostile.replace('<svg', `<svg xmlns="${SVG_NS}"`)
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// An SVG document loaded as an image draws nothing without its namespace,
|
|
45
|
+
// while the same markup injected as HTML (the editor's tile) shows fine.
|
|
46
|
+
it('adds the SVG namespace to a root that lacks one', () => {
|
|
47
|
+
const decoded = (markup: string) =>
|
|
48
|
+
decodeURIComponent(toSafeImageSource(markup)!.split(',')[1]);
|
|
49
|
+
|
|
50
|
+
expect(decoded('<svg viewBox="0 0 24 24"><path d="M0 0"/></svg>')).toBe(
|
|
51
|
+
`<svg xmlns="${SVG_NS}" viewBox="0 0 24 24"><path d="M0 0"/></svg>`
|
|
52
|
+
);
|
|
53
|
+
expect(decoded('<svg><path d="M0 0"/></svg>')).toBe(
|
|
54
|
+
`<svg xmlns="${SVG_NS}"><path d="M0 0"/></svg>`
|
|
55
|
+
);
|
|
56
|
+
// A prefixed declaration is not the default namespace.
|
|
57
|
+
expect(
|
|
58
|
+
decoded(
|
|
59
|
+
'<svg xmlns:xlink="http://www.w3.org/1999/xlink"><use xlink:href="#a"/></svg>'
|
|
60
|
+
)
|
|
61
|
+
).toBe(
|
|
62
|
+
`<svg xmlns="${SVG_NS}" xmlns:xlink="http://www.w3.org/1999/xlink"><use xlink:href="#a"/></svg>`
|
|
63
|
+
);
|
|
64
|
+
expect(decoded('<?xml version="1.0"?>\n<svg width="24">x</svg>')).toBe(
|
|
65
|
+
`<?xml version="1.0"?>\n<svg xmlns="${SVG_NS}" width="24">x</svg>`
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('leaves a root that already declares the namespace alone', () => {
|
|
70
|
+
const nested = `<svg xmlns="${SVG_NS}"><svg viewBox="0 0 1 1"/></svg>`;
|
|
71
|
+
expect(decodeURIComponent(toSafeImageSource(nested)!.split(',')[1])).toBe(
|
|
72
|
+
nested
|
|
73
|
+
);
|
|
74
|
+
expect(toSafeImageSource(SVG)).toBe(
|
|
75
|
+
`data:image/svg+xml;charset=utf-8,${encodeURIComponent(SVG)}`
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('ignores markup larger than the cap', () => {
|
|
80
|
+
const big = `<svg>${'x'.repeat(MAX_INLINE_SVG_LENGTH)}</svg>`;
|
|
81
|
+
|
|
82
|
+
expect(toSafeImageSource(big)).toBeNull();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it.each([
|
|
86
|
+
['https://cdn.example.com/icons/up.svg'],
|
|
87
|
+
['http://cdn.example.com/up.png'],
|
|
88
|
+
['HTTPS://CDN.EXAMPLE.COM/UP.SVG'],
|
|
89
|
+
['/static/up.svg'],
|
|
90
|
+
['data:image/png;base64,iVBORw0KGgo='],
|
|
91
|
+
['data:image/svg+xml;base64,PHN2Zy8+'],
|
|
92
|
+
['data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg"/>']
|
|
93
|
+
])('accepts the image URL %s as it is', (url) => {
|
|
94
|
+
expect(toSafeImageSource(url)).toBe(url);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it.each([
|
|
98
|
+
['javascript:alert(1)'],
|
|
99
|
+
['JaVaScRiPt:alert(1)'],
|
|
100
|
+
['vbscript:msgbox(1)'],
|
|
101
|
+
['data:text/html,<script>alert(1)</script>'],
|
|
102
|
+
['data:application/javascript,alert(1)'],
|
|
103
|
+
['//evil.example/up.svg'],
|
|
104
|
+
['/\\evil.example/up.svg'],
|
|
105
|
+
['/\t/evil.example/up.svg'],
|
|
106
|
+
['https://cdn.example.com/a b.svg'],
|
|
107
|
+
['https://'],
|
|
108
|
+
['up-arrow'],
|
|
109
|
+
['icons/up.svg'],
|
|
110
|
+
['']
|
|
111
|
+
])('rejects %j', (value) => {
|
|
112
|
+
expect(toSafeImageSource(value)).toBeNull();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('rejects anything that is not a string', () => {
|
|
116
|
+
expect(toSafeImageSource(undefined)).toBeNull();
|
|
117
|
+
expect(toSafeImageSource(null)).toBeNull();
|
|
118
|
+
expect(toSafeImageSource(42)).toBeNull();
|
|
119
|
+
expect(
|
|
120
|
+
toSafeImageSource({ url: 'https://cdn.example.com/up.svg' })
|
|
121
|
+
).toBeNull();
|
|
122
|
+
});
|
|
123
|
+
});
|