Skip to content

Commit

Permalink
fix(ui5-wizard): observe content height changes (#2801)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
ilhan007 committed Feb 9, 2021
1 parent 282df4b commit b4cbc43
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 17 deletions.
57 changes: 45 additions & 12 deletions packages/fiori/src/Wizard.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ const metadata = {
type: Float,
},

/**
* Defines the height of the <code>ui5-wizard</code> content.
* @private
*/
contentHeight: {
type: Float,
},

_groupedTabs: {
type: String,
multiple: true,
Expand Down Expand Up @@ -203,7 +211,7 @@ class Wizard extends UI5Element {
getItemsCallback: () => this.enabledStepsInHeaderDOM,
});

this._onResize = this.onResize.bind(this);
this._onStepResize = this.onStepResize.bind(this);

this.i18nBundle = getI18nBundle("@ui5/webcomponents");
}
Expand Down Expand Up @@ -260,19 +268,15 @@ class Wizard extends UI5Element {
}

static get CONTENT_TOP_OFFSET() {
return 80;
return 32;
}

static get staticAreaTemplate() {
return WizardPopoverTemplate;
}

onEnterDOM() {
ResizeHandler.register(this, this._onResize);
}

onExitDOM() {
ResizeHandler.deregister(this, this._onResize);
this.detachStepsResizeObserver();
}

onBeforeRendering() {
Expand All @@ -282,6 +286,7 @@ class Wizard extends UI5Element {
onAfterRendering() {
this.storeStepScrollOffsets();
this.scrollToSelectedStep();
this.attachStepsResizeObserver();
}

/**
Expand Down Expand Up @@ -402,19 +407,33 @@ class Wizard extends UI5Element {
}

/**
* Handles component resize to:
* (1) trigger scroll scrollOffset reCalculation and syncSelection
* (2) hide steps' separators and texts to free more space on small sizes
* Handles resize in order to:
* (1) sync steps' scroll offset and selection
* (2) adapt navition step header
* @private
*/
onResize() {
onStepResize() {
this.width = this.getBoundingClientRect().width;
this.contentHeight = this.getContentHeight();

if (this.responsivePopover && this.responsivePopover.opened) {
this._closeRespPopover();
}
}

attachStepsResizeObserver() {
this.stepsDOM.forEach(stepDOM => {
ResizeHandler.deregister(stepDOM, this._onStepResize);
ResizeHandler.register(stepDOM, this._onStepResize);
});
}

detachStepsResizeObserver() {
this.stepsDOM.forEach(stepDOM => {
ResizeHandler.deregister(stepDOM, this._onStepResize);
});
}

/**
* Updates the expanded attribute for each ui5-wizard-tab based on the ui5-wizard width
* @private
Expand Down Expand Up @@ -600,6 +619,20 @@ class Wizard extends UI5Element {
}
}

getContentHeight() {
let contentHeight = 0;

this.stepsDOM.forEach(step => {
contentHeight += step.getBoundingClientRect().height;
});

return contentHeight;
}

get stepsDOM() {
return Array.from(this.shadowRoot.querySelectorAll(".ui5-wiz-content-item"));
}

get _stepsInHeader() {
return this.getStepsInfo();
}
Expand Down Expand Up @@ -826,7 +859,7 @@ class Wizard extends UI5Element {
}
}

return 0;
return this.selectedStepIndex;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/fiori/src/themes/Wizard.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
.ui5-wiz-content {
position: relative;
overflow: auto;
height: 100%;
height: calc(100% - 4rem); /* the navigation height is 4rem */
box-sizing: border-box;
background: var(--sapBackgroundColor);
}
Expand Down
158 changes: 155 additions & 3 deletions packages/fiori/test/pages/Wizard_test.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
</ui5-label>

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

<ui5-button id="toStep2" design="Emphasized">Step 2</ui5-button>
Expand Down Expand Up @@ -78,6 +79,9 @@
<ui5-label>5 years guarantee included</ui5-label>
<ui5-switch id="sw"></ui5-switch>
</div>

<div id="pureContent" style="height: 1800px; background-color: red; display:none;">
</div>
</div>
</div>

Expand Down Expand Up @@ -122,6 +126,7 @@
</div>
</div>

<ui5-button id="toStep22" design="Emphasized">Step 2</ui5-button>
<ui5-button id="toStep4" design="Emphasized">Step 4</ui5-button>
</ui5-wizard-step>

Expand Down Expand Up @@ -156,6 +161,140 @@
<ui5-button id="finalize" design="Emphasized">Finalize</ui5-button>
</ui5-wizard-step>
</ui5-wizard>

<ui5-dialog id="dialog" stretch header-heading="Wizard">
<ui5-wizard>
<ui5-wizard-step icon="sap-icon://product" selected heading="Product type">
<div style="display: flex; min-height: 200px; flex-direction: column;">
<ui5-title>1. Product Type</ui5-title><br>

<ui5-messagestrip>
The Wizard control is supposed to break down large tasks, into smaller steps, easier for the user to work with.
</ui5-messagestrip><br>

<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.
</ui5-label>

<ui5-input></ui5-input>
</div>

<ui5-button design="Emphasized">Step 2</ui5-button>
</ui5-wizard-step>

<ui5-wizard-step heading="Product Information">
<div style="display: flex;flex-direction: column">
<ui5-title>2. Product Information</ui5-title><br>
<ui5-label wrap>
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
</ui5-label>

<div style="display: flex; flex-direction: column;">
<div style="display: flex; flex-direction: row; justify-content: flex-end; align-items: center; margin-top: 1rem">
<ui5-label>Name</ui5-label>
<ui5-input placeholder="product name..."></ui5-input>
</div>

<div style="display: flex; flex-direction: row; margin-top: 1rem; justify-content: flex-end; align-items: center;">
<ui5-label>Weight</ui5-label>
<ui5-input value="3.65"></ui5-input>
</div>

<div style="display: flex; flex-direction: row; margin-top: 1rem; justify-content: flex-end; align-items: center;">
<ui5-label>Manifacturer</ui5-label>
<ui5-select>
<ui5-option selected>Apple</ui5-option>
<ui5-option>Samsung</ui5-option>
<ui5-option>Huawei</ui5-option>
</ui5-select>
</div>

<div style="display: flex; flex-direction: row; margin-top: 1rem; justify-content: flex-end; align-items: center;">
<ui5-label>5 years guarantee included</ui5-label>
<ui5-switch id="sw2"></ui5-switch>
</div>


<div id="pureContent2" style="height: 1800px; background-color: red; display:none;">
</div>
</div>
</div>

<ui5-button design="Emphasized">Step 3</ui5-button>
</ui5-wizard-step>

<ui5-wizard-step heading="Options">
<div style="display: flex; flex-direction: column;">
<ui5-title>3. Options</ui5-title><br>

<ui5-label wrap>
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
</ui5-label>
<ui5-messagestrip>
The Wizard control is supposed to break down large tasks, into smaller steps, easier for the user to work with.
</ui5-messagestrip><br>

<div style="display: flex; flex-direction: column;">
<div style="display: flex; flex-direction: row; justify-content: flex-end; align-items: center; margin-top: 1rem">
<ui5-label>Manifacture date</ui5-label>
<ui5-date-picker></ui5-date-picker>
</div>

<div style="display: flex; flex-direction: row; justify-content: flex-end; align-items: center; margin-top: 1rem">
<ui5-label>Availability</ui5-label>
<ui5-segmentedbutton>
<ui5-togglebutton icon="employee" pressed>In stock</ui5-togglebutton>
<ui5-togglebutton>In depot</ui5-togglebutton>
<ui5-togglebutton>Damaged</ui5-togglebutton>
<ui5-togglebutton>Out of stock</ui5-togglebutton>
</ui5-segmentedbutton>
</div>

<div style="display: flex; flex-direction: row; justify-content: flex-end; align-items: center; margin-top: 1rem">
<ui5-label>Size</ui5-label>
<ui5-segmentedbutton>
<ui5-togglebutton icon="employee" pressed>Small</ui5-togglebutton>
<ui5-togglebutton>Medium</ui5-togglebutton>
<ui5-togglebutton>Largr</ui5-togglebutton>
</ui5-segmentedbutton>
</div>
</div>
</div>

<ui5-button design="Emphasized">Step 4</ui5-button>
</ui5-wizard-step>

<ui5-wizard-step heading="Pricing">
<div style="display: flex; flex-direction: column;">
<ui5-title>4. Pricing</ui5-title><br>
<ui5-label wrap>
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
</ui5-label>
<ui5-messagestrip>
The Wizard control is supposed to break down large tasks, into smaller steps, easier for the user to work with.
</ui5-messagestrip><br>

<div style="display: flex; flex-direction: column;">
<div style="display: flex; flex-direction: row; justify-content: flex-end; align-items: center; margin-top: 1rem">
<ui5-label>Price</ui5-label>
<ui5-input placeholder="product price..."></ui5-input>
</div>

<div style="display: flex; flex-direction: row; justify-content: flex-end; align-items: center; margin-top: 1rem">
<ui5-label>Quantity</ui5-label>
<ui5-input placeholder="product quantity..."></ui5-input>
</div>

<div style="display: flex; flex-direction: row; margin-top: 1rem; justify-content: flex-end; align-items: center;">
<ui5-label>Vat included</ui5-label>
<ui5-switch checked></ui5-switch>
</div>
</div>
</div>

<ui5-button design="Emphasized">Finalize</ui5-button>
</ui5-wizard-step>
</ui5-wizard>
</ui5-dialog>
</div>

<script>
Expand All @@ -169,19 +308,20 @@
toStep2.addEventListener("click", function () {
deselectAll();
setStep(1);
toStep2.setAttribute("hidden", true);
});
toStep22.addEventListener("click", function () {
deselectAll();
setStep(1);
});

toStep3.addEventListener("click", function () {
deselectAll();
setStep(2);
toStep3.setAttribute("hidden", true);
});

toStep4.addEventListener("click", function () {
deselectAll();
setStep(3);
toStep4.setAttribute("hidden", true);
});

function deselectAll() {
Expand All @@ -199,6 +339,18 @@
function getStep(idx) {
return Array.from(wiz.children)[idx];
}

sw.addEventListener("change", function () {
pureContent.style.display = "block";
});

sw2.addEventListener("change", function () {
pureContent2.style.display = "block";
});

btnOpenDialog.addEventListener("click", function () {
dialog.open();
});
</script>
</body>
</html>
25 changes: 24 additions & 1 deletion packages/fiori/test/specs/Wizard.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,29 @@ describe("Wizard general interaction", () => {
assert.strictEqual(step2InHeader.getAttribute("disabled"), null, "Second step in header is enabled.");

assert.strictEqual(inpSelectionChangeCounter.getProperty("value"), "4",
"Event selection-change fired 4rd time due to scrolling.");
"Event selection-change fired 4th time due to scrolling.");
});

it("tests dynamically increase step size and move to next step", () => {
const wiz = browser.$("#wizTest");
const sw = browser.$("#sw");
const btnToStep2 = browser.$("#toStep22");
const btnToStep3 = browser.$("#toStep3");
const step3 = browser.$("#st3");
const step3InHeader = wiz.shadow$(`[data-ui5-index="3"]`);
const inpSelectionChangeCounter = browser.$("#inpSelectionChangeCounter");

btnToStep3.click(); // click to enable step 3
btnToStep2.click(); // click to get back to step 2
sw.click(); // click to dynamically expand content in step 2
step3.scrollIntoView(); // scroll to step 3
browser.pause(500);

assert.strictEqual(step3.getAttribute("selected"), "true",
"Third step in the content is selected.");
assert.strictEqual(step3InHeader.getAttribute("selected"), "true",
"Third step in the header is selected.");
assert.strictEqual(inpSelectionChangeCounter.getProperty("value"), "5",
"Event selection-change fired once for 5th time due to scrolling.");
});
});

0 comments on commit b4cbc43

Please sign in to comment.