@orangesk/orange-design-system 3.9.0 → 3.11.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.
Files changed (61) hide show
  1. package/build/components/AnchorNavigation/style.css +1 -1
  2. package/build/components/AnchorNavigation/style.css.map +1 -1
  3. package/build/components/Carousel/style.css +1 -1
  4. package/build/components/Carousel/style.css.map +1 -1
  5. package/build/components/Megamenu/style.css +1 -1
  6. package/build/components/Megamenu/style.css.map +1 -1
  7. package/build/components/index.js +1 -1
  8. package/build/components/index.js.map +1 -1
  9. package/build/components/types/index.d.ts +6 -2
  10. package/build/footer-sprite-dark.svg +1 -0
  11. package/build/footer-sprite-light.svg +1 -0
  12. package/build/lib/components.css +1 -1
  13. package/build/lib/components.css.map +1 -1
  14. package/build/lib/megamenu.css +1 -1
  15. package/build/lib/megamenu.css.map +1 -1
  16. package/build/lib/scripts.js +1 -1
  17. package/build/lib/scripts.js.map +1 -1
  18. package/build/lib/style.css +1 -1
  19. package/build/lib/style.css.map +1 -1
  20. package/build/megamenu-sprite-dark.svg +1 -0
  21. package/build/megamenu-sprite-light.svg +1 -0
  22. package/package.json +27 -27
  23. package/public/footer-sprite-dark.svg +1 -0
  24. package/public/footer-sprite-light.svg +1 -0
  25. package/src/assets/footer/appstore.svg +31 -0
  26. package/src/assets/footer/googleplay.svg +52 -0
  27. package/src/assets/footer/logo.svg +33 -0
  28. package/src/{components/Footer/PlanObnovy.tsx → assets/footer/plan-obnovy.svg} +53 -211
  29. package/src/assets/megamenu/input-search.svg +1 -0
  30. package/src/assets/megamenu/input-valid.svg +1 -0
  31. package/src/assets/megamenu/logo-line.svg +5 -0
  32. package/src/assets/megamenu/megamenu-logo.svg +61 -0
  33. package/src/assets/megamenu/store-apple--white.svg +5 -0
  34. package/src/assets/megamenu/store-apple.svg +4 -0
  35. package/src/assets/megamenu/store-huawei--white.svg +5 -0
  36. package/src/assets/megamenu/store-huawei.svg +4 -0
  37. package/src/assets/megamenu/store-play--white.svg +7 -0
  38. package/src/assets/megamenu/store-play.svg +7 -0
  39. package/src/components/AnchorNavigation/styles/mixins.scss +7 -9
  40. package/src/components/BlockAction/CloneElementWithClassName.tsx +10 -7
  41. package/src/components/BlockAction/tests/CloneElementWithClassName.unit.test.jsx +28 -1
  42. package/src/components/Carousel/Carousel.static.ts +111 -14
  43. package/src/components/Carousel/Carousel.tsx +35 -12
  44. package/src/components/Carousel/constants.ts +2 -0
  45. package/src/components/Carousel/styles/mixins.scss +61 -0
  46. package/src/components/Carousel/styles/style.scss +7 -0
  47. package/src/components/Carousel/tests/Carousel.lifecycle.test.ts +223 -0
  48. package/src/components/Footer/Footer.tsx +44 -54
  49. package/src/components/Footer/FooterIcon.tsx +12 -0
  50. package/src/components/Footer/data.ts +4 -2
  51. package/src/components/Footer/tests/Footer.unit.test.jsx +63 -0
  52. package/src/components/Forms/TextInput/styles/mixins.scss +4 -2
  53. package/src/components/Megamenu/Logo.tsx +7 -7
  54. package/src/components/Megamenu/LogoJeTu.tsx +6 -58
  55. package/src/components/Megamenu/MegaMenuIcon.tsx +20 -11
  56. package/src/components/Megamenu/Megamenu.tsx +58 -48
  57. package/src/components/Megamenu/MegamenuBlog.tsx +14 -2
  58. package/src/components/Megamenu/MegamenuSearchContent.tsx +13 -0
  59. package/src/components/Megamenu/MyOrangeMobilePanel.tsx +3 -9
  60. package/src/components/Megamenu/styles/mixins.scss +21 -34
  61. package/src/components/Megamenu/tests/Megamenu.unit.test.jsx +158 -0
@@ -58,7 +58,7 @@ import {
58
58
  import type { MegamenuTile, MenuItem, MenuSection } from "./data";
59
59
  import { megamenuTiles, menuSections } from "./data";
60
60
  import { getMegamenuIds } from "./ids";
61
- import { MegamenuIcon } from "./MegaMenuIcon";
61
+ import { MegamenuIcon, MegamenuSpritePathProvider } from "./MegaMenuIcon";
62
62
  import { MegamenuSearchContent } from "./MegamenuSearchContent";
63
63
  import { MyOrangeMobilePanel } from "./MyOrangeMobilePanel";
64
64
  import {
@@ -79,6 +79,8 @@ export type MegamenuProps = {
79
79
  isB2B?: boolean;
80
80
  /** Controls whether My Orange panel shows logged-in or logged-out content. */
81
81
  isLoggedIn?: boolean;
82
+ /** Replaces My Orange panel triggers with links and omits the account panel. */
83
+ myOrangeHref?: string;
82
84
  /** Number of items displayed on the cart badge. Empty cart when omitted or zero. */
83
85
  cartItems?: number;
84
86
  /** Shows an orange dot for a non-empty cart when item count is unavailable. */
@@ -229,6 +231,7 @@ export const Megamenu = ({
229
231
  compactLogoOnly = false,
230
232
  isB2B = false,
231
233
  isLoggedIn = true,
234
+ myOrangeHref,
232
235
  cartItems,
233
236
  cartHasItems = false,
234
237
  tiles = megamenuTiles,
@@ -239,7 +242,36 @@ export const Megamenu = ({
239
242
  const hasCartItems = cartItems !== undefined && cartItems > 0;
240
243
  const showCartDot = cartHasItems && !hasCartItems;
241
244
 
242
- return (
245
+ const myOrangeContent = (
246
+ <>
247
+ <MegamenuIcon
248
+ width={24}
249
+ height={24}
250
+ name="user"
251
+ className={CLASS_NAV_ICON}
252
+ aria-hidden="true"
253
+ />
254
+ Môj Orange
255
+ </>
256
+ );
257
+ const myOrangeControl = myOrangeHref ? (
258
+ <a href={myOrangeHref} className={CLASS_NAV_BUTTON}>
259
+ {myOrangeContent}
260
+ </a>
261
+ ) : (
262
+ <button
263
+ type="button"
264
+ className={CLASS_NAV_BUTTON}
265
+ data-megamenu-mobile-toggle
266
+ data-megamenu-mobile-target="my-orange"
267
+ aria-haspopup="dialog"
268
+ aria-expanded="false"
269
+ >
270
+ {myOrangeContent}
271
+ </button>
272
+ );
273
+
274
+ const content = (
243
275
  <header>
244
276
  <nav
245
277
  className={cx(CLASS_ROOT, className, {
@@ -391,7 +423,7 @@ export const Megamenu = ({
391
423
  <MegamenuIcon
392
424
  width={16}
393
425
  height={16}
394
- name="rewards"
426
+ name="rewards-accent"
395
427
  className={CLASS_NAV_ICON_CTA}
396
428
  aria-hidden="true"
397
429
  />
@@ -468,25 +500,7 @@ export const Megamenu = ({
468
500
  ) : null}
469
501
  </a>
470
502
  </li>
471
- <li className={CLASS_NAV_ITEM}>
472
- <button
473
- type="button"
474
- className={CLASS_NAV_BUTTON}
475
- data-megamenu-mobile-toggle
476
- data-megamenu-mobile-target="my-orange"
477
- aria-haspopup="dialog"
478
- aria-expanded="false"
479
- >
480
- <MegamenuIcon
481
- width={24}
482
- height={24}
483
- name="user"
484
- className={CLASS_NAV_ICON}
485
- aria-hidden="true"
486
- />
487
- Môj Orange
488
- </button>
489
- </li>
503
+ <li className={CLASS_NAV_ITEM}>{myOrangeControl}</li>
490
504
  <li className={cx(CLASS_NAV_ITEM, CLASS_HIDE_LG_UP)}>
491
505
  <button
492
506
  type="button"
@@ -639,7 +653,7 @@ export const Megamenu = ({
639
653
  <MegamenuIcon
640
654
  width={16}
641
655
  height={16}
642
- name="rewards"
656
+ name="rewards-accent"
643
657
  className={CLASS_NAV_ICON_CTA}
644
658
  aria-hidden="true"
645
659
  />
@@ -657,25 +671,7 @@ export const Megamenu = ({
657
671
  role="group"
658
672
  aria-labelledby="mobile-quick-access-heading"
659
673
  >
660
- <li className={CLASS_NAV_ITEM}>
661
- <button
662
- type="button"
663
- className={CLASS_NAV_BUTTON}
664
- data-megamenu-mobile-toggle
665
- data-megamenu-mobile-target="my-orange"
666
- aria-haspopup="dialog"
667
- aria-expanded="false"
668
- >
669
- <MegamenuIcon
670
- width={24}
671
- height={24}
672
- name="user"
673
- className={CLASS_NAV_ICON}
674
- aria-hidden="true"
675
- />
676
- Môj Orange
677
- </button>
678
- </li>
674
+ <li className={CLASS_NAV_ITEM}>{myOrangeControl}</li>
679
675
  <li className={CLASS_NAV_ITEM}>
680
676
  <a href="/" className={CLASS_NAV_BUTTON}>
681
677
  <MegamenuIcon
@@ -751,12 +747,14 @@ export const Megamenu = ({
751
747
  </div>
752
748
  </MegamenuMobilePanel>
753
749
 
754
- <MyOrangeMobilePanel
755
- panelId={ids.myOrangePanelId}
756
- titleId={ids.myOrangeTitleId}
757
- isB2B={isB2B}
758
- isLoggedIn={isLoggedIn}
759
- />
750
+ {!myOrangeHref ? (
751
+ <MyOrangeMobilePanel
752
+ panelId={ids.myOrangePanelId}
753
+ titleId={ids.myOrangeTitleId}
754
+ isB2B={isB2B}
755
+ isLoggedIn={isLoggedIn}
756
+ />
757
+ ) : null}
760
758
 
761
759
  <button
762
760
  aria-label="Zatvoriť dropdown menu"
@@ -767,4 +765,16 @@ export const Megamenu = ({
767
765
  <div className={CLASS_SPACER} aria-hidden="true" />
768
766
  </header>
769
767
  );
768
+
769
+ return (
770
+ <MegamenuSpritePathProvider
771
+ value={
772
+ isB2B || className?.split(/\s+/).includes("is-dark")
773
+ ? "/megamenu-sprite-dark.svg"
774
+ : "/megamenu-sprite-light.svg"
775
+ }
776
+ >
777
+ {content}
778
+ </MegamenuSpritePathProvider>
779
+ );
770
780
  };
@@ -30,7 +30,7 @@ import {
30
30
  CLASS_TAG_TRANSPARENT,
31
31
  } from "./constants";
32
32
  import { getMegamenuIds } from "./ids";
33
- import { MegamenuIcon } from "./MegaMenuIcon";
33
+ import { MegamenuIcon, MegamenuSpritePathProvider } from "./MegaMenuIcon";
34
34
  import type { MegamenuProps } from "./Megamenu";
35
35
  import { MegamenuSearchContent } from "./MegamenuSearchContent";
36
36
  import { MyOrangeMobilePanel } from "./MyOrangeMobilePanel";
@@ -63,7 +63,7 @@ export const MegamenuBlog = ({
63
63
  const { hasSearchEmpty, hasSearchFilled, showSearchState, searchValue } =
64
64
  getMegamenuSearchState(searchEmpty, searchFilled);
65
65
 
66
- return (
66
+ const content = (
67
67
  <header>
68
68
  <nav
69
69
  className={cx(CLASS_ROOT, className, {
@@ -392,4 +392,16 @@ export const MegamenuBlog = ({
392
392
  <div className={CLASS_SPACER_SMALL} aria-hidden="true" />
393
393
  </header>
394
394
  );
395
+
396
+ return (
397
+ <MegamenuSpritePathProvider
398
+ value={
399
+ isB2B || className?.split(/\s+/).includes("is-dark")
400
+ ? "/megamenu-sprite-dark.svg"
401
+ : "/megamenu-sprite-light.svg"
402
+ }
403
+ >
404
+ {content}
405
+ </MegamenuSpritePathProvider>
406
+ );
395
407
  };
@@ -12,6 +12,7 @@ import {
12
12
  CLASS_TAG,
13
13
  } from "./constants";
14
14
  import { searchResultItems, searchTerms } from "./data";
15
+ import { MegamenuIcon } from "./MegaMenuIcon";
15
16
 
16
17
  type MegamenuSearchContentProps = {
17
18
  inputId: string;
@@ -43,6 +44,12 @@ export const MegamenuSearchContent = ({
43
44
  CLASS_INPUT_WRAPPER_SEARCH_ICON_WITH_PLACEHOLDER,
44
45
  )}
45
46
  >
47
+ <MegamenuIcon
48
+ name="input-search"
49
+ className="megamenu__input-search-icon"
50
+ width={20}
51
+ height={20}
52
+ />
46
53
  <input
47
54
  className={CLASS_INPUT}
48
55
  id={inputId}
@@ -51,6 +58,12 @@ export const MegamenuSearchContent = ({
51
58
  name="search"
52
59
  defaultValue={searchValue}
53
60
  />
61
+ <MegamenuIcon
62
+ name="input-valid"
63
+ className="megamenu__input-status-icon"
64
+ width={16}
65
+ height={16}
66
+ />
54
67
  </span>
55
68
 
56
69
  {hasSearchEmpty ? (
@@ -133,27 +133,21 @@ export const MyOrangeMobilePanel = ({
133
133
  className={CLASS_MOBILE_APP_CARD_STORE}
134
134
  aria-label="Stiahnuť aplikáciu z App Store"
135
135
  >
136
- <img
137
- src={isB2B ? "/apple--white.svg" : "/apple.svg"}
138
- alt=""
139
- />
136
+ <MegamenuIcon name="store-apple" width={24} height={25} />
140
137
  </a>
141
138
  <a
142
139
  href="#"
143
140
  className={CLASS_MOBILE_APP_CARD_STORE}
144
141
  aria-label="Stiahnuť aplikáciu z Google Play"
145
142
  >
146
- <img src={isB2B ? "/play--white.svg" : "/play.svg"} alt="" />
143
+ <MegamenuIcon name="store-play" width={24} height={24} />
147
144
  </a>
148
145
  <a
149
146
  href="#"
150
147
  className={CLASS_MOBILE_APP_CARD_STORE}
151
148
  aria-label="Stiahnuť aplikáciu z Huawei AppGallery"
152
149
  >
153
- <img
154
- src={isB2B ? "/huawei--white.svg" : "/huawei.svg"}
155
- alt=""
156
- />
150
+ <MegamenuIcon name="store-huawei" width={24} height={24} />
157
151
  </a>
158
152
  </div>
159
153
  </div>
@@ -528,6 +528,7 @@
528
528
  &:focus,
529
529
  &:active,
530
530
  &.is-active {
531
+ color: var(--color-text-default) !important;
531
532
  box-shadow: config.$active-line;
532
533
  }
533
534
 
@@ -951,7 +952,8 @@
951
952
  align-items: center;
952
953
  height: convert.to-rem(40px);
953
954
 
954
- img {
955
+ img,
956
+ svg {
955
957
  display: block;
956
958
  width: auto;
957
959
  max-width: convert.to-rem(52px);
@@ -1107,34 +1109,6 @@
1107
1109
  fill: currentColor;
1108
1110
  display: inline-block;
1109
1111
  vertical-align: middle;
1110
-
1111
- &__use {
1112
- display: inline;
1113
-
1114
- // Light theme (default) - show only light version
1115
- &--light {
1116
- display: inline;
1117
- }
1118
-
1119
- &--dark {
1120
- display: none;
1121
- }
1122
- }
1123
-
1124
- // Dark theme context - show only dark version
1125
- .is-dark &,
1126
- [data-theme="dark"] &,
1127
- .dark & {
1128
- &__use {
1129
- &--light {
1130
- display: none;
1131
- }
1132
-
1133
- &--dark {
1134
- display: inline;
1135
- }
1136
- }
1137
- }
1138
1112
  }
1139
1113
 
1140
1114
  @mixin overlay {
@@ -1270,7 +1244,7 @@
1270
1244
 
1271
1245
  @mixin input {
1272
1246
  @include input.base();
1273
- @include input.states();
1247
+ @include input.states($valid-icon: false);
1274
1248
  max-width: unset !important;
1275
1249
  margin-bottom: convert.to-rem(30px);
1276
1250
 
@@ -1301,16 +1275,14 @@
1301
1275
  &--search-icon-with-placeholder {
1302
1276
  isolation: isolate;
1303
1277
 
1304
- &::before {
1305
- content: "";
1278
+ .megamenu__input-search-icon {
1306
1279
  position: absolute;
1307
1280
  top: 50%;
1308
1281
  left: convert.to-rem(8px);
1309
1282
  width: convert.to-rem(20px);
1310
1283
  height: convert.to-rem(20px);
1311
1284
  transform: translateY(-50%);
1312
- background-image: inputConfig.$background-image-search-icon;
1313
- background-repeat: no-repeat;
1285
+ color: #f15e00;
1314
1286
  pointer-events: none;
1315
1287
  z-index: 2;
1316
1288
  }
@@ -1320,6 +1292,21 @@
1320
1292
  padding-left: convert.to-rem(40px);
1321
1293
  margin-bottom: 0;
1322
1294
  }
1295
+
1296
+ .megamenu__input-status-icon {
1297
+ position: absolute;
1298
+ top: 50%;
1299
+ right: convert.to-rem(10px);
1300
+ width: convert.to-rem(16px);
1301
+ height: convert.to-rem(16px);
1302
+ transform: translateY(-50%);
1303
+ pointer-events: none;
1304
+ z-index: 2;
1305
+ }
1306
+
1307
+ #{config.$class-root}__input:not(.is-valid) ~ .megamenu__input-status-icon {
1308
+ display: none;
1309
+ }
1323
1310
  }
1324
1311
 
1325
1312
  &--search-responsive {
@@ -1,7 +1,10 @@
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
+
1
3
  import { render } from "@testing-library/react";
2
4
  import "@testing-library/jest-dom/vitest";
3
5
 
4
6
  import { Megamenu, MegamenuBlog } from "../";
7
+ import MegamenuController from "../Megamenu.static";
5
8
 
6
9
  describe("Megamenu logo markup", () => {
7
10
  it("separates top links from the language switcher without a spacer item", () => {
@@ -148,6 +151,56 @@ describe("Megamenu logo markup", () => {
148
151
  expect(container).not.toHaveTextContent("0905 123 456");
149
152
  });
150
153
 
154
+ it.each([false, true])(
155
+ "renders My Orange as SSO links without account panel markup (B2B: %s)",
156
+ (isB2B) => {
157
+ const { container } = render(
158
+ <Megamenu
159
+ isLoggedIn={false}
160
+ myOrangeHref="/sso"
161
+ isB2B={isB2B}
162
+ notSticky
163
+ />,
164
+ );
165
+ const links = container.querySelectorAll('a[href="/sso"]');
166
+
167
+ expect(links).toHaveLength(2);
168
+ for (const link of links) {
169
+ expect(link).toHaveTextContent("Môj Orange");
170
+ expect(link).not.toHaveAttribute("data-megamenu-mobile-toggle");
171
+ expect(link).not.toHaveAttribute("aria-haspopup");
172
+ expect(link).not.toHaveAttribute("aria-expanded");
173
+ }
174
+ expect(
175
+ container.querySelector('[data-megamenu-mobile-target="my-orange"]'),
176
+ ).toBeNull();
177
+ expect(
178
+ container.querySelector('[data-megamenu-mobile-panel="my-orange"]'),
179
+ ).toBeNull();
180
+ expect(container.querySelector(".megamenu__mobile-account")).toBeNull();
181
+ expect(
182
+ container.querySelector('[data-megamenu-mobile-panel="main"]'),
183
+ ).toBeInTheDocument();
184
+
185
+ const controller = new MegamenuController(
186
+ container.querySelector("[data-megamenu]"),
187
+ );
188
+ try {
189
+ container.querySelector('[data-megamenu-mobile-target="main"]').click();
190
+ expect(
191
+ container.querySelector('[data-megamenu-mobile-panel="main"]'),
192
+ ).toHaveAttribute("aria-hidden", "false");
193
+ expect(links[0]).toHaveAttribute("href", "/sso");
194
+ controller.closeMobileMenu();
195
+ expect(
196
+ container.querySelector('[data-megamenu-mobile-panel="main"]'),
197
+ ).toHaveAttribute("aria-hidden", "true");
198
+ } finally {
199
+ controller.destroy();
200
+ }
201
+ },
202
+ );
203
+
151
204
  it("renders optional cart item count", () => {
152
205
  const { container } = render(<Megamenu cartItems={3} notSticky />);
153
206
 
@@ -175,3 +228,108 @@ describe("Megamenu logo markup", () => {
175
228
  expect(cart?.nextElementSibling).not.toBeInTheDocument();
176
229
  });
177
230
  });
231
+
232
+ describe("Megamenu vendor sprite markup", () => {
233
+ it.each([
234
+ ["main B2C", Megamenu, false],
235
+ ["main B2B", Megamenu, true],
236
+ ["blog B2C", MegamenuBlog, false],
237
+ ["blog B2B", MegamenuBlog, true],
238
+ ])(
239
+ "resolves every SVG in %s through the external sprite",
240
+ (_, Component, isB2B) => {
241
+ const spritePath = `/megamenu-sprite-${isB2B ? "dark" : "light"}.svg`;
242
+ const sprite = new DOMParser().parseFromString(
243
+ readFileSync(`public${spritePath}`, "utf8"),
244
+ "image/svg+xml",
245
+ );
246
+ const { container } = render(
247
+ <Component isB2B={isB2B} className={isB2B ? "is-dark" : undefined} />,
248
+ );
249
+
250
+ expect(
251
+ container.querySelector("svg path, svg rect, svg circle"),
252
+ ).toBeNull();
253
+ expect(container.querySelector('img[src$=".svg"]')).toBeNull();
254
+ for (const use of container.querySelectorAll("svg use")) {
255
+ const href = use.getAttribute("href") || use.getAttribute("xlink:href");
256
+ expect(href).toMatch(new RegExp(`^${spritePath}#`));
257
+ expect(use.parentElement.querySelectorAll("use")).toHaveLength(1);
258
+ expect(sprite.getElementById(href.split("#")[1])).not.toBeNull();
259
+ }
260
+ for (const wrapper of container.querySelectorAll(
261
+ ".megamenu__input-wrapper",
262
+ )) {
263
+ expect(
264
+ wrapper.querySelector(".megamenu__input-search-icon use"),
265
+ ).not.toBeNull();
266
+ expect(
267
+ wrapper.querySelector(".megamenu__input-status-icon use"),
268
+ ).not.toBeNull();
269
+ }
270
+ },
271
+ );
272
+ });
273
+
274
+ describe("Megamenu dedicated sprites", () => {
275
+ it("keeps megamenu-owned sources and symbols out of the global sprite", () => {
276
+ const globalSprite = new DOMParser().parseFromString(
277
+ readFileSync("public/sprite.svg", "utf8"),
278
+ "image/svg+xml",
279
+ );
280
+ const sources = readdirSync("src/assets/megamenu").filter((file) =>
281
+ file.endsWith(".svg"),
282
+ );
283
+
284
+ expect(sources.length).toBeGreaterThan(0);
285
+ for (const file of sources) {
286
+ expect(existsSync(`src/assets/icons/${file}`), file).toBe(false);
287
+ expect(globalSprite.getElementById(file.slice(0, -4)), file).toBeNull();
288
+ }
289
+ expect(globalSprite.getElementById("rewards-accent")).toBeNull();
290
+ });
291
+
292
+ it("ships matching symbol IDs with theme colors and no unrelated global icons", () => {
293
+ const parse = (theme) =>
294
+ new DOMParser().parseFromString(
295
+ readFileSync(`public/megamenu-sprite-${theme}.svg`, "utf8"),
296
+ "image/svg+xml",
297
+ );
298
+ const light = parse("light");
299
+ const dark = parse("dark");
300
+ const ids = (doc) =>
301
+ Array.from(doc.querySelectorAll("symbol"), (s) => s.id);
302
+ expect(ids(light)).toEqual(ids(dark));
303
+ expect(ids(light)).toHaveLength(20);
304
+ expect(light.getElementById("user").getAttribute("fill")).toBe("#000");
305
+ expect(dark.getElementById("user").getAttribute("fill")).toBe("#fff");
306
+ expect(light.getElementById("linkedin")).toBeNull();
307
+ expect(light.getElementById("megamenu-logo").outerHTML).not.toEqual(
308
+ dark.getElementById("megamenu-logo").outerHTML,
309
+ );
310
+ expect(light.getElementById("store-apple").outerHTML).not.toEqual(
311
+ dark.getElementById("store-apple").outerHTML,
312
+ );
313
+ for (const doc of [light, dark]) {
314
+ expect(doc.querySelector("parsererror")).toBeNull();
315
+ for (const element of doc.querySelectorAll("[clip-path]")) {
316
+ const id = element
317
+ .getAttribute("clip-path")
318
+ .match(/url\(#([^)]+)\)/)?.[1];
319
+ expect(doc.getElementById(id)).not.toBeNull();
320
+ }
321
+ }
322
+ });
323
+ });
324
+
325
+ it.each([Megamenu, MegamenuBlog])(
326
+ "uses the dark sprite for an explicit dark navigation class",
327
+ (Component) => {
328
+ const { container } = render(<Component className="is-dark" />);
329
+ for (const use of container.querySelectorAll("use")) {
330
+ expect(use.getAttribute("xlink:href")).toMatch(
331
+ /^\/megamenu-sprite-dark\.svg#/,
332
+ );
333
+ }
334
+ },
335
+ );