Skip to content

Commit b4cbc43

Browse files
authored
fix(ui5-wizard): observe content height changes (#2801)
Issue: When the step content size is changed runtime, step selection might go wrong as the steps' scroll positions are out of sync - the wizard works with the old ones. Solution: Observe steps and update state to store steps' scroll positions. FIXES: #2784
1 parent 282df4b commit b4cbc43

File tree

4 files changed

+225
-17
lines changed

4 files changed

+225
-17
lines changed

packages/fiori/src/Wizard.js

+45-12
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ const metadata = {
6363
type: Float,
6464
},
6565

66+
/**
67+
* Defines the height of the <code>ui5-wizard</code> content.
68+
* @private
69+
*/
70+
contentHeight: {
71+
type: Float,
72+
},
73+
6674
_groupedTabs: {
6775
type: String,
6876
multiple: true,
@@ -203,7 +211,7 @@ class Wizard extends UI5Element {
203211
getItemsCallback: () => this.enabledStepsInHeaderDOM,
204212
});
205213

206-
this._onResize = this.onResize.bind(this);
214+
this._onStepResize = this.onStepResize.bind(this);
207215

208216
this.i18nBundle = getI18nBundle("@ui5/webcomponents");
209217
}
@@ -260,19 +268,15 @@ class Wizard extends UI5Element {
260268
}
261269

262270
static get CONTENT_TOP_OFFSET() {
263-
return 80;
271+
return 32;
264272
}
265273

266274
static get staticAreaTemplate() {
267275
return WizardPopoverTemplate;
268276
}
269277

270-
onEnterDOM() {
271-
ResizeHandler.register(this, this._onResize);
272-
}
273-
274278
onExitDOM() {
275-
ResizeHandler.deregister(this, this._onResize);
279+
this.detachStepsResizeObserver();
276280
}
277281

278282
onBeforeRendering() {
@@ -282,6 +286,7 @@ class Wizard extends UI5Element {
282286
onAfterRendering() {
283287
this.storeStepScrollOffsets();
284288
this.scrollToSelectedStep();
289+
this.attachStepsResizeObserver();
285290
}
286291

287292
/**
@@ -402,19 +407,33 @@ class Wizard extends UI5Element {
402407
}
403408

404409
/**
405-
* Handles component resize to:
406-
* (1) trigger scroll scrollOffset reCalculation and syncSelection
407-
* (2) hide steps' separators and texts to free more space on small sizes
410+
* Handles resize in order to:
411+
* (1) sync steps' scroll offset and selection
412+
* (2) adapt navition step header
408413
* @private
409414
*/
410-
onResize() {
415+
onStepResize() {
411416
this.width = this.getBoundingClientRect().width;
417+
this.contentHeight = this.getContentHeight();
412418

413419
if (this.responsivePopover && this.responsivePopover.opened) {
414420
this._closeRespPopover();
415421
}
416422
}
417423

424+
attachStepsResizeObserver() {
425+
this.stepsDOM.forEach(stepDOM => {
426+
ResizeHandler.deregister(stepDOM, this._onStepResize);
427+
ResizeHandler.register(stepDOM, this._onStepResize);
428+
});
429+
}
430+
431+
detachStepsResizeObserver() {
432+
this.stepsDOM.forEach(stepDOM => {
433+
ResizeHandler.deregister(stepDOM, this._onStepResize);
434+
});
435+
}
436+
418437
/**
419438
* Updates the expanded attribute for each ui5-wizard-tab based on the ui5-wizard width
420439
* @private
@@ -600,6 +619,20 @@ class Wizard extends UI5Element {
600619
}
601620
}
602621

622+
getContentHeight() {
623+
let contentHeight = 0;
624+
625+
this.stepsDOM.forEach(step => {
626+
contentHeight += step.getBoundingClientRect().height;
627+
});
628+
629+
return contentHeight;
630+
}
631+
632+
get stepsDOM() {
633+
return Array.from(this.shadowRoot.querySelectorAll(".ui5-wiz-content-item"));
634+
}
635+
603636
get _stepsInHeader() {
604637
return this.getStepsInfo();
605638
}
@@ -826,7 +859,7 @@ class Wizard extends UI5Element {
826859
}
827860
}
828861

829-
return 0;
862+
return this.selectedStepIndex;
830863
}
831864

832865
/**

packages/fiori/src/themes/Wizard.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
.ui5-wiz-content {
1515
position: relative;
1616
overflow: auto;
17-
height: 100%;
17+
height: calc(100% - 4rem); /* the navigation height is 4rem */
1818
box-sizing: border-box;
1919
background: var(--sapBackgroundColor);
2020
}

packages/fiori/test/pages/Wizard_test.html

+155-3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
</ui5-label>
4343

4444
<ui5-input id="inpSelectionChangeCounter"></ui5-input>
45+
<ui5-button id="btnOpenDialog" style="width: 150px; align-self: flex-end;">Open Wizard Dialog</ui5-button>
4546
</div>
4647

4748
<ui5-button id="toStep2" design="Emphasized">Step 2</ui5-button>
@@ -78,6 +79,9 @@
7879
<ui5-label>5 years guarantee included</ui5-label>
7980
<ui5-switch id="sw"></ui5-switch>
8081
</div>
82+
83+
<div id="pureContent" style="height: 1800px; background-color: red; display:none;">
84+
</div>
8185
</div>
8286
</div>
8387

@@ -122,6 +126,7 @@
122126
</div>
123127
</div>
124128

129+
<ui5-button id="toStep22" design="Emphasized">Step 2</ui5-button>
125130
<ui5-button id="toStep4" design="Emphasized">Step 4</ui5-button>
126131
</ui5-wizard-step>
127132

@@ -156,6 +161,140 @@
156161
<ui5-button id="finalize" design="Emphasized">Finalize</ui5-button>
157162
</ui5-wizard-step>
158163
</ui5-wizard>
164+
165+
<ui5-dialog id="dialog" stretch header-heading="Wizard">
166+
<ui5-wizard>
167+
<ui5-wizard-step icon="sap-icon://product" selected heading="Product type">
168+
<div style="display: flex; min-height: 200px; flex-direction: column;">
169+
<ui5-title>1. Product Type</ui5-title><br>
170+
171+
<ui5-messagestrip>
172+
The Wizard control is supposed to break down large tasks, into smaller steps, easier for the user to work with.
173+
</ui5-messagestrip><br>
174+
175+
<ui5-label wrap>Sed fermentum, mi et tristique ullamcorper, sapien sapien faucibus sem, quis pretium nibh lorem malesuada diam. Nulla quis arcu aliquet, feugiat massa semper, volutpat diam. Nam vitae ante posuere, molestie neque sit amet, dapibus velit. Maecenas eleifend tempor lorem. Mauris vitae elementum mi, sed eleifend ligula. Nulla tempor vulputate dolor, nec dignissim quam convallis ut. Praesent vitae commodo felis, ut iaculis felis. Fusce quis eleifend sapien, eget facilisis nibh. Suspendisse est velit, scelerisque ut commodo eget, dignissim quis metus. Cras faucibus consequat gravida. Curabitur vitae quam felis. Phasellus ac leo eleifend, commodo tortor et, varius quam. Aliquam erat volutpat.
176+
</ui5-label>
177+
178+
<ui5-input></ui5-input>
179+
</div>
180+
181+
<ui5-button design="Emphasized">Step 2</ui5-button>
182+
</ui5-wizard-step>
183+
184+
<ui5-wizard-step heading="Product Information">
185+
<div style="display: flex;flex-direction: column">
186+
<ui5-title>2. Product Information</ui5-title><br>
187+
<ui5-label wrap>
188+
Integer pellentesque leo sit amet dui vehicula, quis ullamcorper est pulvinar. Nam in libero sem. Suspendisse arcu metus, molestie a turpis a, molestie aliquet dui. Donec ppellentesque leo sit amet dui vehicula, quis ullamcorper est pulvinar. Nam in libero sem. Suspendisse arcu metus, molestie a turpis a, molestie aliquet dui. Donec pulvinar, sapien corper eu, posuere malesuada nisl. Integer pellentesque leo sit amet dui vehicula, quis ullamcorper est pulvinar. Nam in libero sem. Suspendisse arcu metus, molestie a turpis a, molestie aliquet dui. Donec pulvinar, sapien
189+
</ui5-label>
190+
191+
<div style="display: flex; flex-direction: column;">
192+
<div style="display: flex; flex-direction: row; justify-content: flex-end; align-items: center; margin-top: 1rem">
193+
<ui5-label>Name</ui5-label>
194+
<ui5-input placeholder="product name..."></ui5-input>
195+
</div>
196+
197+
<div style="display: flex; flex-direction: row; margin-top: 1rem; justify-content: flex-end; align-items: center;">
198+
<ui5-label>Weight</ui5-label>
199+
<ui5-input value="3.65"></ui5-input>
200+
</div>
201+
202+
<div style="display: flex; flex-direction: row; margin-top: 1rem; justify-content: flex-end; align-items: center;">
203+
<ui5-label>Manifacturer</ui5-label>
204+
<ui5-select>
205+
<ui5-option selected>Apple</ui5-option>
206+
<ui5-option>Samsung</ui5-option>
207+
<ui5-option>Huawei</ui5-option>
208+
</ui5-select>
209+
</div>
210+
211+
<div style="display: flex; flex-direction: row; margin-top: 1rem; justify-content: flex-end; align-items: center;">
212+
<ui5-label>5 years guarantee included</ui5-label>
213+
<ui5-switch id="sw2"></ui5-switch>
214+
</div>
215+
216+
217+
<div id="pureContent2" style="height: 1800px; background-color: red; display:none;">
218+
</div>
219+
</div>
220+
</div>
221+
222+
<ui5-button design="Emphasized">Step 3</ui5-button>
223+
</ui5-wizard-step>
224+
225+
<ui5-wizard-step heading="Options">
226+
<div style="display: flex; flex-direction: column;">
227+
<ui5-title>3. Options</ui5-title><br>
228+
229+
<ui5-label wrap>
230+
Integer pellentesque leo sit amet dui vehicula, quis ullamcorper est pulvinar. Nam in libero sem. Suspendisse arcu metus, molestie a turpis a, molestie aliquet dui. Donec ppellentesque leo sit amet dui vehicula, quis ullamcorper est pulvinar. Nam in libero sem. Suspendisse arcu metus, molestie a turpis a, molestie aliquet dui. Donec pulvinar, sapien corper eu, posuere malesuada nisl. Integer pellentesque leo sit amet dui vehicula, quis ullamcorper est pulvinar. Nam in libero sem. Suspendisse arcu metus, molestie a turpis a, molestie aliquet dui. Donec pulvinar, sapien
231+
</ui5-label>
232+
<ui5-messagestrip>
233+
The Wizard control is supposed to break down large tasks, into smaller steps, easier for the user to work with.
234+
</ui5-messagestrip><br>
235+
236+
<div style="display: flex; flex-direction: column;">
237+
<div style="display: flex; flex-direction: row; justify-content: flex-end; align-items: center; margin-top: 1rem">
238+
<ui5-label>Manifacture date</ui5-label>
239+
<ui5-date-picker></ui5-date-picker>
240+
</div>
241+
242+
<div style="display: flex; flex-direction: row; justify-content: flex-end; align-items: center; margin-top: 1rem">
243+
<ui5-label>Availability</ui5-label>
244+
<ui5-segmentedbutton>
245+
<ui5-togglebutton icon="employee" pressed>In stock</ui5-togglebutton>
246+
<ui5-togglebutton>In depot</ui5-togglebutton>
247+
<ui5-togglebutton>Damaged</ui5-togglebutton>
248+
<ui5-togglebutton>Out of stock</ui5-togglebutton>
249+
</ui5-segmentedbutton>
250+
</div>
251+
252+
<div style="display: flex; flex-direction: row; justify-content: flex-end; align-items: center; margin-top: 1rem">
253+
<ui5-label>Size</ui5-label>
254+
<ui5-segmentedbutton>
255+
<ui5-togglebutton icon="employee" pressed>Small</ui5-togglebutton>
256+
<ui5-togglebutton>Medium</ui5-togglebutton>
257+
<ui5-togglebutton>Largr</ui5-togglebutton>
258+
</ui5-segmentedbutton>
259+
</div>
260+
</div>
261+
</div>
262+
263+
<ui5-button design="Emphasized">Step 4</ui5-button>
264+
</ui5-wizard-step>
265+
266+
<ui5-wizard-step heading="Pricing">
267+
<div style="display: flex; flex-direction: column;">
268+
<ui5-title>4. Pricing</ui5-title><br>
269+
<ui5-label wrap>
270+
Integer pellentesque leo sit amet dui vehicula, quis ullamcorper est pulvinar. Nam in libero sem. Suspendisse arcu metus, molestie a turpis a, molestie aliquet dui. Donec ppellentesque leo sit amet dui vehicula, quis ullamcorper est pulvinar. Nam in libero sem. Suspendisse arcu metus, molestie a turpis a, molestie aliquet dui. Donec pulvinar, sapien corper eu, posuere malesuada nisl. Integer pellentesque leo sit amet dui vehicula, quis ullamcorper est pulvinar. Nam in libero sem. Suspendisse arcu metus, molestie a turpis a, molestie aliquet dui. Donec pulvinar, sapien
271+
</ui5-label>
272+
<ui5-messagestrip>
273+
The Wizard control is supposed to break down large tasks, into smaller steps, easier for the user to work with.
274+
</ui5-messagestrip><br>
275+
276+
<div style="display: flex; flex-direction: column;">
277+
<div style="display: flex; flex-direction: row; justify-content: flex-end; align-items: center; margin-top: 1rem">
278+
<ui5-label>Price</ui5-label>
279+
<ui5-input placeholder="product price..."></ui5-input>
280+
</div>
281+
282+
<div style="display: flex; flex-direction: row; justify-content: flex-end; align-items: center; margin-top: 1rem">
283+
<ui5-label>Quantity</ui5-label>
284+
<ui5-input placeholder="product quantity..."></ui5-input>
285+
</div>
286+
287+
<div style="display: flex; flex-direction: row; margin-top: 1rem; justify-content: flex-end; align-items: center;">
288+
<ui5-label>Vat included</ui5-label>
289+
<ui5-switch checked></ui5-switch>
290+
</div>
291+
</div>
292+
</div>
293+
294+
<ui5-button design="Emphasized">Finalize</ui5-button>
295+
</ui5-wizard-step>
296+
</ui5-wizard>
297+
</ui5-dialog>
159298
</div>
160299

161300
<script>
@@ -169,19 +308,20 @@
169308
toStep2.addEventListener("click", function () {
170309
deselectAll();
171310
setStep(1);
172-
toStep2.setAttribute("hidden", true);
311+
});
312+
toStep22.addEventListener("click", function () {
313+
deselectAll();
314+
setStep(1);
173315
});
174316

175317
toStep3.addEventListener("click", function () {
176318
deselectAll();
177319
setStep(2);
178-
toStep3.setAttribute("hidden", true);
179320
});
180321

181322
toStep4.addEventListener("click", function () {
182323
deselectAll();
183324
setStep(3);
184-
toStep4.setAttribute("hidden", true);
185325
});
186326

187327
function deselectAll() {
@@ -199,6 +339,18 @@
199339
function getStep(idx) {
200340
return Array.from(wiz.children)[idx];
201341
}
342+
343+
sw.addEventListener("change", function () {
344+
pureContent.style.display = "block";
345+
});
346+
347+
sw2.addEventListener("change", function () {
348+
pureContent2.style.display = "block";
349+
});
350+
351+
btnOpenDialog.addEventListener("click", function () {
352+
dialog.open();
353+
});
202354
</script>
203355
</body>
204356
</html>

packages/fiori/test/specs/Wizard.spec.js

+24-1
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,29 @@ describe("Wizard general interaction", () => {
150150
assert.strictEqual(step2InHeader.getAttribute("disabled"), null, "Second step in header is enabled.");
151151

152152
assert.strictEqual(inpSelectionChangeCounter.getProperty("value"), "4",
153-
"Event selection-change fired 4rd time due to scrolling.");
153+
"Event selection-change fired 4th time due to scrolling.");
154+
});
155+
156+
it("tests dynamically increase step size and move to next step", () => {
157+
const wiz = browser.$("#wizTest");
158+
const sw = browser.$("#sw");
159+
const btnToStep2 = browser.$("#toStep22");
160+
const btnToStep3 = browser.$("#toStep3");
161+
const step3 = browser.$("#st3");
162+
const step3InHeader = wiz.shadow$(`[data-ui5-index="3"]`);
163+
const inpSelectionChangeCounter = browser.$("#inpSelectionChangeCounter");
164+
165+
btnToStep3.click(); // click to enable step 3
166+
btnToStep2.click(); // click to get back to step 2
167+
sw.click(); // click to dynamically expand content in step 2
168+
step3.scrollIntoView(); // scroll to step 3
169+
browser.pause(500);
170+
171+
assert.strictEqual(step3.getAttribute("selected"), "true",
172+
"Third step in the content is selected.");
173+
assert.strictEqual(step3InHeader.getAttribute("selected"), "true",
174+
"Third step in the header is selected.");
175+
assert.strictEqual(inpSelectionChangeCounter.getProperty("value"), "5",
176+
"Event selection-change fired once for 5th time due to scrolling.");
154177
});
155178
});

0 commit comments

Comments
 (0)