@sapudex/web-components 2.30.0 → 2.32.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 (215) hide show
  1. package/cypress/specs/Breadcrumbs.cy.tsx +26 -0
  2. package/cypress/specs/ListItem.cy.tsx +22 -26
  3. package/cypress/specs/MediaPlayer.cy.tsx +209 -0
  4. package/cypress/specs/PromoTile.cy.tsx +10 -0
  5. package/cypress/specs/SecondaryNavigationDesktop.cy.tsx +74 -21
  6. package/cypress/specs/SecondaryNavigationMobile.cy.tsx +58 -1
  7. package/cypress/specs/SelectBox.cy.tsx +76 -0
  8. package/cypress/specs/TabNavigation.cy.tsx +603 -39
  9. package/cypress/support/component-imports.instrumented.ts +2 -0
  10. package/cypress/support/component-imports.src.ts +2 -0
  11. package/dist/Breadcrumbs.d.ts +5 -4
  12. package/dist/Breadcrumbs.js +15 -17
  13. package/dist/Breadcrumbs.js.map +1 -1
  14. package/dist/BreadcrumbsTemplate.js +1 -1
  15. package/dist/BreadcrumbsTemplate.js.map +1 -1
  16. package/dist/List.js +3 -3
  17. package/dist/List.js.map +1 -1
  18. package/dist/ListItemBase.d.ts +0 -2
  19. package/dist/ListItemBase.js +0 -6
  20. package/dist/ListItemBase.js.map +1 -1
  21. package/dist/MastheadCountrySelector.d.ts +3 -15
  22. package/dist/MastheadCountrySelector.js +28 -72
  23. package/dist/MastheadCountrySelector.js.map +1 -1
  24. package/dist/MastheadItem.d.ts +1 -0
  25. package/dist/MastheadItem.js +12 -4
  26. package/dist/MastheadItem.js.map +1 -1
  27. package/dist/MastheadMenuItem.d.ts +7 -0
  28. package/dist/MastheadMenuItem.js +3 -0
  29. package/dist/MastheadMenuItem.js.map +1 -1
  30. package/dist/MastheadMobileMenu.d.ts +2 -12
  31. package/dist/MastheadMobileMenu.js +31 -116
  32. package/dist/MastheadMobileMenu.js.map +1 -1
  33. package/dist/MastheadProfileMenu.js +1 -1
  34. package/dist/MastheadProfileMenu.js.map +1 -1
  35. package/dist/MastheadShell.d.ts +1 -6
  36. package/dist/MastheadShell.js +7 -44
  37. package/dist/MastheadShell.js.map +1 -1
  38. package/dist/MediaPlayer.d.ts +15 -0
  39. package/dist/MediaPlayer.js +28 -0
  40. package/dist/MediaPlayer.js.map +1 -1
  41. package/dist/Pagination.d.ts +14 -1
  42. package/dist/Pagination.js +39 -6
  43. package/dist/Pagination.js.map +1 -1
  44. package/dist/PromoTile.d.ts +8 -0
  45. package/dist/PromoTile.js +4 -0
  46. package/dist/PromoTile.js.map +1 -1
  47. package/dist/PromoTileTemplate.js +3 -1
  48. package/dist/PromoTileTemplate.js.map +1 -1
  49. package/dist/SecondaryNavigation.d.ts +12 -2
  50. package/dist/SecondaryNavigation.js +59 -8
  51. package/dist/SecondaryNavigation.js.map +1 -1
  52. package/dist/SecondaryNavigationItem.d.ts +0 -1
  53. package/dist/SecondaryNavigationItem.js +7 -5
  54. package/dist/SecondaryNavigationItem.js.map +1 -1
  55. package/dist/SecondaryNavigationSubItem.js +1 -1
  56. package/dist/SecondaryNavigationSubItem.js.map +1 -1
  57. package/dist/SelectBase.js +28 -7
  58. package/dist/SelectBase.js.map +1 -1
  59. package/dist/Tab.d.ts +1 -1
  60. package/dist/Tab.js +3 -3
  61. package/dist/Tab.js.map +1 -1
  62. package/dist/TabNavigation.d.ts +64 -0
  63. package/dist/TabNavigation.js +124 -1
  64. package/dist/TabNavigation.js.map +1 -1
  65. package/dist/assets/{InputSuggestions-CaAXs3ok.js → InputSuggestions-CHLLTki3.js} +1 -1
  66. package/dist/assets/{ListItemStandardExpandableTextTemplate-CPqUBd--.js → ListItemStandardExpandableTextTemplate-vM1eIFe4.js} +1 -1
  67. package/dist/assets/{bundle.esm-L-7H6J4f.js → bundle.esm-fUZ1I3qm.js} +218 -218
  68. package/dist/assets/messagebundle_en-iNqRB1VZ.js +1 -0
  69. package/dist/css/themes/BreadcrumbsPopover.css +1 -1
  70. package/dist/css/themes/Dropdown.css +1 -1
  71. package/dist/css/themes/Link.css +1 -1
  72. package/dist/css/themes/ListGroupHeader.css +1 -1
  73. package/dist/css/themes/MastheadCountrySelector.css +1 -1
  74. package/dist/css/themes/MastheadItem.css +1 -1
  75. package/dist/css/themes/MastheadShell.css +1 -1
  76. package/dist/css/themes/RatingIndicator.css +1 -1
  77. package/dist/css/themes/SecondaryNavigation.css +1 -1
  78. package/dist/css/themes/SecondaryNavigationItem.css +1 -1
  79. package/dist/css/themes/SecondaryNavigationSubItem.css +1 -1
  80. package/dist/css/themes/Slider.css +1 -1
  81. package/dist/css/themes/TabNavigation.css +1 -1
  82. package/dist/custom-elements-internal.json +407 -167
  83. package/dist/custom-elements.json +320 -152
  84. package/dist/generated/assets/i18n/messagebundle_en.json +1 -1
  85. package/dist/generated/i18n/i18n-defaults.d.ts +2 -1
  86. package/dist/generated/i18n/i18n-defaults.js +3 -2
  87. package/dist/generated/i18n/i18n-defaults.js.map +1 -1
  88. package/dist/generated/templates/ListGroupHeaderTemplate.lit.js +12 -16
  89. package/dist/generated/templates/ListGroupHeaderTemplate.lit.js.map +1 -1
  90. package/dist/generated/templates/ListItemTemplate.lit.js +32 -36
  91. package/dist/generated/templates/ListItemTemplate.lit.js.map +1 -1
  92. package/dist/generated/templates/MastheadCountrySelectorTemplate.lit.js +3 -3
  93. package/dist/generated/templates/MastheadCountrySelectorTemplate.lit.js.map +1 -1
  94. package/dist/generated/templates/MastheadMenuItemTemplate.lit.js +1 -1
  95. package/dist/generated/templates/MastheadMenuItemTemplate.lit.js.map +1 -1
  96. package/dist/generated/templates/MastheadMobileMenuTemplate.lit.js +1 -1
  97. package/dist/generated/templates/MastheadMobileMenuTemplate.lit.js.map +1 -1
  98. package/dist/generated/templates/MastheadShellTemplate.lit.js +9 -10
  99. package/dist/generated/templates/MastheadShellTemplate.lit.js.map +1 -1
  100. package/dist/generated/templates/SecondaryNavigationTemplate.lit.js +1 -1
  101. package/dist/generated/templates/SecondaryNavigationTemplate.lit.js.map +1 -1
  102. package/dist/generated/templates/TabNavigationTemplate.lit.js +10 -8
  103. package/dist/generated/templates/TabNavigationTemplate.lit.js.map +1 -1
  104. package/dist/generated/themes/BreadcrumbsPopover.css.d.ts +1 -1
  105. package/dist/generated/themes/BreadcrumbsPopover.css.js +1 -1
  106. package/dist/generated/themes/BreadcrumbsPopover.css.js.map +1 -1
  107. package/dist/generated/themes/Dropdown.css.d.ts +1 -1
  108. package/dist/generated/themes/Dropdown.css.js +1 -1
  109. package/dist/generated/themes/Dropdown.css.js.map +1 -1
  110. package/dist/generated/themes/Link.css.d.ts +1 -1
  111. package/dist/generated/themes/Link.css.js +1 -1
  112. package/dist/generated/themes/Link.css.js.map +1 -1
  113. package/dist/generated/themes/ListGroupHeader.css.d.ts +1 -1
  114. package/dist/generated/themes/ListGroupHeader.css.js +1 -1
  115. package/dist/generated/themes/ListGroupHeader.css.js.map +1 -1
  116. package/dist/generated/themes/MastheadCountrySelector.css.d.ts +1 -1
  117. package/dist/generated/themes/MastheadCountrySelector.css.js +1 -1
  118. package/dist/generated/themes/MastheadCountrySelector.css.js.map +1 -1
  119. package/dist/generated/themes/MastheadItem.css.d.ts +1 -1
  120. package/dist/generated/themes/MastheadItem.css.js +1 -1
  121. package/dist/generated/themes/MastheadItem.css.js.map +1 -1
  122. package/dist/generated/themes/MastheadShell.css.d.ts +1 -1
  123. package/dist/generated/themes/MastheadShell.css.js +1 -1
  124. package/dist/generated/themes/MastheadShell.css.js.map +1 -1
  125. package/dist/generated/themes/RatingIndicator.css.d.ts +1 -1
  126. package/dist/generated/themes/RatingIndicator.css.js +1 -1
  127. package/dist/generated/themes/RatingIndicator.css.js.map +1 -1
  128. package/dist/generated/themes/SecondaryNavigation.css.d.ts +1 -1
  129. package/dist/generated/themes/SecondaryNavigation.css.js +1 -1
  130. package/dist/generated/themes/SecondaryNavigation.css.js.map +1 -1
  131. package/dist/generated/themes/SecondaryNavigationItem.css.d.ts +1 -1
  132. package/dist/generated/themes/SecondaryNavigationItem.css.js +1 -1
  133. package/dist/generated/themes/SecondaryNavigationItem.css.js.map +1 -1
  134. package/dist/generated/themes/SecondaryNavigationSubItem.css.d.ts +1 -1
  135. package/dist/generated/themes/SecondaryNavigationSubItem.css.js +1 -1
  136. package/dist/generated/themes/SecondaryNavigationSubItem.css.js.map +1 -1
  137. package/dist/generated/themes/Slider.css.d.ts +1 -1
  138. package/dist/generated/themes/Slider.css.js +1 -1
  139. package/dist/generated/themes/Slider.css.js.map +1 -1
  140. package/dist/generated/themes/TabNavigation.css.d.ts +1 -1
  141. package/dist/generated/themes/TabNavigation.css.js +1 -1
  142. package/dist/generated/themes/TabNavigation.css.js.map +1 -1
  143. package/dist/messagebundle.properties +4 -1
  144. package/dist/messagebundle_en.properties +2 -1
  145. package/dist/types/List.d.ts +0 -2
  146. package/dist/types/List.js +0 -2
  147. package/dist/types/List.js.map +1 -1
  148. package/dist/vscode.html-custom-data.json +45 -76
  149. package/dist/web-types.json +96 -76
  150. package/package.json +2 -1
  151. package/src/Breadcrumbs.ts +17 -21
  152. package/src/BreadcrumbsTemplate.tsx +1 -1
  153. package/src/List.ts +3 -3
  154. package/src/ListGroupHeader.hbs +7 -11
  155. package/src/ListItem.hbs +0 -2
  156. package/src/ListItemBase.ts +0 -8
  157. package/src/MastheadCountrySelector.hbs +5 -2
  158. package/src/MastheadCountrySelector.ts +31 -85
  159. package/src/MastheadItem.ts +13 -4
  160. package/src/MastheadMenuItem.hbs +1 -0
  161. package/src/MastheadMenuItem.ts +9 -0
  162. package/src/MastheadMobileMenu.hbs +0 -1
  163. package/src/MastheadMobileMenu.ts +34 -130
  164. package/src/MastheadProfileMenu.ts +1 -1
  165. package/src/MastheadShell.hbs +9 -11
  166. package/src/MastheadShell.ts +8 -50
  167. package/src/MediaPlayer.ts +35 -0
  168. package/src/Pagination.ts +51 -7
  169. package/src/PromoTile.ts +10 -0
  170. package/src/PromoTileTemplate.tsx +4 -1
  171. package/src/SecondaryNavigation.hbs +1 -0
  172. package/src/SecondaryNavigation.ts +77 -9
  173. package/src/SecondaryNavigationItem.ts +1 -8
  174. package/src/SecondaryNavigationSubItem.ts +1 -1
  175. package/src/SelectBase.ts +33 -7
  176. package/src/Tab.ts +4 -4
  177. package/src/TabNavigation.hbs +15 -0
  178. package/src/TabNavigation.ts +134 -1
  179. package/src/generated/i18n/i18n-defaults.ts +2 -2
  180. package/src/generated/templates/ListGroupHeaderTemplate.lit.ts +12 -16
  181. package/src/generated/templates/ListItemTemplate.lit.ts +32 -36
  182. package/src/generated/templates/MastheadCountrySelectorTemplate.lit.ts +3 -3
  183. package/src/generated/templates/MastheadMenuItemTemplate.lit.ts +1 -1
  184. package/src/generated/templates/MastheadMobileMenuTemplate.lit.ts +1 -1
  185. package/src/generated/templates/MastheadShellTemplate.lit.ts +9 -10
  186. package/src/generated/templates/SecondaryNavigationTemplate.lit.ts +1 -1
  187. package/src/generated/templates/TabNavigationTemplate.lit.ts +10 -8
  188. package/src/generated/themes/BreadcrumbsPopover.css.ts +1 -1
  189. package/src/generated/themes/Dropdown.css.ts +1 -1
  190. package/src/generated/themes/Link.css.ts +1 -1
  191. package/src/generated/themes/ListGroupHeader.css.ts +1 -1
  192. package/src/generated/themes/MastheadCountrySelector.css.ts +1 -1
  193. package/src/generated/themes/MastheadItem.css.ts +1 -1
  194. package/src/generated/themes/MastheadShell.css.ts +1 -1
  195. package/src/generated/themes/RatingIndicator.css.ts +1 -1
  196. package/src/generated/themes/SecondaryNavigation.css.ts +1 -1
  197. package/src/generated/themes/SecondaryNavigationItem.css.ts +1 -1
  198. package/src/generated/themes/SecondaryNavigationSubItem.css.ts +1 -1
  199. package/src/generated/themes/Slider.css.ts +1 -1
  200. package/src/generated/themes/TabNavigation.css.ts +1 -1
  201. package/src/i18n/messagebundle.properties +4 -1
  202. package/src/i18n/messagebundle_en.properties +2 -1
  203. package/src/themes/BreadcrumbsPopover.css +4 -2
  204. package/src/themes/Dropdown.css +5 -1
  205. package/src/themes/Link.css +2 -3
  206. package/src/themes/ListGroupHeader.css +3 -11
  207. package/src/themes/MastheadCountrySelector.css +2 -2
  208. package/src/themes/MastheadItem.css +57 -0
  209. package/src/themes/MastheadShell.css +0 -2
  210. package/src/themes/SecondaryNavigation.css +6 -3
  211. package/src/themes/SecondaryNavigationItem.css +11 -10
  212. package/src/themes/SecondaryNavigationSubItem.css +3 -2
  213. package/src/themes/TabNavigation.css +27 -16
  214. package/src/types/List.ts +0 -2
  215. package/dist/assets/messagebundle_en-J-MHTtdL.js +0 -1
@@ -46,6 +46,32 @@ describe("Breadcrumbs", () => {
46
46
  cy.mount(breadcrumbs);
47
47
  });
48
48
 
49
+ describe("item-click event source", () => {
50
+ beforeEach(() => {
51
+ const itemClickHandler = cy.stub().as("itemClickHandler");
52
+ cy.get("#breadcrumbs").then($breadcrumbs => {
53
+ $breadcrumbs[0].addEventListener("item-click", itemClickHandler);
54
+ });
55
+ });
56
+
57
+ it("fires item-click with source 'main' when clicking a visible breadcrumb link", () => {
58
+ cy.get("#breadcrumbs").shadow().find("[udex-link].udex-breadcrumbs__item--visible").first().click();
59
+ cy.get("@itemClickHandler")
60
+ .should("have.been.called")
61
+ .its("lastCall.args.0.detail.source")
62
+ .should("equal", "main");
63
+ });
64
+
65
+ it("fires item-click with source 'dropdown' when clicking an item inside the dropdown", () => {
66
+ cy.get("#breadcrumbs").shadow().find(".udex-breadcrumbs__trigger").click();
67
+ cy.get("#breadcrumbs").shadow().find("[udex-link].udex-breadcrumbs__item--hidden").first().click();
68
+ cy.get("@itemClickHandler")
69
+ .should("have.been.called")
70
+ .its("lastCall.args.0.detail.source")
71
+ .should("equal", "dropdown");
72
+ });
73
+ });
74
+
49
75
  it("tests if web component is correctly rendered", () => {
50
76
  cy.get("#breadcrumbs").shadow().find(".ui5-breadcrumbs-root").should("be.visible");
51
77
  });
@@ -5,7 +5,7 @@ import { UDExAvatarSize } from "../../src/Avatar.js";
5
5
  import { UDExAdditionalTextState, UDExListStyleType } from "../../src/types/List.js";
6
6
  import UDExStatusBadgeValueState from "../../dist/types/StatusBadgeValueState.js";
7
7
 
8
- const defaultListItem = <UDExListItem
8
+ const defaultListItem = <UDExListItem
9
9
  id="listItem"
10
10
  accessibleName="accessible-name"
11
11
  supportingText="Supporting text"
@@ -20,7 +20,7 @@ const defaultListItem = <UDExListItem
20
20
  Default List Item
21
21
  </UDExListItem>
22
22
 
23
- const listItemSuccess = <UDExListItem
23
+ const listItemSuccess = <UDExListItem
24
24
  id="listItemSuccess"
25
25
  accessibleName="accessible-name"
26
26
  supportingText="Supporting text"
@@ -33,7 +33,7 @@ const listItemSuccess = <UDExListItem
33
33
  Default List Item
34
34
  </UDExListItem>
35
35
 
36
- const listItemError = <UDExListItem
36
+ const listItemError = <UDExListItem
37
37
  id="listItemError"
38
38
  accessibleName="accessible-name"
39
39
  supportingText="Supporting text"
@@ -46,7 +46,7 @@ const listItemError = <UDExListItem
46
46
  Default List Item
47
47
  </UDExListItem>
48
48
 
49
- const listItemWarning = <UDExListItem
49
+ const listItemWarning = <UDExListItem
50
50
  id="listItemWarning"
51
51
  accessibleName="accessible-name"
52
52
  supportingText="Supporting text"
@@ -59,7 +59,7 @@ const listItemWarning = <UDExListItem
59
59
  Default List Item
60
60
  </UDExListItem>
61
61
 
62
- const listItemWithIcons = <UDExListItem
62
+ const listItemWithIcons = <UDExListItem
63
63
  id="listItemIcons"
64
64
  accessible-name="accessible-name"
65
65
  supporting-text="Supporting text"
@@ -80,36 +80,32 @@ describe("ListItem", () => {
80
80
  cy.get("[udex-list-item]").should("exist");
81
81
  });
82
82
  });
83
-
83
+
84
84
  describe("Basic attributes", () => {
85
85
  beforeEach(() => {
86
86
  cy.mount(defaultListItem);
87
87
  });
88
-
88
+
89
89
  it("has correct supporting text", () => {
90
90
  cy.get("#listItem").shadow().find(".udex-list__supporting-text").should("have.text", "Supporting text");
91
91
  });
92
-
92
+
93
93
  it("has correct avatar", () => {
94
94
  cy.get("#listItem").shadow().find(".udex-list__avatar").should("exist");
95
95
  });
96
-
96
+
97
97
  it("has avatar of size M", () => {
98
98
  cy.get("#listItem").shadow().find(".udex-list__avatar").should("have.attr", "size", "M");
99
99
  });
100
-
100
+
101
101
  it("has avatar of shape Square", () => {
102
102
  cy.get("#listItem").shadow().find(".udex-list__avatar").should("have.attr", "shape", "Square");
103
103
  });
104
-
104
+
105
105
  it("has avatar with alternative text", () => {
106
106
  cy.get("#listItem").shadow().find(".udex-list__avatar").find("img").should("have.attr", "alt", "Person Avatar");
107
107
  });
108
-
109
- it("renders bullet marker at the start of the item", () => {
110
- cy.get("#listItem").shadow().find(".udex-list__circle").should("exist");
111
- });
112
-
108
+
113
109
  it("has correct accessibleName", () => {
114
110
  cy.get("#listItem").shadow().find(".udex-list__content-wrap").should(($wrapper) => {
115
111
  const itemId = $wrapper.attr("_id");
@@ -126,51 +122,51 @@ describe("ListItem", () => {
126
122
  cy.get("#listItem").shadow().find("udex-status-badge").should("have.attr", "size", UDExAvatarSize.M);
127
123
  });
128
124
  });
129
-
125
+
130
126
  describe("With Icons", () => {
131
127
  beforeEach(() => {
132
128
  cy.mount(listItemWithIcons);
133
129
  });
134
-
130
+
135
131
  it("renders first and second icons when both slots are filled", () => {
136
132
  cy.get("#listItemIcons").find("udex-button").should("have.length", 2);
137
133
  });
138
-
134
+
139
135
  it("becomes selected when clicking", () => {
140
136
  cy.get("#listItemIcons").shadow().find(".udex-list__item").click();
141
137
  cy.get("#listItemIcons").should("have.attr", "selected");
142
138
  });
143
-
139
+
144
140
  it("pressing Enter sets data-selected", () => {
145
141
  cy.realPress("Enter");
146
142
  cy.get("#listItemIcons").shadow().find(".udex-list__item").should("have.attr", "data-selected");
147
143
  });
148
-
144
+
149
145
  it("pressing Space sets data-selected", () => {
150
146
  cy.realPress("Space");
151
147
  cy.get("#listItemIcons").shadow().find(".udex-list__item").should("have.attr", "data-selected");
152
148
  });
153
149
  });
154
-
150
+
155
151
  describe("Additional Text States", () => {
156
152
  it("shows Information state", () => {
157
153
  cy.mount(defaultListItem);
158
154
  cy.get("#listItem").shadow().find(".udex-list__add-text").should("have.css", "color", "rgb(0, 64, 176)");
159
155
  });
160
-
156
+
161
157
  it("shows Success state", () => {
162
158
  cy.mount(listItemSuccess);
163
159
  cy.get("#listItemSuccess").shadow().find(".udex-list__add-text").should("have.css", "color", "rgb(30, 89, 47)");
164
160
  });
165
-
161
+
166
162
  it("shows Error state", () => {
167
163
  cy.mount(listItemError);
168
164
  cy.get("#listItemError").shadow().find(".udex-list__add-text").should("have.css", "color", "rgb(132, 6, 6)");
169
165
  });
170
-
166
+
171
167
  it("shows Warning state", () => {
172
168
  cy.mount(listItemWarning);
173
169
  cy.get("#listItemWarning").shadow().find(".udex-list__add-text").should("have.css", "color", "rgb(141, 42, 0)");
174
170
  });
175
171
  });
176
- });
172
+ });
@@ -0,0 +1,209 @@
1
+ import { UDExMediaPlayer } from "../support/component-imports.js";
2
+
3
+ // A real video source is not needed for plugin tests — VideoJS initialises
4
+ // its player regardless of whether the source resolves, so we use a
5
+ // lightweight placeholder that avoids network requests in CI.
6
+ const BLANK_SOURCE = "about:blank";
7
+ const BLANK_TYPE = "video/mp4";
8
+
9
+ const basicPlayer = (
10
+ <UDExMediaPlayer
11
+ id="test-media-player"
12
+ source={BLANK_SOURCE}
13
+ type={BLANK_TYPE}
14
+ />
15
+ );
16
+
17
+ describe("MediaPlayer — custom plugin support", () => {
18
+ describe("videojsReady event", () => {
19
+ it("fires before the player is created", () => {
20
+ // Attach a window-level listener BEFORE mounting so we never miss the
21
+ // event regardless of how quickly the videojs import resolves.
22
+ // videojsReady has bubbles:true so it reaches the window.
23
+ let playerExistedAtEventTime = true; // pessimistic default
24
+
25
+ cy.window().then((win) => {
26
+ win.addEventListener("videojsReady", (e: Event) => {
27
+ const el = (e as CustomEvent).target as any;
28
+ playerExistedAtEventTime = !!el?.player;
29
+ }, { once: true });
30
+ });
31
+
32
+ cy.mount(basicPlayer);
33
+
34
+ // Wait for the player to eventually be created, then verify the captured
35
+ // flag shows the player did NOT exist when the event fired.
36
+ cy.get("#test-media-player").should(() => {
37
+ expect(
38
+ (Cypress.$("#test-media-player")[0] as any).player,
39
+ "player should be created after videojsReady"
40
+ ).to.exist;
41
+ });
42
+
43
+ cy.then(() => {
44
+ expect(playerExistedAtEventTime, "player.player must be null when videojsReady fires").to.be.false;
45
+ });
46
+ });
47
+
48
+ it("provides the videojs constructor in event detail", () => {
49
+ let capturedVideojs: any = null;
50
+
51
+ cy.window().then((win) => {
52
+ win.addEventListener("videojsReady", (e: Event) => {
53
+ capturedVideojs = (e as CustomEvent).detail.videojs;
54
+ }, { once: true });
55
+ });
56
+
57
+ cy.mount(basicPlayer);
58
+
59
+ cy.get("#test-media-player").should(() => {
60
+ expect(capturedVideojs, "videojs must be received in event detail").to.be.a("function");
61
+ expect(capturedVideojs.registerPlugin).to.be.a("function");
62
+ expect(capturedVideojs.getPlugin).to.be.a("function");
63
+ });
64
+ });
65
+
66
+ it("allows registering a plugin that is available on the player", () => {
67
+ cy.window().then((win) => {
68
+ win.addEventListener("videojsReady", (e: Event) => {
69
+ const videojs = (e as CustomEvent).detail.videojs;
70
+ videojs.registerPlugin("eventListenerPlugin", function (this: any) {
71
+ this.addClass("plugin-applied");
72
+ });
73
+ }, { once: true });
74
+ });
75
+
76
+ cy.mount(
77
+ <UDExMediaPlayer
78
+ id="plugin-player"
79
+ source={BLANK_SOURCE}
80
+ type={BLANK_TYPE}
81
+ options={JSON.stringify({ plugins: { eventListenerPlugin: {} } })}
82
+ />,
83
+ );
84
+
85
+ // hasPlugin() returns true once the plugin has been registered on the
86
+ // videojs constructor — it does not require the player to be created yet.
87
+ cy.get("#plugin-player").should(() => {
88
+ const el = Cypress.$("#plugin-player")[0] as any;
89
+ expect(el.player?.hasPlugin("eventListenerPlugin")).to.be.true;
90
+ });
91
+ });
92
+ });
93
+
94
+ describe("registerPlugin() method", () => {
95
+ it("stores plugins in _pendingPlugins before rendering", () => {
96
+ cy.document().then((doc) => {
97
+ const el = doc.createElement("udex-media-player") as any;
98
+ el.id = "pre-register-player";
99
+ // Register BEFORE appending to DOM (before onAfterRendering fires).
100
+ el.registerPlugin("storedPlugin", () => {});
101
+ expect(el._pendingPlugins).to.have.length(1);
102
+ expect(el._pendingPlugins[0].name).to.equal("storedPlugin");
103
+ });
104
+ });
105
+
106
+ it("applies pending plugins to videojs before creating the player", () => {
107
+ let pluginWasRegistered = false;
108
+
109
+ // Use window listener to capture videojsReady and check registration.
110
+ cy.window().then((win) => {
111
+ win.addEventListener("videojsReady", (e: Event) => {
112
+ const videojs = (e as CustomEvent).detail.videojs;
113
+ // If registerPlugin() worked, the plugin was registered on the
114
+ // constructor before this event fired.
115
+ pluginWasRegistered = typeof videojs.getPlugin("prePlugin") === "function";
116
+ }, { once: true });
117
+ });
118
+
119
+ cy.document().then((doc) => {
120
+ const el = doc.createElement("udex-media-player") as any;
121
+ el.id = "pre-mount-player";
122
+ el.source = BLANK_SOURCE;
123
+ el.type = BLANK_TYPE;
124
+ el.registerPlugin("prePlugin", () => {});
125
+ doc.body.appendChild(el);
126
+ });
127
+
128
+ cy.get("#pre-mount-player").should(() => {
129
+ expect(pluginWasRegistered, "plugin must be on videojs constructor when videojsReady fires").to.be.true;
130
+ });
131
+ });
132
+
133
+ it("clears _pendingPlugins after applying them", () => {
134
+ cy.mount(basicPlayer);
135
+
136
+ // Add a plugin after mount (will be applied on next render cycle or
137
+ // persists until next onAfterRendering — here we just verify the array
138
+ // is empty once initialization is complete).
139
+ cy.get("#test-media-player").should(() => {
140
+ const el = Cypress.$("#test-media-player")[0] as any;
141
+ // After first onAfterRendering, _pendingPlugins must be cleared.
142
+ expect(el._pendingPlugins, "_pendingPlugins must be empty after init").to.have.length(0);
143
+ });
144
+ });
145
+ });
146
+
147
+ describe("enableGlobalDependency interaction", () => {
148
+ it("exposes window.videojs and still fires videojsReady", () => {
149
+ let videojsReadyFired = false;
150
+
151
+ cy.window().then((win) => {
152
+ win.addEventListener("videojsReady", () => {
153
+ videojsReadyFired = true;
154
+ }, { once: true });
155
+ });
156
+
157
+ cy.mount(
158
+ <UDExMediaPlayer
159
+ id="global-dep-player"
160
+ source={BLANK_SOURCE}
161
+ type={BLANK_TYPE}
162
+ enableGlobalDependency
163
+ />,
164
+ );
165
+
166
+ cy.window().should((win) => {
167
+ expect((win as any).videojs, "window.videojs must be set").to.be.a("function");
168
+ expect(videojsReadyFired, "videojsReady must have fired").to.be.true;
169
+ });
170
+ });
171
+ });
172
+
173
+ describe("backward compatibility", () => {
174
+ it("renders the video-js element without any custom plugins", () => {
175
+ cy.mount(basicPlayer);
176
+
177
+ cy.get("#test-media-player")
178
+ .shadow()
179
+ .find(".video-js")
180
+ .should("exist");
181
+ });
182
+
183
+ it("still fires videoReady after custom plugin setup", () => {
184
+ const videoReadyStub = cy.stub().as("videoReady");
185
+
186
+ // Attach both listeners before mounting — videoReady has bubbles:true
187
+ // so it reaches the window. Attaching after cy.mount() risks missing
188
+ // the event if the player's ready callback resolves first.
189
+ cy.window().then((win) => {
190
+ win.addEventListener("videojsReady", (e: Event) => {
191
+ const videojs = (e as CustomEvent).detail.videojs;
192
+ videojs.registerPlugin("compatPlugin", function (this: any) {});
193
+ }, { once: true });
194
+
195
+ win.addEventListener("videoReady", videoReadyStub, { once: true });
196
+ });
197
+
198
+ cy.mount(
199
+ <UDExMediaPlayer
200
+ id="compat-player"
201
+ source={BLANK_SOURCE}
202
+ type={BLANK_TYPE}
203
+ />,
204
+ );
205
+
206
+ cy.get("@videoReady").should("have.been.calledOnce");
207
+ });
208
+ });
209
+ });
@@ -8,6 +8,7 @@ const logoUrl = "http://test.com";
8
8
  const buttonLabel = "Button";
9
9
  const design = "Tertiary";
10
10
  const colorScheme = "Grey9";
11
+ const gradient = "linear-gradient(135deg, var(--udexColorBlue10) 0%,var(--udexColorBlue11) 100%)";
11
12
  const promoTile = <UDExPromoTile>Text</UDExPromoTile>;
12
13
  const simplePromoTile = <UDExPromoTile eyebrow={eyebrow} header={header}>Text</UDExPromoTile>;
13
14
  const iconPromoTile = <UDExPromoTile icon={iconName} image-label={imageLabel}>Text</UDExPromoTile>;
@@ -23,6 +24,7 @@ const pictogramPromoTile = <UDExPromoTile button={buttonLabel}>
23
24
  />
24
25
  </UDExPromoTile>;
25
26
  const darkPromoTile = <UDExPromoTile color-scheme={colorScheme} button={buttonLabel}>Text</UDExPromoTile>;
27
+ const gradientPromoTile = <UDExPromoTile background-gradient={gradient} button={buttonLabel}>Text</UDExPromoTile>;
26
28
 
27
29
  describe("Promo Tile", () => {
28
30
  it("renders simple promo tile", () => {
@@ -86,6 +88,14 @@ describe("Promo Tile", () => {
86
88
  cy.get("[udex-promo-tile]").shadow().find(".udex-promo-tile__container").should("have.class", "udex-promo-tile--dark");
87
89
  cy.get("[udex-promo-tile]").shadow().find("[udex-button]").should("have.attr", "design", "Secondary");
88
90
  });
91
+
92
+ it("renders promo tile with gradient", () => {
93
+ cy.mount(gradientPromoTile);
94
+ cy.get("[udex-promo-tile]").shadow().find(".udex-promo-tile__container")
95
+ .should("have.attr", "style")
96
+ .and("include", gradient)
97
+ .and("include", "rgb(255, 255, 255)");
98
+ });
89
99
  });
90
100
 
91
101
  describe("Promo Tile - CTA button", () => {
@@ -262,29 +262,37 @@ describe("Desktop", () => {
262
262
  cy.get("#secondary-nav-without-breadcrumbs").shadow().find("div").first().should("have.attr", "class", rootClass)
263
263
  });
264
264
 
265
- describe("Direction atributes", () => {
266
- beforeEach(() => {
267
- cy.mount(secondaryNavigationWithoutBreadcrumbs)
268
- cy.viewport(1080, 640);
269
- })
265
+ it("emits empty analyticsPath when clicking a top-level item without sub-items", () => {
266
+ cy.get("#secondary-nav").shadow().find("udex-secondary-navigation-item").eq(2).click();
267
+ cy.get("@itemClickHandler").should("have.been.called")
268
+ .its("lastCall.args.0.detail.analyticsPath").should("deep.equal", ["Secondary Navigation 3"]);
269
+ });
270
270
 
271
- xit("has the attr \"data-horizontal-align=`right`\" when doesn't fit", () => {
272
- const moreNavItem = cy.get("#secondary-nav-without-breadcrumbs").shadow().find("[udex-secondary-navigation-item]").eq(5);
273
- moreNavItem.click();
274
- moreNavItem.shadow().find(".udex-secondary-navigation__dropdown", { timeout: 1000 })
275
- .should("have.attr", "data-horizontal-align", "right")
276
- });
271
+ it("emits analyticsPath with only the item title when clicking a top-level item with sub-items", () => {
272
+ cy.get("#secondary-nav").shadow().find("udex-secondary-navigation-item").first().click();
273
+ cy.get("@itemClickHandler").should("have.been.called")
274
+ .its("lastCall.args.0.detail.analyticsPath").should("deep.equal", ["Secondary Navigation 1"]);
275
+ });
277
276
 
278
- it("has the attr \"data-horizontal-align=`left`\" when doesn't fit in the \"rtl\" mode", () => {
279
- cy.get("body").invoke("attr", "dir", "rtl");
280
- const moreNavItem = cy.get("#secondary-nav-without-breadcrumbs").shadow().find("[udex-secondary-navigation-item]").eq(5);
281
- moreNavItem.
282
- click()
283
- .shadow()
284
- .find(".udex-secondary-navigation__dropdown")
285
- .should("have.attr", "data-horizontal-align", "left")
286
- });
287
- })
277
+ it("emits analyticsPath with parent and sub-item titles when clicking a sub-item", () => {
278
+ cy.get("#secondary-nav").shadow().find("udex-secondary-navigation-item").first().click();
279
+ cy.get("#secondary-nav").shadow().find("udex-secondary-navigation-item").first()
280
+ .shadow().find("udex-secondary-navigation-sub-item").eq(1).click();
281
+ cy.get("@itemClickHandler").should("have.been.called")
282
+ .its("lastCall.args.0.detail.analyticsPath").should("deep.equal", ["Secondary Navigation 1", "secondary nested 2"]);
283
+ });
284
+
285
+ it("emits analyticsPath with full ancestor chain when clicking a deeply nested sub-item", () => {
286
+ cy.get("#secondary-nav").shadow().find("udex-secondary-navigation-item").first().click();
287
+ cy.get("#secondary-nav").shadow().find("udex-secondary-navigation-item").first()
288
+ .shadow().find("udex-secondary-navigation-sub-item").first().click();
289
+ cy.get("#secondary-nav").shadow().find("udex-secondary-navigation-item").first()
290
+ .shadow().find("udex-secondary-navigation-sub-item").eq(2).click();
291
+ cy.get("#secondary-nav").shadow().find("udex-secondary-navigation-item").first()
292
+ .shadow().find("udex-secondary-navigation-sub-item").eq(1).click();
293
+ cy.get("@itemClickHandler").should("have.been.called")
294
+ .its("lastCall.args.0.detail.analyticsPath").should("deep.equal", ["Secondary Navigation 1", "secondary nested 1", "tertiary nested 2", "quaternary nested 1"]);
295
+ });
288
296
 
289
297
  it("closes dropdown and focuses buy now button", () => {
290
298
  cy.get("#secondary-nav").shadow().find("udex-secondary-navigation-item").first().click()
@@ -362,4 +370,49 @@ describe("Desktop", () => {
362
370
 
363
371
  cy.get("@itemClickHandler").should("have.been.called").its("lastCall.args.0.detail.clickedItem").should("include",{ title: "secondary nested 2", path: "#secondary_nested_2"})
364
372
  });
373
+
374
+ it("emits isMore: true when clicking the More button", () => {
375
+ cy.get("#secondary-nav").shadow().find("udex-secondary-navigation-item").eq(6).click();
376
+ cy.get("@itemClickHandler").should("have.been.called")
377
+ .its("lastCall.args.0.detail.isMore").should("be.true");
378
+ });
379
+
380
+ it("emits isMore: true when clicking an overflow item from the More dropdown", () => {
381
+ cy.get("#secondary-nav").shadow().find("udex-secondary-navigation-item").eq(6).click();
382
+ cy.get("#secondary-nav").shadow().find("udex-secondary-navigation-item").eq(6)
383
+ .shadow().find("udex-secondary-navigation-sub-item").first().click();
384
+ cy.get("@itemClickHandler").should("have.been.called")
385
+ .its("lastCall.args.0.detail.isMore").should("be.true");
386
+ });
387
+
388
+ it("does not emit isMore when clicking a regular item", () => {
389
+ cy.get("#secondary-nav").shadow().find("udex-secondary-navigation-item").eq(2).click();
390
+ cy.get("@itemClickHandler").should("have.been.called")
391
+ .its("lastCall.args.0.detail").should("not.have.property", "isMore");
392
+ });
393
+
394
+ describe("Direction attributes", () => {
395
+ beforeEach(() => {
396
+ cy.mount(secondaryNavigationWithoutBreadcrumbs)
397
+ cy.viewport(1080, 640);
398
+ })
399
+
400
+ // TODO: Fix tests in scope https://github.tools.sap/sapudex/digital-design-system/issues/2989
401
+ xit("has the attr \"data-horizontal-align=`right`\" when doesn't fit", () => {
402
+ cy.get("#secondary-nav-without-breadcrumbs").shadow().find("[udex-secondary-navigation-item]").eq(5).click();
403
+ cy.get("#secondary-nav-without-breadcrumbs").shadow().find("[udex-secondary-navigation-item]").eq(5)
404
+ .shadow().find(".udex-secondary-navigation__dropdown", { timeout: 5000 })
405
+ .should("be.visible")
406
+ .should("have.attr", "data-horizontal-align", "right")
407
+ });
408
+
409
+ xit("has the attr \"data-horizontal-align=`left`\" when doesn't fit in the \"rtl\" mode", () => {
410
+ cy.get("body").invoke("attr", "dir", "rtl");
411
+ cy.get("#secondary-nav-without-breadcrumbs").shadow().find("[udex-secondary-navigation-item]").eq(5).click();
412
+ cy.get("#secondary-nav-without-breadcrumbs").shadow().find("[udex-secondary-navigation-item]").eq(5)
413
+ .shadow()
414
+ .find(".udex-secondary-navigation__dropdown")
415
+ .should("have.attr", "data-horizontal-align", "left")
416
+ });
417
+ })
365
418
  });
@@ -78,5 +78,62 @@ describe("SecondaryNavigation", () => {
78
78
  const subTitle = cy.get("#secondary-nav").shadow().find(".udex-secondary-navigation__mobile-nav").shadow().find(".udex-secondary-navigation__item-sub-title");
79
79
  subTitle.should("have.text", "Secondary Navigation 3 mobile");
80
80
  });
81
+
82
+ describe("analyticsPath", () => {
83
+ beforeEach(() => {
84
+ const itemClickHandler = cy.stub().as("itemClickHandler");
85
+ cy.get("#secondary-nav").then($navigation => {
86
+ $navigation[0].addEventListener("item-click", itemClickHandler);
87
+ });
88
+ });
89
+
90
+ it("uses mobileLinkTitle in analyticsPath when clicking a top-level item", () => {
91
+ cy.get("#secondary-nav").shadow().find(".udex-secondary-navigation__mobile-nav").click();
92
+ cy.get("#secondary-nav").shadow().find(".udex-secondary-navigation__mobile-nav")
93
+ .shadow().find("udex-secondary-navigation-sub-item").eq(2).click();
94
+ cy.get("@itemClickHandler").should("have.been.called")
95
+ .its("lastCall.args.0.detail.analyticsPath").should("deep.equal", ["Secondary Navigation 3 mobile"]);
96
+ });
97
+
98
+ it("uses title in analyticsPath when mobileLinkTitle is absent", () => {
99
+ cy.get("#secondary-nav").shadow().find(".udex-secondary-navigation__mobile-nav").click();
100
+ cy.get("#secondary-nav").shadow().find(".udex-secondary-navigation__mobile-nav")
101
+ .shadow().find("udex-secondary-navigation-sub-item").eq(1).click();
102
+ cy.get("@itemClickHandler").should("have.been.called")
103
+ .its("lastCall.args.0.detail.analyticsPath").should("deep.equal", ["Secondary Navigation 2"]);
104
+ });
105
+ });
106
+
107
+ describe("mobile-menu-opener-click event", () => {
108
+ beforeEach(() => {
109
+ const mobileMenuOpenerClickHandler = cy.stub().as("mobileMenuOpenerClickHandler");
110
+ cy.get("#secondary-nav").then($navigation => {
111
+ $navigation[0].addEventListener("mobile-menu-opener-click", mobileMenuOpenerClickHandler);
112
+ });
113
+ });
114
+
115
+ it("fires when the mobile menu opener is clicked", () => {
116
+ cy.get("#secondary-nav").shadow().find(".udex-secondary-navigation__mobile-nav").click();
117
+ cy.get("@mobileMenuOpenerClickHandler").should("have.been.calledOnce");
118
+ });
119
+
120
+ it("event detail contains analyticsName", () => {
121
+ cy.get("#secondary-nav").shadow().find(".udex-secondary-navigation__mobile-nav").click();
122
+ cy.get("@mobileMenuOpenerClickHandler").should("have.been.called")
123
+ .its("lastCall.args.0.detail.analyticsName").should("equal", "header-sub");
124
+ });
125
+
126
+ it("event detail contains sourceNode", () => {
127
+ cy.get("#secondary-nav").shadow().find(".udex-secondary-navigation__mobile-nav").click();
128
+ cy.get("@mobileMenuOpenerClickHandler").should("have.been.called")
129
+ .its("lastCall.args.0.detail.sourceNode").should("exist");
130
+ });
131
+
132
+ it("event detail contains originalEvent", () => {
133
+ cy.get("#secondary-nav").shadow().find(".udex-secondary-navigation__mobile-nav").click();
134
+ cy.get("@mobileMenuOpenerClickHandler").should("have.been.called")
135
+ .its("lastCall.args.0.detail.originalEvent").should("exist");
136
+ });
137
+ });
81
138
  });
82
- })
139
+ })