@sapudex/web-components 2.31.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/Breadcrumbs.cy.tsx +26 -0
- package/cypress/specs/MediaPlayer.cy.tsx +209 -0
- package/cypress/specs/SecondaryNavigationDesktop.cy.tsx +74 -21
- package/cypress/specs/SecondaryNavigationMobile.cy.tsx +58 -1
- package/cypress/specs/SelectBox.cy.tsx +35 -0
- package/cypress/specs/StepInput.cy.tsx +507 -0
- package/cypress/specs/TabNavigation.cy.tsx +603 -39
- package/cypress/support/component-imports.instrumented.ts +4 -0
- package/cypress/support/component-imports.src.ts +7 -3
- package/dist/Breadcrumbs.d.ts +5 -0
- package/dist/Breadcrumbs.js +8 -1
- package/dist/Breadcrumbs.js.map +1 -1
- 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/MastheadItem.js +5 -4
- package/dist/MastheadItem.js.map +1 -1
- package/dist/MediaPlayer.d.ts +15 -0
- package/dist/MediaPlayer.js +28 -0
- package/dist/MediaPlayer.js.map +1 -1
- package/dist/SecondaryNavigation.d.ts +12 -2
- package/dist/SecondaryNavigation.js +59 -8
- package/dist/SecondaryNavigation.js.map +1 -1
- package/dist/SecondaryNavigationItem.d.ts +0 -1
- package/dist/SecondaryNavigationItem.js +0 -4
- package/dist/SecondaryNavigationItem.js.map +1 -1
- package/dist/SecondaryNavigationSubItem.js +1 -1
- package/dist/SecondaryNavigationSubItem.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/Tab.d.ts +1 -1
- package/dist/Tab.js +3 -3
- package/dist/Tab.js.map +1 -1
- package/dist/TabNavigation.d.ts +64 -0
- package/dist/TabNavigation.js +124 -1
- package/dist/TabNavigation.js.map +1 -1
- package/dist/assets/{InputSuggestions-i2kFpCfC.js → InputSuggestions-DeXhpK0F.js} +1 -1
- package/dist/assets/{ListItemStandardExpandableTextTemplate-C1ceDaxu.js → ListItemStandardExpandableTextTemplate-jMEdrC07.js} +1 -1
- package/dist/assets/{bundle.esm-DQ3cxywT.js → bundle.esm-CwJAt93x.js} +219 -219
- package/dist/assets/messagebundle_en-iNqRB1VZ.js +1 -0
- package/dist/css/themes/BreadcrumbsPopover.css +1 -1
- 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/css/themes/TabNavigation.css +1 -1
- package/dist/custom-elements-internal.json +221 -0
- package/dist/custom-elements.json +149 -0
- package/dist/generated/assets/i18n/messagebundle_en.json +1 -1
- package/dist/generated/i18n/i18n-defaults.js +1 -1
- package/dist/generated/i18n/i18n-defaults.js.map +1 -1
- package/dist/generated/templates/SecondaryNavigationTemplate.lit.js +1 -1
- package/dist/generated/templates/SecondaryNavigationTemplate.lit.js.map +1 -1
- package/dist/generated/templates/TabNavigationTemplate.lit.js +10 -8
- package/dist/generated/templates/TabNavigationTemplate.lit.js.map +1 -1
- package/dist/generated/themes/BreadcrumbsPopover.css.d.ts +1 -1
- package/dist/generated/themes/BreadcrumbsPopover.css.js +1 -1
- package/dist/generated/themes/BreadcrumbsPopover.css.js.map +1 -1
- 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/generated/themes/TabNavigation.css.d.ts +1 -1
- package/dist/generated/themes/TabNavigation.css.js +1 -1
- package/dist/generated/themes/TabNavigation.css.js.map +1 -1
- package/dist/messagebundle.properties +1 -1
- package/dist/messagebundle_en.properties +1 -1
- package/dist/vscode.html-custom-data.json +23 -3
- package/dist/web-types.json +64 -4
- package/package.json +1 -1
- package/src/Breadcrumbs.ts +9 -1
- package/src/HeroBanner.ts +0 -5
- package/src/HeroBannerTemplate.tsx +6 -1
- package/src/MastheadItem.ts +5 -4
- package/src/MediaPlayer.ts +35 -0
- package/src/SecondaryNavigation.hbs +1 -0
- package/src/SecondaryNavigation.ts +77 -9
- package/src/SecondaryNavigationItem.ts +0 -6
- package/src/SecondaryNavigationSubItem.ts +1 -1
- package/src/SelectBase.ts +2 -1
- package/src/StepInput.ts +20 -0
- package/src/StepInputTemplate.tsx +1 -1
- package/src/Tab.ts +4 -4
- package/src/TabNavigation.hbs +15 -0
- package/src/TabNavigation.ts +134 -1
- package/src/generated/i18n/i18n-defaults.ts +1 -1
- package/src/generated/templates/SecondaryNavigationTemplate.lit.ts +1 -1
- package/src/generated/templates/TabNavigationTemplate.lit.ts +10 -8
- package/src/generated/themes/BreadcrumbsPopover.css.ts +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/generated/themes/TabNavigation.css.ts +1 -1
- package/src/i18n/messagebundle.properties +1 -1
- package/src/i18n/messagebundle_en.properties +1 -1
- package/src/themes/BreadcrumbsPopover.css +2 -2
- 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/src/themes/TabNavigation.css +27 -16
- package/dist/assets/messagebundle_en-DWAgV5VH.js +0 -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
|
@@ -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
|
+
});
|