@sapudex/web-components 2.32.0 → 2.32.1
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/cypress/specs/SelectBox.cy.tsx +35 -0
- package/cypress/specs/StepInput.cy.tsx +507 -0
- package/cypress/support/component-imports.instrumented.ts +2 -0
- package/cypress/support/component-imports.src.ts +5 -3
- package/dist/HeroBanner.d.ts +0 -3
- package/dist/HeroBanner.js +0 -5
- package/dist/HeroBanner.js.map +1 -1
- package/dist/HeroBannerTemplate.d.ts +2 -0
- package/dist/HeroBannerTemplate.js +6 -1
- package/dist/HeroBannerTemplate.js.map +1 -1
- package/dist/SelectBase.js +2 -1
- package/dist/SelectBase.js.map +1 -1
- package/dist/StepInput.d.ts +3 -0
- package/dist/StepInput.js +17 -0
- package/dist/StepInput.js.map +1 -1
- package/dist/StepInputTemplate.js +1 -1
- package/dist/StepInputTemplate.js.map +1 -1
- package/dist/assets/{InputSuggestions-CHLLTki3.js → InputSuggestions-DeXhpK0F.js} +1 -1
- package/dist/assets/{ListItemStandardExpandableTextTemplate-vM1eIFe4.js → ListItemStandardExpandableTextTemplate-jMEdrC07.js} +1 -1
- package/dist/assets/{bundle.esm-fUZ1I3qm.js → bundle.esm-CwJAt93x.js} +198 -198
- package/dist/css/themes/FilterMenu.css +1 -1
- package/dist/css/themes/HeroBanner.css +1 -1
- package/dist/css/themes/Modal.css +1 -1
- package/dist/css/themes/RatingIndicator.css +1 -1
- package/dist/css/themes/SecondaryNavigationItem.css +1 -1
- package/dist/css/themes/Slider.css +1 -1
- package/dist/custom-elements-internal.json +154 -154
- package/dist/custom-elements.json +139 -139
- package/dist/generated/themes/FilterMenu.css.d.ts +1 -1
- package/dist/generated/themes/FilterMenu.css.js +1 -1
- package/dist/generated/themes/FilterMenu.css.js.map +1 -1
- package/dist/generated/themes/HeroBanner.css.d.ts +1 -1
- package/dist/generated/themes/HeroBanner.css.js +1 -1
- package/dist/generated/themes/HeroBanner.css.js.map +1 -1
- package/dist/generated/themes/Modal.css.d.ts +1 -1
- package/dist/generated/themes/Modal.css.js +1 -1
- package/dist/generated/themes/Modal.css.js.map +1 -1
- package/dist/generated/themes/RatingIndicator.css.d.ts +1 -1
- package/dist/generated/themes/RatingIndicator.css.js +1 -1
- package/dist/generated/themes/RatingIndicator.css.js.map +1 -1
- package/dist/generated/themes/SecondaryNavigationItem.css.d.ts +1 -1
- package/dist/generated/themes/SecondaryNavigationItem.css.js +1 -1
- package/dist/generated/themes/SecondaryNavigationItem.css.js.map +1 -1
- package/dist/generated/themes/Slider.css.d.ts +1 -1
- package/dist/generated/themes/Slider.css.js +1 -1
- package/dist/generated/themes/Slider.css.js.map +1 -1
- package/dist/web-types.json +1 -1
- package/package.json +1 -1
- package/src/HeroBanner.ts +0 -5
- package/src/HeroBannerTemplate.tsx +6 -1
- package/src/SelectBase.ts +2 -1
- package/src/StepInput.ts +20 -0
- package/src/StepInputTemplate.tsx +1 -1
- package/src/generated/themes/FilterMenu.css.ts +1 -1
- package/src/generated/themes/HeroBanner.css.ts +1 -1
- package/src/generated/themes/Modal.css.ts +1 -1
- package/src/generated/themes/RatingIndicator.css.ts +1 -1
- package/src/generated/themes/SecondaryNavigationItem.css.ts +1 -1
- package/src/generated/themes/Slider.css.ts +1 -1
- package/src/themes/FilterMenu.css +5 -1
- package/src/themes/HeroBanner.css +93 -76
- package/src/themes/Modal.css +3 -2
- package/src/themes/SecondaryNavigationItem.css +4 -1
- package/dist/generated/templates/HeroBannerTemplate.lit.d.ts +0 -3
- package/dist/generated/templates/HeroBannerTemplate.lit.js +0 -15
- package/dist/generated/templates/HeroBannerTemplate.lit.js.map +0 -1
- package/src/HeroBanner.hbs +0 -46
- package/src/generated/templates/HeroBannerTemplate.lit.ts +0 -19
|
@@ -439,6 +439,41 @@ describe("SelectBox", () => {
|
|
|
439
439
|
.should("have.text", "JavaScript");
|
|
440
440
|
});
|
|
441
441
|
|
|
442
|
+
it("SingleSelect mode - typing after selecting an item shows typed character in the search field", () => {
|
|
443
|
+
cy.mount(shortComponent);
|
|
444
|
+
cy.get("#select-box").shadow().find(".udex-text-field__search").as("searchField");
|
|
445
|
+
cy.get("#select-box").shadow().find(".udex-text-field__trigger").click();
|
|
446
|
+
cy.get("#select-box").shadow().find("udex-list-item").eq(0).click();
|
|
447
|
+
|
|
448
|
+
cy.get("#select-box").shadow()
|
|
449
|
+
.find(".udex-text-field__single-label")
|
|
450
|
+
.should("have.text", "JavaScript");
|
|
451
|
+
|
|
452
|
+
cy.get("#select-box").shadow().find(".udex-text-field__trigger").click();
|
|
453
|
+
cy.get("@searchField").realPress("j");
|
|
454
|
+
|
|
455
|
+
cy.get("@searchField").should("have.value", "j");
|
|
456
|
+
cy.get("#select-box").shadow()
|
|
457
|
+
.find(".udex-text-field__single-label")
|
|
458
|
+
.should("not.exist");
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it("SingleSelect mode - programmatic selection change updates the displayed value", () => {
|
|
462
|
+
cy.mount(shortComponent);
|
|
463
|
+
cy.get("#select-box").shadow()
|
|
464
|
+
.find(".udex-text-field__single-label")
|
|
465
|
+
.should("not.exist");
|
|
466
|
+
|
|
467
|
+
cy.get("#select-box").find("udex-select-box-item").eq(1)
|
|
468
|
+
.then($el => {
|
|
469
|
+
($el[0] as any).selected = true;
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
cy.get("#select-box").shadow()
|
|
473
|
+
.find(".udex-text-field__single-label")
|
|
474
|
+
.should("have.text", "Python");
|
|
475
|
+
});
|
|
476
|
+
|
|
442
477
|
it("MultiSelect mode - after clicking on the item dropdown is opened", () => {
|
|
443
478
|
cy.mount(multiSelect);
|
|
444
479
|
cy.get("#select-box").shadow().find(".udex-text-field__trigger").click();
|
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
import { UDExStepInput } from "../support/component-imports.js";
|
|
2
|
+
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Helper: asserts focus has moved to the native <input> inside udex-text-field.
|
|
5
|
+
// _incValue()/_decValue() call this.input.focus() when a boundary is hit.
|
|
6
|
+
//
|
|
7
|
+
// With nested shadow DOMs, document.activeElement returns the outermost shadow
|
|
8
|
+
// host visible to the document (<udex-step-input>). To confirm focus is
|
|
9
|
+
// specifically on <udex-text-field>, we traverse one level deeper using
|
|
10
|
+
// shadowRoot.activeElement on the step-input's shadow root.
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
function assertFocusIsInput() {
|
|
13
|
+
cy.get("[udex-step-input]").then(($host) => {
|
|
14
|
+
cy.document().then((doc) => {
|
|
15
|
+
const stepInput = $host[0];
|
|
16
|
+
const textField = stepInput.shadowRoot?.querySelector("[udex-text-field]");
|
|
17
|
+
expect(
|
|
18
|
+
doc.activeElement === stepInput && stepInput.shadowRoot?.activeElement === textField,
|
|
19
|
+
"focus is inside the text field after boundary was reached",
|
|
20
|
+
).to.be.true;
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Cypress component tests — StepInput focus management on button disable
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
describe("UDExStepInput — focus management on button disable", () => {
|
|
29
|
+
// -------------------------------------------------------------------------
|
|
30
|
+
// Scenario 1: increment to max via click
|
|
31
|
+
// Setup: value=4, min=0, max=5, step=1
|
|
32
|
+
// Action: click the + button (value becomes 5, _incIconDisabled becomes true)
|
|
33
|
+
// Expect: focus moves to the inner text input, NOT lost to document.body
|
|
34
|
+
// -------------------------------------------------------------------------
|
|
35
|
+
describe("incrementing to max value — click", () => {
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
cy.mount(
|
|
38
|
+
<UDExStepInput
|
|
39
|
+
id="si-inc-to-max"
|
|
40
|
+
value={4}
|
|
41
|
+
min={0}
|
|
42
|
+
max={5}
|
|
43
|
+
step={1}
|
|
44
|
+
/>,
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("disables the increment button after reaching max", () => {
|
|
49
|
+
cy.get("[udex-step-input]")
|
|
50
|
+
.shadow()
|
|
51
|
+
.find(".udex-step-input__button_inc")
|
|
52
|
+
.click();
|
|
53
|
+
cy.get("[udex-step-input]")
|
|
54
|
+
.shadow()
|
|
55
|
+
.find(".udex-step-input__button_inc")
|
|
56
|
+
.should("have.attr", "disabled");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Updated: source sends focus to this.input (inner text input), not the opposite button
|
|
60
|
+
it("moves focus to the text input when increment button becomes disabled on click", () => {
|
|
61
|
+
cy.get("[udex-step-input]")
|
|
62
|
+
.shadow()
|
|
63
|
+
.find(".udex-step-input__button_inc")
|
|
64
|
+
.click();
|
|
65
|
+
assertFocusIsInput();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("keeps the decrement button enabled after incrementing to max", () => {
|
|
69
|
+
cy.get("[udex-step-input]")
|
|
70
|
+
.shadow()
|
|
71
|
+
.find(".udex-step-input__button_inc")
|
|
72
|
+
.click();
|
|
73
|
+
cy.get("[udex-step-input]")
|
|
74
|
+
.shadow()
|
|
75
|
+
.find(".udex-step-input__button_dec")
|
|
76
|
+
.should("not.have.attr", "disabled");
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// -------------------------------------------------------------------------
|
|
81
|
+
// Scenario 2: increment to max via keyboard (Enter on the + button)
|
|
82
|
+
// Setup: value=4, min=0, max=5, step=1
|
|
83
|
+
// Action: focus the + button, press Enter (triggers onClick → _incValue)
|
|
84
|
+
// Expect: focus moves to the inner text input
|
|
85
|
+
// -------------------------------------------------------------------------
|
|
86
|
+
describe("incrementing to max value — Enter key", () => {
|
|
87
|
+
beforeEach(() => {
|
|
88
|
+
cy.mount(
|
|
89
|
+
<UDExStepInput
|
|
90
|
+
id="si-inc-to-max-kbd"
|
|
91
|
+
value={4}
|
|
92
|
+
min={0}
|
|
93
|
+
max={5}
|
|
94
|
+
step={1}
|
|
95
|
+
/>,
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("moves focus to the text input when increment button becomes disabled via Enter key", () => {
|
|
100
|
+
cy.get("[udex-step-input]")
|
|
101
|
+
.shadow()
|
|
102
|
+
.find(".udex-step-input__button_inc")
|
|
103
|
+
.shadow()
|
|
104
|
+
.find(".ui5-button-root")
|
|
105
|
+
.focus()
|
|
106
|
+
.realPress("Enter");
|
|
107
|
+
assertFocusIsInput();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("disables the increment button after reaching max via Enter key", () => {
|
|
111
|
+
cy.get("[udex-step-input]")
|
|
112
|
+
.shadow()
|
|
113
|
+
.find(".udex-step-input__button_inc")
|
|
114
|
+
.shadow()
|
|
115
|
+
.find(".ui5-button-root")
|
|
116
|
+
.focus()
|
|
117
|
+
.realPress("Enter");
|
|
118
|
+
cy.get("[udex-step-input]")
|
|
119
|
+
.shadow()
|
|
120
|
+
.find(".udex-step-input__button_inc")
|
|
121
|
+
.should("have.attr", "disabled");
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// -------------------------------------------------------------------------
|
|
126
|
+
// Scenario 3: decrement to min via click
|
|
127
|
+
// Setup: value=1, min=0, max=5, step=1
|
|
128
|
+
// Action: click the - button (value becomes 0, _decIconDisabled becomes true)
|
|
129
|
+
// Expect: focus moves to the inner text input
|
|
130
|
+
// -------------------------------------------------------------------------
|
|
131
|
+
describe("decrementing to min value — click", () => {
|
|
132
|
+
beforeEach(() => {
|
|
133
|
+
cy.mount(
|
|
134
|
+
<UDExStepInput
|
|
135
|
+
id="si-dec-to-min"
|
|
136
|
+
value={1}
|
|
137
|
+
min={0}
|
|
138
|
+
max={5}
|
|
139
|
+
step={1}
|
|
140
|
+
/>,
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("disables the decrement button after reaching min", () => {
|
|
145
|
+
cy.get("[udex-step-input]")
|
|
146
|
+
.shadow()
|
|
147
|
+
.find(".udex-step-input__button_dec")
|
|
148
|
+
.click();
|
|
149
|
+
cy.get("[udex-step-input]")
|
|
150
|
+
.shadow()
|
|
151
|
+
.find(".udex-step-input__button_dec")
|
|
152
|
+
.should("have.attr", "disabled");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Updated: source sends focus to this.input (inner text input), not the opposite button
|
|
156
|
+
it("moves focus to the text input when decrement button becomes disabled on click", () => {
|
|
157
|
+
cy.get("[udex-step-input]")
|
|
158
|
+
.shadow()
|
|
159
|
+
.find(".udex-step-input__button_dec")
|
|
160
|
+
.click();
|
|
161
|
+
assertFocusIsInput();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("keeps the increment button enabled after decrementing to min", () => {
|
|
165
|
+
cy.get("[udex-step-input]")
|
|
166
|
+
.shadow()
|
|
167
|
+
.find(".udex-step-input__button_dec")
|
|
168
|
+
.click();
|
|
169
|
+
cy.get("[udex-step-input]")
|
|
170
|
+
.shadow()
|
|
171
|
+
.find(".udex-step-input__button_inc")
|
|
172
|
+
.should("not.have.attr", "disabled");
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// -------------------------------------------------------------------------
|
|
177
|
+
// Scenario 4: decrement to min via keyboard (Enter on the - button)
|
|
178
|
+
// Setup: value=1, min=0, max=5, step=1
|
|
179
|
+
// Action: focus the - button, press Enter (triggers onClick → _decValue)
|
|
180
|
+
// Expect: focus moves to the inner text input
|
|
181
|
+
// -------------------------------------------------------------------------
|
|
182
|
+
describe("decrementing to min value — Enter key", () => {
|
|
183
|
+
beforeEach(() => {
|
|
184
|
+
cy.mount(
|
|
185
|
+
<UDExStepInput
|
|
186
|
+
id="si-dec-to-min-kbd"
|
|
187
|
+
value={1}
|
|
188
|
+
min={0}
|
|
189
|
+
max={5}
|
|
190
|
+
step={1}
|
|
191
|
+
/>,
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("moves focus to the text input when decrement button becomes disabled via Enter key", () => {
|
|
196
|
+
cy.get("[udex-step-input]")
|
|
197
|
+
.shadow()
|
|
198
|
+
.find(".udex-step-input__button_dec")
|
|
199
|
+
.shadow()
|
|
200
|
+
.find(".ui5-button-root")
|
|
201
|
+
.focus()
|
|
202
|
+
.realPress("Enter");
|
|
203
|
+
assertFocusIsInput();
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("disables the decrement button after reaching min via Enter key", () => {
|
|
207
|
+
cy.get("[udex-step-input]")
|
|
208
|
+
.shadow()
|
|
209
|
+
.find(".udex-step-input__button_dec")
|
|
210
|
+
.shadow()
|
|
211
|
+
.find(".ui5-button-root")
|
|
212
|
+
.focus()
|
|
213
|
+
.realPress("Enter");
|
|
214
|
+
cy.get("[udex-step-input]")
|
|
215
|
+
.shadow()
|
|
216
|
+
.find(".udex-step-input__button_dec")
|
|
217
|
+
.should("have.attr", "disabled");
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// -------------------------------------------------------------------------
|
|
222
|
+
// Scenario 5: min equals max — both buttons disabled on render
|
|
223
|
+
// Both buttons are disabled from the start; clicking them (forced) must
|
|
224
|
+
// not change the value because _incIconInactive / _decIconInactive is true.
|
|
225
|
+
// -------------------------------------------------------------------------
|
|
226
|
+
describe("min equals max — both buttons disabled on render", () => {
|
|
227
|
+
beforeEach(() => {
|
|
228
|
+
cy.mount(
|
|
229
|
+
<UDExStepInput
|
|
230
|
+
id="si-min-eq-max"
|
|
231
|
+
value={3}
|
|
232
|
+
min={3}
|
|
233
|
+
max={3}
|
|
234
|
+
step={1}
|
|
235
|
+
/>,
|
|
236
|
+
);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("disables the increment button when min equals max", () => {
|
|
240
|
+
cy.get("[udex-step-input]")
|
|
241
|
+
.shadow()
|
|
242
|
+
.find(".udex-step-input__button_inc")
|
|
243
|
+
.should("have.attr", "disabled");
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("disables the decrement button when min equals max", () => {
|
|
247
|
+
cy.get("[udex-step-input]")
|
|
248
|
+
.shadow()
|
|
249
|
+
.find(".udex-step-input__button_dec")
|
|
250
|
+
.should("have.attr", "disabled");
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("does not change value when increment button is clicked while both are disabled", () => {
|
|
254
|
+
// The click guard (_incIconInactive) prevents any value change.
|
|
255
|
+
cy.get("[udex-step-input]")
|
|
256
|
+
.shadow()
|
|
257
|
+
.find(".udex-step-input__button_inc")
|
|
258
|
+
.click({ force: true }); // force because the element is disabled
|
|
259
|
+
cy.get("[udex-step-input]").should("have.attr", "value", "3");
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("does not change value when decrement button is clicked while both are disabled", () => {
|
|
263
|
+
cy.get("[udex-step-input]")
|
|
264
|
+
.shadow()
|
|
265
|
+
.find(".udex-step-input__button_dec")
|
|
266
|
+
.click({ force: true });
|
|
267
|
+
cy.get("[udex-step-input]").should("have.attr", "value", "3");
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// -------------------------------------------------------------------------
|
|
272
|
+
// Scenario 6: focus stays inside the component boundary
|
|
273
|
+
// After hitting a boundary the active element must still be a descendant
|
|
274
|
+
// of udex-step-input — focus must never escape to document.body.
|
|
275
|
+
// This validates the core WCAG requirement for focus management.
|
|
276
|
+
// -------------------------------------------------------------------------
|
|
277
|
+
describe("focus stays inside component when a button becomes disabled", () => {
|
|
278
|
+
it("focus remains within step-input after incrementing to max", () => {
|
|
279
|
+
cy.mount(
|
|
280
|
+
<UDExStepInput value={4} min={0} max={5} step={1} />,
|
|
281
|
+
);
|
|
282
|
+
cy.get("[udex-step-input]")
|
|
283
|
+
.shadow()
|
|
284
|
+
.find(".udex-step-input__button_inc")
|
|
285
|
+
.click();
|
|
286
|
+
|
|
287
|
+
cy.document().then((doc) => {
|
|
288
|
+
cy.get("[udex-step-input]").then(($host) => {
|
|
289
|
+
const activeEl = doc.activeElement;
|
|
290
|
+
expect(
|
|
291
|
+
activeEl === $host[0] || $host[0].contains(activeEl),
|
|
292
|
+
"focus stays within udex-step-input after incrementing to max",
|
|
293
|
+
).to.be.true;
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it("focus remains within step-input after decrementing to min", () => {
|
|
299
|
+
cy.mount(
|
|
300
|
+
<UDExStepInput value={1} min={0} max={5} step={1} />,
|
|
301
|
+
);
|
|
302
|
+
cy.get("[udex-step-input]")
|
|
303
|
+
.shadow()
|
|
304
|
+
.find(".udex-step-input__button_dec")
|
|
305
|
+
.click();
|
|
306
|
+
|
|
307
|
+
cy.document().then((doc) => {
|
|
308
|
+
cy.get("[udex-step-input]").then(($host) => {
|
|
309
|
+
const activeEl = doc.activeElement;
|
|
310
|
+
expect(
|
|
311
|
+
activeEl === $host[0] || $host[0].contains(activeEl),
|
|
312
|
+
"focus stays within udex-step-input after decrementing to min",
|
|
313
|
+
).to.be.true;
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// -------------------------------------------------------------------------
|
|
320
|
+
// Scenario 7: focus is NOT redirected when boundary is not reached
|
|
321
|
+
// _incValue / _decValue must only redirect focus when the boundary is hit.
|
|
322
|
+
// Increments and decrements that land in the middle of the range must leave
|
|
323
|
+
// focus on the clicked button (default browser behaviour).
|
|
324
|
+
// -------------------------------------------------------------------------
|
|
325
|
+
describe("focus is not redirected when boundary is not reached", () => {
|
|
326
|
+
it("does not redirect focus to text input after a non-boundary increment", () => {
|
|
327
|
+
cy.mount(
|
|
328
|
+
<UDExStepInput value={2} min={0} max={5} step={1} />,
|
|
329
|
+
);
|
|
330
|
+
cy.get("[udex-step-input]")
|
|
331
|
+
.shadow()
|
|
332
|
+
.find(".udex-step-input__button_inc")
|
|
333
|
+
.click();
|
|
334
|
+
|
|
335
|
+
// Button must still be enabled (value 3, below max 5)
|
|
336
|
+
cy.get("[udex-step-input]")
|
|
337
|
+
.shadow()
|
|
338
|
+
.find(".udex-step-input__button_inc")
|
|
339
|
+
.should("not.have.attr", "disabled");
|
|
340
|
+
|
|
341
|
+
// Focus must NOT have been redirected to the text field
|
|
342
|
+
cy.get("[udex-step-input]")
|
|
343
|
+
.shadow()
|
|
344
|
+
.find("[udex-text-field]")
|
|
345
|
+
.then(($textField) => {
|
|
346
|
+
cy.document().then((doc) => {
|
|
347
|
+
const activeEl = doc.activeElement;
|
|
348
|
+
expect(
|
|
349
|
+
activeEl === $textField[0] || $textField[0].contains(activeEl),
|
|
350
|
+
"focus was NOT incorrectly redirected to text input after non-boundary increment",
|
|
351
|
+
).to.be.false;
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it("does not redirect focus to text input after a non-boundary decrement", () => {
|
|
357
|
+
cy.mount(
|
|
358
|
+
<UDExStepInput value={3} min={0} max={5} step={1} />,
|
|
359
|
+
);
|
|
360
|
+
cy.get("[udex-step-input]")
|
|
361
|
+
.shadow()
|
|
362
|
+
.find(".udex-step-input__button_dec")
|
|
363
|
+
.click();
|
|
364
|
+
|
|
365
|
+
// Button must still be enabled (value 2, above min 0)
|
|
366
|
+
cy.get("[udex-step-input]")
|
|
367
|
+
.shadow()
|
|
368
|
+
.find(".udex-step-input__button_dec")
|
|
369
|
+
.should("not.have.attr", "disabled");
|
|
370
|
+
|
|
371
|
+
// Focus must NOT have been redirected to the text field
|
|
372
|
+
cy.get("[udex-step-input]")
|
|
373
|
+
.shadow()
|
|
374
|
+
.find("[udex-text-field]")
|
|
375
|
+
.then(($textField) => {
|
|
376
|
+
cy.document().then((doc) => {
|
|
377
|
+
const activeEl = doc.activeElement;
|
|
378
|
+
expect(
|
|
379
|
+
activeEl === $textField[0] || $textField[0].contains(activeEl),
|
|
380
|
+
"focus was NOT incorrectly redirected to text input after non-boundary decrement",
|
|
381
|
+
).to.be.false;
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
// -------------------------------------------------------------------------
|
|
388
|
+
// Scenario 8: aria-live region suppressed on boundary hit
|
|
389
|
+
// When _incValue / _decValue hits a boundary and redirects focus to the
|
|
390
|
+
// input, the aria-live region must be empty so the screen reader does not
|
|
391
|
+
// double-announce the value (once from the live region and once from the
|
|
392
|
+
// focused input).
|
|
393
|
+
// -------------------------------------------------------------------------
|
|
394
|
+
describe("aria-live region suppressed on boundary hit", () => {
|
|
395
|
+
it("clears the aria-live region when incrementing to max", () => {
|
|
396
|
+
cy.mount(
|
|
397
|
+
<UDExStepInput value={4} min={0} max={5} step={1} />,
|
|
398
|
+
);
|
|
399
|
+
cy.get("[udex-step-input]")
|
|
400
|
+
.shadow()
|
|
401
|
+
.find(".udex-step-input__button_inc")
|
|
402
|
+
.click();
|
|
403
|
+
|
|
404
|
+
// The increment button must be disabled (boundary reached)
|
|
405
|
+
cy.get("[udex-step-input]")
|
|
406
|
+
.shadow()
|
|
407
|
+
.find(".udex-step-input__button_inc")
|
|
408
|
+
.should("have.attr", "disabled");
|
|
409
|
+
|
|
410
|
+
// The aria-live div must be empty to prevent SR double-announcement
|
|
411
|
+
cy.get("[udex-step-input]")
|
|
412
|
+
.shadow()
|
|
413
|
+
.find("[aria-live]")
|
|
414
|
+
.should("have.text", "");
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it("clears the aria-live region when decrementing to min", () => {
|
|
418
|
+
cy.mount(
|
|
419
|
+
<UDExStepInput value={1} min={0} max={5} step={1} />,
|
|
420
|
+
);
|
|
421
|
+
cy.get("[udex-step-input]")
|
|
422
|
+
.shadow()
|
|
423
|
+
.find(".udex-step-input__button_dec")
|
|
424
|
+
.click();
|
|
425
|
+
|
|
426
|
+
// The decrement button must be disabled (boundary reached)
|
|
427
|
+
cy.get("[udex-step-input]")
|
|
428
|
+
.shadow()
|
|
429
|
+
.find(".udex-step-input__button_dec")
|
|
430
|
+
.should("have.attr", "disabled");
|
|
431
|
+
|
|
432
|
+
// The aria-live div must be empty to prevent SR double-announcement
|
|
433
|
+
cy.get("[udex-step-input]")
|
|
434
|
+
.shadow()
|
|
435
|
+
.find("[aria-live]")
|
|
436
|
+
.should("have.text", "");
|
|
437
|
+
});
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
// -------------------------------------------------------------------------
|
|
441
|
+
// Scenario 9: aria-live region still announces for mid-range changes
|
|
442
|
+
// When the value changes but does NOT hit a boundary, the aria-live
|
|
443
|
+
// region must contain the updated value so the screen reader announces
|
|
444
|
+
// it normally.
|
|
445
|
+
// -------------------------------------------------------------------------
|
|
446
|
+
describe("aria-live region announces value for mid-range changes", () => {
|
|
447
|
+
it("shows the updated value in the aria-live region after a non-boundary increment", () => {
|
|
448
|
+
cy.mount(
|
|
449
|
+
<UDExStepInput value={2} min={0} max={5} step={1} />,
|
|
450
|
+
);
|
|
451
|
+
cy.get("[udex-step-input]")
|
|
452
|
+
.shadow()
|
|
453
|
+
.find(".udex-step-input__button_inc")
|
|
454
|
+
.click();
|
|
455
|
+
|
|
456
|
+
// The increment button must still be enabled (value 3, below max 5)
|
|
457
|
+
cy.get("[udex-step-input]")
|
|
458
|
+
.shadow()
|
|
459
|
+
.find(".udex-step-input__button_inc")
|
|
460
|
+
.should("not.have.attr", "disabled");
|
|
461
|
+
|
|
462
|
+
// The aria-live div must contain the new value for SR announcement
|
|
463
|
+
cy.get("[udex-step-input]")
|
|
464
|
+
.shadow()
|
|
465
|
+
.find("[aria-live]")
|
|
466
|
+
.should("have.text", "3");
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
it("shows the updated value in the aria-live region after a non-boundary decrement", () => {
|
|
470
|
+
cy.mount(
|
|
471
|
+
<UDExStepInput value={3} min={0} max={5} step={1} />,
|
|
472
|
+
);
|
|
473
|
+
cy.get("[udex-step-input]")
|
|
474
|
+
.shadow()
|
|
475
|
+
.find(".udex-step-input__button_dec")
|
|
476
|
+
.click();
|
|
477
|
+
|
|
478
|
+
// The decrement button must still be enabled (value 2, above min 0)
|
|
479
|
+
cy.get("[udex-step-input]")
|
|
480
|
+
.shadow()
|
|
481
|
+
.find(".udex-step-input__button_dec")
|
|
482
|
+
.should("not.have.attr", "disabled");
|
|
483
|
+
|
|
484
|
+
// The aria-live div must contain the new value for SR announcement
|
|
485
|
+
cy.get("[udex-step-input]")
|
|
486
|
+
.shadow()
|
|
487
|
+
.find("[aria-live]")
|
|
488
|
+
.should("have.text", "2");
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
it("shows the precision-formatted value in the aria-live region", () => {
|
|
492
|
+
cy.mount(
|
|
493
|
+
<UDExStepInput value={2} min={0} max={5} step={0.1} valuePrecision={1} />,
|
|
494
|
+
);
|
|
495
|
+
cy.get("[udex-step-input]")
|
|
496
|
+
.shadow()
|
|
497
|
+
.find(".udex-step-input__button_inc")
|
|
498
|
+
.click();
|
|
499
|
+
|
|
500
|
+
// The aria-live div must contain the precision-formatted value
|
|
501
|
+
cy.get("[udex-step-input]")
|
|
502
|
+
.shadow()
|
|
503
|
+
.find("[aria-live]")
|
|
504
|
+
.should("have.text", "2.1");
|
|
505
|
+
});
|
|
506
|
+
});
|
|
507
|
+
});
|
|
@@ -54,6 +54,7 @@ import UDExSlider from "../../instrumented/Slider.js";
|
|
|
54
54
|
import UDExSocialMediaButton from "../../instrumented/SocialMediaButton.js";
|
|
55
55
|
import UDExStatusBadge from "../../instrumented/StatusBadge.js";
|
|
56
56
|
import UDExStatusMessage from "../../instrumented/StatusMessage.js";
|
|
57
|
+
import UDExStepInput from "../../instrumented/StepInput.js";
|
|
57
58
|
import UDExSwitch from "../../instrumented/Switch.js";
|
|
58
59
|
import UDExTable from "../../instrumented/Table.js";
|
|
59
60
|
import UDExTableCell from "../../instrumented/TableCell.js";
|
|
@@ -130,6 +131,7 @@ export {
|
|
|
130
131
|
UDExSocialMediaButton,
|
|
131
132
|
UDExStatusBadge,
|
|
132
133
|
UDExStatusMessage,
|
|
134
|
+
UDExStepInput,
|
|
133
135
|
UDExSwitch,
|
|
134
136
|
UDExTable,
|
|
135
137
|
UDExTableCell,
|
|
@@ -31,9 +31,9 @@ import UDExInputGroup from "../../src/InputGroup.js";
|
|
|
31
31
|
import UDExLink from "../../src/Link.js";
|
|
32
32
|
import UDExLinkList from "../../src/LinkList.js";
|
|
33
33
|
import UDExLinkListItem from "../../src/LinkListItem.js";
|
|
34
|
-
import UDExList from "../../
|
|
35
|
-
import UDExListGroupHeader from "../../
|
|
36
|
-
import UDExListItem from "../../
|
|
34
|
+
import UDExList from "../../src/List.js";
|
|
35
|
+
import UDExListGroupHeader from "../../src/ListGroupHeader.js";
|
|
36
|
+
import UDExListItem from "../../src/ListItem.js";
|
|
37
37
|
import UDExMastheadProfileMenu from "../../src/MastheadProfileMenu.js";
|
|
38
38
|
import UDExMessageStrip from "../../src/MessageStrip.js";
|
|
39
39
|
import UDExMediaPlayer from "../../src/MediaPlayer.js";
|
|
@@ -54,6 +54,7 @@ import UDExSlider from "../../src/Slider.js";
|
|
|
54
54
|
import UDExSocialMediaButton from "../../src/SocialMediaButton.js";
|
|
55
55
|
import UDExStatusBadge from "../../src/StatusBadge.js";
|
|
56
56
|
import UDExStatusMessage from "../../src/StatusMessage.js";
|
|
57
|
+
import UDExStepInput from "../../src/StepInput.js";
|
|
57
58
|
import UDExSwitch from "../../src/Switch.js";
|
|
58
59
|
import UDExTab from "../../src/Tab.js";
|
|
59
60
|
import UDExTabNavigation from "../../src/TabNavigation.js";
|
|
@@ -132,6 +133,7 @@ export {
|
|
|
132
133
|
UDExSocialMediaButton,
|
|
133
134
|
UDExStatusBadge,
|
|
134
135
|
UDExStatusMessage,
|
|
136
|
+
UDExStepInput,
|
|
135
137
|
UDExSwitch,
|
|
136
138
|
UDExTab,
|
|
137
139
|
UDExTabNavigation,
|
package/dist/HeroBanner.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
|
|
2
|
-
import "@ui5/webcomponents-icons/dist/media-play.js";
|
|
3
|
-
import "@ui5/webcomponents-icons/dist/media-pause.js";
|
|
4
2
|
export declare enum UDExImageLoadingStrategies {
|
|
5
3
|
Eager = "eager",
|
|
6
4
|
Lazy = "lazy"
|
|
@@ -106,7 +104,6 @@ declare class UDExHeroBanner extends UI5Element {
|
|
|
106
104
|
get hasMediaContent(): boolean;
|
|
107
105
|
get hasAdditionalContent(): boolean;
|
|
108
106
|
get hasCustomBackgroundPicture(): boolean;
|
|
109
|
-
get mediaButtonIconName(): "media-play" | "media-pause";
|
|
110
107
|
get _video(): HTMLVideoElement | null | undefined;
|
|
111
108
|
_toggleMediaButton(): void;
|
|
112
109
|
_calculateMediaButtonState(): void;
|
package/dist/HeroBanner.js
CHANGED
|
@@ -11,8 +11,6 @@ import slot from "@ui5/webcomponents-base/dist/decorators/slot.js";
|
|
|
11
11
|
import jsxRenderer from "@ui5/webcomponents-base/renderer/JsxRenderer.js";
|
|
12
12
|
import { isSpace } from "@ui5/webcomponents-base/dist/Keys.js";
|
|
13
13
|
import Icon from "@ui5/webcomponents/dist/Icon.js";
|
|
14
|
-
import "@ui5/webcomponents-icons/dist/media-play.js";
|
|
15
|
-
import "@ui5/webcomponents-icons/dist/media-pause.js";
|
|
16
14
|
import HeroBannerTemplate from "./HeroBannerTemplate.js";
|
|
17
15
|
// Styles
|
|
18
16
|
import HeroBannerCss from "./generated/themes/HeroBanner.css.js";
|
|
@@ -76,9 +74,6 @@ let UDExHeroBanner = class UDExHeroBanner extends UI5Element {
|
|
|
76
74
|
get hasCustomBackgroundPicture() {
|
|
77
75
|
return !!this.backgroundPicture?.length;
|
|
78
76
|
}
|
|
79
|
-
get mediaButtonIconName() {
|
|
80
|
-
return this._isVideoRunning ? "media-pause" : "media-play";
|
|
81
|
-
}
|
|
82
77
|
get _video() {
|
|
83
78
|
return this.shadowRoot?.querySelector("video");
|
|
84
79
|
}
|
package/dist/HeroBanner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HeroBanner.js","sourceRoot":"","sources":["../src/HeroBanner.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,UAAU,MAAM,4CAA4C,CAAC;AACpE,OAAO,aAAa,MAAM,0DAA0D,CAAC;AACrF,OAAO,QAAQ,MAAM,qDAAqD,CAAC;AAC3E,OAAO,IAAI,MAAM,iDAAiD,CAAC;AACnE,OAAO,WAAW,MAAM,iDAAiD,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAC;AAC/D,OAAO,IAAI,MAAM,iCAAiC,CAAC;AACnD,OAAO,
|
|
1
|
+
{"version":3,"file":"HeroBanner.js","sourceRoot":"","sources":["../src/HeroBanner.ts"],"names":[],"mappings":"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","sourcesContent":["import UI5Element from \"@ui5/webcomponents-base/dist/UI5Element.js\";\nimport customElement from \"@ui5/webcomponents-base/dist/decorators/customElement.js\";\nimport property from \"@ui5/webcomponents-base/dist/decorators/property.js\";\nimport slot from \"@ui5/webcomponents-base/dist/decorators/slot.js\";\nimport jsxRenderer from \"@ui5/webcomponents-base/renderer/JsxRenderer.js\";\nimport { isSpace } from \"@ui5/webcomponents-base/dist/Keys.js\";\nimport Icon from \"@ui5/webcomponents/dist/Icon.js\";\nimport HeroBannerTemplate from \"./HeroBannerTemplate.js\";\n\n// Styles\nimport HeroBannerCss from \"./generated/themes/HeroBanner.css.js\";\n\nexport enum UDExImageLoadingStrategies {\n Eager = \"eager\",\n Lazy = \"lazy\",\n}\n\n/**\n * @class\n *\n * @constructor\n * @extends UI5Element\n * @tagname udex-hero-banner\n * @public\n */\n@customElement({\n tag: \"udex-hero-banner\",\n renderer: jsxRenderer,\n styles: HeroBannerCss,\n template: HeroBannerTemplate,\n dependencies: [Icon],\n})\nclass UDExHeroBanner extends UI5Element {\n /**\n * Allows control over the background image source.\n *\n * @public\n */\n @property({ type: String })\n backgroundImage?: string;\n\n /**\n * Allows control over the background image loading strategy.\n *\n * @public\n */\n @property()\n backgroundImageLoadingStrategy: `${UDExImageLoadingStrategies}` = UDExImageLoadingStrategies.Eager;\n\n /**\n * Used as the label for background image.\n *\n * @public\n */\n @property({ type: String })\n backgroundImageLabel?: string;\n\n /**\n * Allows control over the background video source.\n *\n * @public\n */\n @property({ type: String })\n backgroundVideo?: string;\n\n /**\n * Used as the label for background video.\n *\n * @public\n */\n @property({ type: String })\n backgroundVideoLabel?: string;\n\n /**\n * Allows control over the background color.\n * Value provided should be compatible with standard CSS rendering, e.g:\n * - HEX: \"#FFF\", \"#FFFFFF\", \"FFFFFFFF\"\n * - RGB/A: \"rgb(255, 255, 255)\", \"rgba(255, 255, 255, 1)\"\n * - HSL/A: \"hsl(0, 100%, 100%)\", \"hsla(0, 100%, 100%, 1\"\n * - Variables: \"var(--sapShell_Background)\", \"var(--udexColorNeutralWhite)\"\n * - Text values: \"white\", \"red\"\n *\n * @public\n */\n @property({ type: String })\n backgroundColor?: string;\n\n /**\n * Allows control over the background-image to set css gradient.\n * Value provided should be compatible with standard CSS linear-gradient rendering, e.g:\n * linear-gradient(to bottom, #1e5799 0%,#7db9e8 100%);\n * linear-gradient(to bottom, var(--udexColorBlue2) 0%,var(--udexColorBlue4) 100%);\n *\n * @public\n */\n @property({ type: String })\n backgroundGradient?: string;\n\n @property({ type: Boolean, noAttribute: true })\n _isVideoRunning?: boolean;\n\n /**\n * Slot for primary content.\n *\n * @slot\n * @public\n */\n @slot()\n content?: Array<HTMLElement>;\n\n /**\n * Slot for image or video content.\n *\n * @slot\n * @public\n */\n @slot()\n mediaContent?: Array<HTMLElement>;\n\n /**\n * Slot for any additional content.\n *\n * @slot\n * @public\n */\n @slot()\n additionalContent?: Array<HTMLElement>;\n\n /**\n * Slot background picture override.\n *\n * @slot\n * @public\n */\n @slot()\n backgroundPicture?: Array<HTMLElement>;\n\n get hasBackgroundImage(): boolean {\n return !!this.backgroundImage;\n }\n get hasBackgroundVideo(): boolean {\n return !!this.backgroundVideo;\n }\n get wrapperBackgroundColor(): string {\n return this.backgroundColor ? this.backgroundColor : \"transparent\";\n }\n\n get wrapperBackgroundImage(): string {\n return this.backgroundGradient ? this.backgroundGradient : \"\";\n }\n\n /**\n * Determines if the video should autoplay based on the user's global preference.\n */\n get shouldVideoAutoplay() {\n return !window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n }\n\n get heroBannerClasses(): string {\n const heroBannerClasses = [\"udex-hero-banner\"];\n if (this.hasBackgroundVideo) {\n heroBannerClasses.push(\"udex-hero-banner--video\");\n }\n return heroBannerClasses.join(\" \");\n }\n\n get hasMediaContent() {\n return !!this.mediaContent?.length;\n }\n\n get hasAdditionalContent() {\n return !!this.additionalContent?.length;\n }\n\n get hasCustomBackgroundPicture() {\n return !!this.backgroundPicture?.length;\n }\n\n get _video() {\n return this.shadowRoot?.querySelector<HTMLVideoElement>(\"video\");\n }\n\n _toggleMediaButton(): void {\n if (this._video?.paused) {\n this._video?.play();\n } else {\n this._video?.pause();\n }\n this._calculateMediaButtonState();\n }\n\n _calculateMediaButtonState() {\n this._isVideoRunning = this._video ? !this._video.paused : false;\n }\n\n _isHTMLDivElement = (element: HTMLElement): element is HTMLDivElement => {\n return element.tagName === \"DIV\";\n };\n\n _handleKeydown(event: KeyboardEvent): void {\n const target = <HTMLElement>event.target;\n if (this._isHTMLDivElement(target) && isSpace(event)) {\n event.preventDefault();\n event.stopPropagation();\n this._toggleMediaButton();\n }\n }\n\n onEnterDOM() {\n if (this.backgroundVideo) {\n if (this.backgroundColor) {\n this.style.setProperty(\"--udexHeroBannerBackgroundVideoColor\", `${this.wrapperBackgroundColor}`);\n }\n this._video?.addEventListener(\"play\", () => {\n this._calculateMediaButtonState();\n });\n }\n }\n}\nUDExHeroBanner.define();\n\nexport default UDExHeroBanner;\n"]}
|