Skip to content

Toolbar items physical #11374

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
12f4567
fix(ui5-shellbar): avatar font-size reduced
Apr 14, 2025
be09a55
Merge branch 'main' of github.com:SAP/ui5-webcomponents
Apr 14, 2025
79216a5
chore(ui5-toolbar): physical items introduced
Apr 15, 2025
e087244
Revert "chore(ui5-toolbar): physical items introduced"
Apr 15, 2025
fc684e7
Merge branch 'main' of github.com:SAP/ui5-webcomponents
Apr 15, 2025
e691edf
chore(ui5-toolbar): physical items introduced
Apr 15, 2025
d4f8b1c
chore(ui5-toolbar): items maded physical
Apr 17, 2025
1fc87c0
fix(ui5-toolbar): physical elements
Apr 17, 2025
24c9467
chore(ui5-toolbar): toolbar items made physical
Apr 22, 2025
42d412b
chore(ui5-toolbar): items made physical
Apr 22, 2025
f3b5389
chore(ui5-toolbar): toolbar items made physical
Apr 23, 2025
39dfa8d
Merge branch 'main' of github.com:SAP/ui5-webcomponents into toolbar-…
Apr 27, 2025
a920608
chore(ui5-toolbar): items made physical
Apr 27, 2025
5d7a7a5
chore(ui5-toolbar): physical items
Apr 28, 2025
7dac390
fix(ui5-toolbar): some tests rewritten in cy
May 6, 2025
3d930f3
fix(ui5-toolbar): test fix
May 6, 2025
1b0d5d5
Merge branch 'main' of github.com:SAP/ui5-webcomponents
May 6, 2025
132b628
Merge branch 'main' into toolbar-items-physical
May 6, 2025
212286a
fix(ui5-toolbar): fix userSettingsMenu tests
May 7, 2025
1601e48
fix(ui5-toolbar): dynamicPage tests fixed
May 7, 2025
b42894b
fix(ui5-toolbar): styles added for ToolbarSelect
May 8, 2025
ce9f257
fix(ui5-toolbar): private prop for overflow items added, yet not working
May 8, 2025
6122397
fix(ui5-toolbar): overflow API provided
May 9, 2025
46748b7
fix(ui5-toolbar): edited documentation
May 9, 2025
ffaca39
Merge branch 'main' into toolbar-items-physical
PetyaMarkovaBogdanova May 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 13 additions & 39 deletions docs/internal/Toolbar.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
# Creating a web component abstract item to be used inside Toolbar
# Creating a web component toolbar item to be used inside Toolbar

*This section explains how to build abstract items in order to be compatible with UI5 Toolbar.*
*This section explains how to build toolbar items in order to be compatible with UI5 Toolbar.*
*It will guide you through the process of how we created `ui5-toolbar-button`, to be
compatible with `ui5-toolbar`. Currently developed items can be used without those efforts. They are:*
1. ui5-toolbar-button
2. ui5-toolbar-select
3. ui5-toolbar-separator
4. ui5-toolbar-spacer

## Abstract items

### Why are abstract items needed?

When the toolbar renders its slotted items within a popover in the static area, simply relocating the actual DOM nodes within its slots can lead to reference issues, causing the slotted nodes to lose their parent reference (e.g., the toolbar). This is the reason why the toolbar must operate with abstract items. Abstract items are not rendered directly within the DOM; instead, they function as data used by the toolbar to produce corresponding physical web components. On the other hand, useful modifications detected by the toolbar on the physical items are synchronised with the abstract ones. (see step [Events](#events))

The `ui5-toolbar` is a composite web component, that slots different UI5 components, designing them as abstract items. They can contain
## Toolbar Items
The `ui5-toolbar` is a composite web component, that slots different UI5 components, designing them as toolbar items. They can contain
properties, slots and events, and they can match the API of already existing component.
In order to be suitable for usage inside `ui5-toolbar`, each component should adhere to following guidelines:

Expand All @@ -25,17 +20,19 @@ In order to be suitable for usage inside `ui5-toolbar`, each component should ad
ToolbarButton.ts
```

2. The new component needs to implement two template files with name of the following type:
2. The new component needs to implement template file with name of the following type:

```javascript
ToolbarButton.hbs and ToolbarPopoverButton.hbs
ToolbarButton.hbs
```

3. It needs to implement **customElement** decorator, which is good to contain custom tag name:
3. It needs to implement **customElement** decorator, which is good to contain custom tag name, template and renderer:

```javascript
@customElement({
tag: "ui5-toolbar-button"
tag: "ui5-toolbar-button",
template: ToolbarButtonTemplate,
renderer: jsxRenderer,
})
```

Expand All @@ -45,29 +42,7 @@ ToolbarButton.hbs and ToolbarPopoverButton.hbs
class ToolbarButton extends ToolbarItem
```

5. Inside the module there should be two template getters: for toolbar and popover representation.

```javascript
static get toolbarTemplate() {
return ToolbarButtonTemplate;
}

static get toolbarPopoverTemplate() {
return ToolbarPopoverButtonTemplate;
}
```

6. After the class declaration there should be a registry call for the item inside the toolbar. **registerToolbarItem** helper should be added as a dependency.

```javascript
import { registerToolbarItem } from "./ToolbarRegistry.js";
```

```javascript
registerToolbarItem(ToolbarButton);
```

7. In the templates there should be mapping of the properties that need to be used in the component inside Toolbar.
5. In the templates there should be mapping of the properties that need to be used in the component inside Toolbar.

Inside ToolbarButton.ts:

Expand All @@ -91,14 +66,13 @@ Inside ToolbarButtonTemplate.hbs:
  {{this.text}}
</ui5-button>
```
8. The new component's DOM root element needs to have `"ui5-tb-item"` CSS class in order to get default styles for item (margins etc.).
9. The new class needs to be added to the bundle file in the corresponding library.
6. The new class needs to be added to the bundle file in the corresponding library.

Inside bundle.common.js:
```javascript
import ToolbarButton from "./dist/ToolbarButton.js";
```
10. Use your newly created component inside the ui5-toolbar like this:
7. Use your newly created component inside the ui5-toolbar like this:

```html
<ui5-toolbar>
Expand Down
24 changes: 16 additions & 8 deletions packages/fiori/cypress/specs/UserSettingsDialog.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,11 @@ describe("Events", () => {
settings.get(0).addEventListener("before-close", cy.stub().as("beforeClosed"));
});

cy.get("@settings").shadow().find("[ui5-toolbar]").shadow()
.find("[ui5-button]")
cy.get("@settings").shadow().find("[ui5-toolbar]")
.find("[ui5-toolbar-button]")
.first()
.shadow()
.find("[ui5-button]")
.as("closeButton");
cy.get("@closeButton")
.click();
Expand All @@ -320,9 +322,11 @@ describe("Events", () => {
settings.get(0).addEventListener("close", cy.stub().as("closed"));
});

cy.get("@settings").shadow().find("[ui5-toolbar]").shadow()
.find("[ui5-button]")
cy.get("@settings").shadow().find("[ui5-toolbar]")
.find("[ui5-toolbar-button]")
.first()
.shadow()
.find("[ui5-button]")
.as("closeButton");
cy.get("@closeButton")
.click();
Expand Down Expand Up @@ -487,9 +491,11 @@ describe("Responsiveness", () => {
});
cy.get("@settings").shadow().find("[ui5-dialog]").should("exist");
cy.get("@settings").shadow().find("[ui5-toolbar]").should("exist");
cy.get("@settings").shadow().find("[ui5-toolbar]").shadow()
.find("[ui5-button]")
cy.get("@settings").shadow().find("[ui5-toolbar]")
.find("[ui5-toolbar-button]")
.first()
.shadow()
.find("[ui5-button]")
.as("closeButton");
cy.get("@closeButton")
.click();
Expand Down Expand Up @@ -523,9 +529,11 @@ describe("Responsiveness", () => {
.find("[ui5-li]")
.as("item");
cy.get("@item").should("have.attr", "type", "Navigation");
cy.get("@settings").shadow().find("[ui5-toolbar]").shadow()
.find("[ui5-button]")
cy.get("@settings").shadow().find("[ui5-toolbar]")
.find("[ui5-toolbar-button]")
.first()
.shadow()
.find("[ui5-button]")
.as("closeButton");
cy.get("@closeButton")
.click();
Expand Down
2 changes: 1 addition & 1 deletion packages/fiori/test/specs/DynamicPage.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ describe("Page general interaction", () => {

it("allows toggle the footer", async () => {
const footer = await browser.$("#page").shadow$(".ui5-dynamic-page-footer");
const toggleFooterButton = await browser.$("#actionsToolbar").shadow$("#toggleFooterBtn");
const toggleFooterButton = await browser.$("#actionsToolbar").$("#toggleFooterBtn");

assert.ok(await footer.isDisplayedInViewport(), "Footer should be visible.");

Expand Down
175 changes: 174 additions & 1 deletion packages/main/cypress/specs/Toolbar.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,15 @@ describe("Toolbar general interaction", () => {
</Toolbar>
);

// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500);

cy.get("ui5-toolbar-button[text='Button 1']")
.then(button => {
button.get(0).addEventListener("click", cy.stub().as("clicked"));
});

cy.get("ui5-button", { includeShadowDom: true }).contains("Button 1")
cy.get("ui5-button", { includeShadowDom: true }).contains("Button 1")
.click();

cy.get("@clicked")
Expand Down Expand Up @@ -169,6 +172,77 @@ describe("Toolbar general interaction", () => {
cy.get("@closed")
.should("have.been.calledOnce");
});

it("Should move button with alwaysOverflow priority to overflow popover", async () => {

cy.mount(
<Toolbar id="otb_d">
<ToolbarButton text="Add" icon={add} overflow-priority="AlwaysOverflow" stableDomRef="tb-button-add-d"></ToolbarButton>
<ToolbarButton text="Employee" icon={employee} overflow-priority="AlwaysOverflow" stableDomRef="tb-button-employee-d"></ToolbarButton>
</Toolbar>
);

// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500);

const otb = cy.get("#otb_d");

cy.get("otb")
.shadow()
.find(".ui5-tb-overflow-btn")
.click();
const overflowButton = otb.shadow().find(".ui5-tb-overflow-btn");

cy.get("#otb_d")
.shadow()
.find(".ui5-overflow-popover")
.should("have.attr", "open", "true");
overflowButton.click();
cy.wait(500);

cy.get("@popover")
.find(".ui5-tb-popover-item")
.should("have.length", 2);

cy.get("@popover")
.find(`[stable-dom-ref="tb-button-employee-d"]`)
.should("have.class", "ui5-tb-popover-item");
});

it("Should properly prevent the closing of the overflow menu when preventClosing = true", () => {
cy.mount(
<div style="width: 250px;">
<Toolbar id="testEventpreventClosing-toolbar">
<ToolbarButton text="Some longer title text">

</ToolbarButton>
<ToolbarSelect>
<ToolbarSelectOption>
test
</ToolbarSelectOption>
</ToolbarSelect>
</Toolbar>
</div>
)

// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000);

cy.get("#testEventpreventClosing-toolbar")
.shadow()
.find(".ui5-tb-overflow-btn")
.click();
cy.get("[ui5-toolbar-select]")
.shadow()
.find("[ui5-select]")
.click();

cy.get("#testEventpreventClosing-toolbar")
.shadow()
.find(".ui5-overflow-popover")
.should("have.attr", "open", "open");
});

});

describe("Accessibility", () => {
Expand All @@ -185,10 +259,109 @@ describe("Accessibility", () => {
</ToolbarSelect>
</Toolbar>
);
cy.wait(1000);

cy.get("ui5-toolbar")
.shadow()
.find(".ui5-overflow-popover")
.should("have.attr", "accessible-name", "Available Values");
});
});

//ToolbarSelect
describe("Toolbar Select", () => {
it("Should render the select with the correct attributes inside the popover", () => {
cy.mount(
<div style="width: 250px;">
<Toolbar id="otb_e">
<ToolbarSelect value-state="Critical" accessible-name="Add" accessible-name-ref="title" id="toolbar-select">
<ToolbarSelectOption>1</ToolbarSelectOption>
<ToolbarSelectOption selected>2</ToolbarSelectOption>
<ToolbarSelectOption>3</ToolbarSelectOption>
</ToolbarSelect>


<ToolbarSelect disabled class="custom-class">
<ToolbarSelectOption>1</ToolbarSelectOption>
<ToolbarSelectOption selected>2</ToolbarSelectOption>
<ToolbarSelectOption>3</ToolbarSelectOption>
</ToolbarSelect>
</Toolbar>
</div>
);

const otb = cy.get("#otb_e").as("otb");

cy.get("@otb")
.shadow()
.find(".ui5-tb-overflow-btn")
.click();
const overflowButton = otb.shadow().find(".ui5-tb-overflow-btn");

cy.get("@otb")
.shadow()
.find(".ui5-overflow-popover").as("popover")
.should("have.attr", "open", "open");
overflowButton.click();
cy.wait(500);

cy.get("@otb")
.find("#toolbar-select")
.should("have.attr", "value-state", "Critical")

.should("have.attr", "accessible-name", "Add")

.should("have.attr", "accessible-name-ref", "title")

cy.get("@otb")
.find(".custom-class")
.should("have.attr", "disabled", "disabled");

});

//ToolbarButton
it("Should render the button with the correct text inside the popover", async () => {
cy.viewport(200, 1080);

cy.get("#otb_d").within(() => {
cy.get(".ui5-tb-overflow-btn").click();
cy.get("ui5-popover").shadow().within(() => {
cy.get("ui5-toolbar-button").shadow().within(() => {
cy.get("ui5-button").then($button => {
expect($button).to.have.text("Back");
expect($button).to.have.attr("design", "Emphasized");
expect($button).to.have.attr("disabled", "true");
expect($button).to.have.attr("icon", "sap-icon://add");
expect($button).to.have.attr("end-icon", "sap-icon://employee");
expect($button).to.have.attr("tooltip", "Add");
});
});
});
});
});

it ("Should render the button with the correct accessible name inside the popover", async () => {
cy.viewport(100, 1080);

cy.get("#otb_d").within(() => {
cy.get(".ui5-tb-overflow-btn").click();
cy.get("ui5-popover").shadow().within(() => {
cy.get("ui5-button[accessible-name]").then($button => {
expect($button).to.have.attr("accessible-name", "Add");
expect($button).to.have.attr("accessible-name-ref", "btn");
});
});
});
});

it("Should render the button with the correct accessibilityAttributes inside the popover", async () => {
cy.viewport(100, 1080);

cy.get("#otb_d").within(() => {
cy.get(".ui5-tb-overflow-btn").click();
cy.get("ui5-popover").shadow().within(() => {
cy.get("ui5-button[accessible-name]").invoke("prop", "accessibilityAttributes").should("have.property", "expanded", "true");
});
});
});
});
Loading
Loading