From 66b74a09928cb0b1e9b4e6f95101dd0bd6cb4d5e Mon Sep 17 00:00:00 2001 From: Daniel Phillip Nowak Date: Wed, 12 Jun 2024 10:56:56 +0200 Subject: [PATCH 01/46] feat(ui5-grid): adding horizontal column alignment --- packages/main/src/Table.ts | 24 ++++++++++++++--- packages/main/src/TableCellBase.ts | 9 +++++++ packages/main/src/TableHeaderRow.ts | 2 +- packages/main/src/types/TableCellAlign.ts | 33 +++++++++++++++++++++++ packages/main/test/pages/Grid.html | 0 packages/main/test/pages/Table.html | 2 +- 6 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 packages/main/src/types/TableCellAlign.ts create mode 100644 packages/main/test/pages/Grid.html diff --git a/packages/main/src/Table.ts b/packages/main/src/Table.ts index 8a11e692b93b..b1dc0673bec7 100644 --- a/packages/main/src/Table.ts +++ b/packages/main/src/Table.ts @@ -206,7 +206,7 @@ class Table extends UI5Element { * * @public */ - @slot({ type: HTMLElement, invalidateOnChildChange: { properties: false, slots: true } }) + @slot({ type: HTMLElement, invalidateOnChildChange: { properties: true, slots: true } }) headerRow!: Array; /** @@ -344,6 +344,7 @@ class Table extends UI5Element { this.style.setProperty(getScopedVarName("--ui5_grid_sticky_top"), this.stickyTop); this._refreshPopinState(); + this._refreshAlignment(); } onAfterRendering(): void { @@ -425,6 +426,17 @@ class Table extends UI5Element { }); } + _refreshAlignment() { + this.headerRow[0].cells.forEach((header, index) => { + this.rows.forEach(row => { + const cell = row.cells[index]; + if (cell && cell.hAlign !== header.hAlign) { + cell.hAlign = header.hAlign; + } + }); + }); + } + _onGrow() { this._growing?.loadMore(); } @@ -464,11 +476,17 @@ class Table extends UI5Element { } get styles() { - return { + const tableStyle = { table: { "grid-template-columns": this._gridTemplateColumns, }, - }; + } as any; + + this.headerRow[0].cells.forEach(headerCell => { + tableStyle.table[`--h-align-${(headerCell as any)._individualSlot}`] = headerCell.hAlign; + }); + + return tableStyle; } get _gridTemplateColumns() { diff --git a/packages/main/src/TableCellBase.ts b/packages/main/src/TableCellBase.ts index 6abffab2b098..7148615a4d70 100644 --- a/packages/main/src/TableCellBase.ts +++ b/packages/main/src/TableCellBase.ts @@ -6,6 +6,7 @@ import property from "@ui5/webcomponents-base/dist/decorators/property.js"; import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; import { getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js"; import TableCellBaseStyles from "./generated/themes/TableCellBase.css.js"; +import TableCellAlign from "./types/TableCellAlign.js"; /** * @class @@ -31,6 +32,9 @@ abstract class TableCellBase extends UI5Element { @property({ type: Boolean }) _popin = false; + @property() + hAlign?: `${TableCellAlign}` = TableCellAlign.Begin; + protected ariaRole: string = "gridcell"; static i18nBundle: I18nBundle; @@ -48,6 +52,11 @@ abstract class TableCellBase extends UI5Element { } else { this.setAttribute("role", this.ariaRole); } + if (this.hAlign) { + this.style.justifyContent = this.hAlign; + } else { + this.style.justifyContent = `var(--h-align-${(this as any)._individualSlot})`; + } } getFocusDomRef() { diff --git a/packages/main/src/TableHeaderRow.ts b/packages/main/src/TableHeaderRow.ts index 3e2fb9d2edd1..911a52ba15be 100644 --- a/packages/main/src/TableHeaderRow.ts +++ b/packages/main/src/TableHeaderRow.ts @@ -56,7 +56,7 @@ class TableHeaderRow extends TableRowBase { type: HTMLElement, "default": true, invalidateOnChildChange: { - properties: ["width", "_popin"], + properties: ["width", "_popin", "hAlign"], slots: false, }, individualSlots: true, diff --git a/packages/main/src/types/TableCellAlign.ts b/packages/main/src/types/TableCellAlign.ts new file mode 100644 index 000000000000..1d20829ae1b8 --- /dev/null +++ b/packages/main/src/types/TableCellAlign.ts @@ -0,0 +1,33 @@ +/** + * Alignment of the <ui5-table-cell> component. + * + * @public + */ +enum TableCellAlign { + /** + * @public + */ + Left = "left", + + /** + * @public + */ + Begin = "start", + + /** + * @public + */ + Right = "right", + + /** + * @public + */ + End = "end", + + /** + * @public + */ + Center = "center", +} + +export default TableCellAlign; diff --git a/packages/main/test/pages/Grid.html b/packages/main/test/pages/Grid.html new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/main/test/pages/Table.html b/packages/main/test/pages/Table.html index 389b196e0435..ccda303b1a07 100644 --- a/packages/main/test/pages/Table.html +++ b/packages/main/test/pages/Table.html @@ -27,7 +27,7 @@ - Product + Product Supplier Dimensions Weight From a971d037a6da44f7c6e8667af7f53583d9f44dba Mon Sep 17 00:00:00 2001 From: Daniel Phillip Nowak Date: Wed, 12 Jun 2024 10:56:56 +0200 Subject: [PATCH 02/46] feat(ui5-grid): adding horizontal column alignment --- packages/main/src/TableCellBase.ts | 4 ++-- packages/main/test/pages/Table.html | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/main/src/TableCellBase.ts b/packages/main/src/TableCellBase.ts index 7148615a4d70..b52f066932dd 100644 --- a/packages/main/src/TableCellBase.ts +++ b/packages/main/src/TableCellBase.ts @@ -6,7 +6,7 @@ import property from "@ui5/webcomponents-base/dist/decorators/property.js"; import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; import { getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js"; import TableCellBaseStyles from "./generated/themes/TableCellBase.css.js"; -import TableCellAlign from "./types/TableCellAlign.js"; +import type TableCellAlign from "./types/TableCellAlign.js"; /** * @class @@ -33,7 +33,7 @@ abstract class TableCellBase extends UI5Element { _popin = false; @property() - hAlign?: `${TableCellAlign}` = TableCellAlign.Begin; + hAlign?: `${TableCellAlign}`; protected ariaRole: string = "gridcell"; diff --git a/packages/main/test/pages/Table.html b/packages/main/test/pages/Table.html index ccda303b1a07..f73016ffcde1 100644 --- a/packages/main/test/pages/Table.html +++ b/packages/main/test/pages/Table.html @@ -28,8 +28,8 @@ Product - Supplier - Dimensions + Supplier + Dimensions Weight Price From 2a4c627c8420b7ed9a42d783d5b727a24ad2107c Mon Sep 17 00:00:00 2001 From: Daniel Phillip Nowak Date: Wed, 12 Jun 2024 10:56:56 +0200 Subject: [PATCH 03/46] feat(ui5-grid): adding horizontal column alignment --- packages/main/src/Table.ts | 12 ------------ packages/main/test/pages/Grid.html | 0 2 files changed, 12 deletions(-) delete mode 100644 packages/main/test/pages/Grid.html diff --git a/packages/main/src/Table.ts b/packages/main/src/Table.ts index b1dc0673bec7..05a5ced02e7c 100644 --- a/packages/main/src/Table.ts +++ b/packages/main/src/Table.ts @@ -344,7 +344,6 @@ class Table extends UI5Element { this.style.setProperty(getScopedVarName("--ui5_grid_sticky_top"), this.stickyTop); this._refreshPopinState(); - this._refreshAlignment(); } onAfterRendering(): void { @@ -426,17 +425,6 @@ class Table extends UI5Element { }); } - _refreshAlignment() { - this.headerRow[0].cells.forEach((header, index) => { - this.rows.forEach(row => { - const cell = row.cells[index]; - if (cell && cell.hAlign !== header.hAlign) { - cell.hAlign = header.hAlign; - } - }); - }); - } - _onGrow() { this._growing?.loadMore(); } diff --git a/packages/main/test/pages/Grid.html b/packages/main/test/pages/Grid.html deleted file mode 100644 index e69de29bb2d1..000000000000 From 55fac392dfad080290ebbe1b2c682a5ee8a5bade Mon Sep 17 00:00:00 2001 From: Daniel Phillip Nowak Date: Wed, 12 Jun 2024 10:56:56 +0200 Subject: [PATCH 04/46] feat(ui5-grid): adding horizontal column alignment --- packages/main/src/Table.ts | 5 +- packages/main/src/TableCellBase.ts | 10 +- packages/main/src/TableHeaderCell.ts | 6 + ...llAlign.ts => TableCellHorizontalAlign.ts} | 14 +- packages/main/test/pages/HAlignTable.html | 50 +++++ packages/main/test/pages/Table.html | 6 +- packages/main/test/specs/Table.spec.js | 196 ++++++++++++++++++ 7 files changed, 273 insertions(+), 14 deletions(-) rename packages/main/src/types/{TableCellAlign.ts => TableCellHorizontalAlign.ts} (57%) create mode 100644 packages/main/test/pages/HAlignTable.html diff --git a/packages/main/src/Table.ts b/packages/main/src/Table.ts index 05a5ced02e7c..dc5badb1dbe2 100644 --- a/packages/main/src/Table.ts +++ b/packages/main/src/Table.ts @@ -25,6 +25,7 @@ import { import BusyIndicator from "./BusyIndicator.js"; import TableCell from "./TableCell.js"; import { findVerticalScrollContainer, scrollElementIntoView, isFeature } from "./TableUtils.js"; +import TableCellHorizontalAlign from "./types/TableCellHorizontalAlign.js"; /** * Interface for components that can be slotted inside the features slot of the ui5-table. @@ -206,7 +207,7 @@ class Table extends UI5Element { * * @public */ - @slot({ type: HTMLElement, invalidateOnChildChange: { properties: true, slots: true } }) + @slot({ type: HTMLElement, invalidateOnChildChange: { properties: false, slots: true } }) headerRow!: Array; /** @@ -471,7 +472,7 @@ class Table extends UI5Element { } as any; this.headerRow[0].cells.forEach(headerCell => { - tableStyle.table[`--h-align-${(headerCell as any)._individualSlot}`] = headerCell.hAlign; + tableStyle.table[`--h-align-${(headerCell as any)._individualSlot}`] = headerCell.hAlign || TableCellHorizontalAlign.Left; }); return tableStyle; diff --git a/packages/main/src/TableCellBase.ts b/packages/main/src/TableCellBase.ts index b52f066932dd..82798484ec2b 100644 --- a/packages/main/src/TableCellBase.ts +++ b/packages/main/src/TableCellBase.ts @@ -6,7 +6,7 @@ import property from "@ui5/webcomponents-base/dist/decorators/property.js"; import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; import { getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js"; import TableCellBaseStyles from "./generated/themes/TableCellBase.css.js"; -import type TableCellAlign from "./types/TableCellAlign.js"; +import type TableCellHorizontalAlign from "./types/TableCellHorizontalAlign.js"; /** * @class @@ -32,8 +32,14 @@ abstract class TableCellBase extends UI5Element { @property({ type: Boolean }) _popin = false; + /** + * Determines the horizontal alignment of table cells. + * Note: All values valid for justify-content can be used not just the ones inside the enum. + * @default "Left" + * @public + */ @property() - hAlign?: `${TableCellAlign}`; + hAlign?: `${TableCellHorizontalAlign}`; protected ariaRole: string = "gridcell"; diff --git a/packages/main/src/TableHeaderCell.ts b/packages/main/src/TableHeaderCell.ts index bd9e89328fc8..74d5c3a04af5 100644 --- a/packages/main/src/TableHeaderCell.ts +++ b/packages/main/src/TableHeaderCell.ts @@ -85,6 +85,12 @@ class TableHeaderCell extends TableCellBase { this.style.maxWidth = this.maxWidth; this.style.width = this.width; } + + onBeforeRendering() { + super.onBeforeRendering(); + // overwrite setting of TableCellBase so that the TableHeaderCell always uses the slot variable + this.style.justifyContent = `var(--h-align-${(this as any)._individualSlot})`; + } } TableHeaderCell.define(); diff --git a/packages/main/src/types/TableCellAlign.ts b/packages/main/src/types/TableCellHorizontalAlign.ts similarity index 57% rename from packages/main/src/types/TableCellAlign.ts rename to packages/main/src/types/TableCellHorizontalAlign.ts index 1d20829ae1b8..03e65cdadea9 100644 --- a/packages/main/src/types/TableCellAlign.ts +++ b/packages/main/src/types/TableCellHorizontalAlign.ts @@ -3,31 +3,31 @@ * * @public */ -enum TableCellAlign { +enum TableCellHorizontalAlign { /** * @public */ - Left = "left", + Left = "Left", /** * @public */ - Begin = "start", + Begin = "Start", /** * @public */ - Right = "right", + Right = "Right", /** * @public */ - End = "end", + End = "End", /** * @public */ - Center = "center", + Center = "Center", } -export default TableCellAlign; +export default TableCellHorizontalAlign; diff --git a/packages/main/test/pages/HAlignTable.html b/packages/main/test/pages/HAlignTable.html new file mode 100644 index 000000000000..fa73aad2ee32 --- /dev/null +++ b/packages/main/test/pages/HAlignTable.html @@ -0,0 +1,50 @@ + + + + + + + + + Table (horizontal alignment) + + + +
+ + + + Product + Supplier + Dimensions + Weight + Price + + + Notebook Basic 15
HT-1000
+ Very Best Screens + 30 x 18 x 3 cm + 4.2 KG + 956 EUR +
+ + Notebook Basic 17
HT-1001
+ Smartcards + 29 x 17 x 3.1 cm + 4.5 KG + 1249 EUR +
+ + Notebook Basic 18
HT-1002
+ Technocom + 32 x 21 x 4 cm + 3.7 KG + 29 EUR +
+
+ +
+ + + + diff --git a/packages/main/test/pages/Table.html b/packages/main/test/pages/Table.html index f73016ffcde1..389b196e0435 100644 --- a/packages/main/test/pages/Table.html +++ b/packages/main/test/pages/Table.html @@ -27,9 +27,9 @@ - Product - Supplier - Dimensions + Product + Supplier + Dimensions Weight Price diff --git a/packages/main/test/specs/Table.spec.js b/packages/main/test/specs/Table.spec.js index 8cc64483b03c..d3db9599f77a 100644 --- a/packages/main/test/specs/Table.spec.js +++ b/packages/main/test/specs/Table.spec.js @@ -133,6 +133,202 @@ describe("Table - Popin Mode", async () => { }); }); +describe("Table - Horizontal alignment of cells", async () => { + beforeEach(async () => { + await browser.url(`test/pages/HAlignTable.html`); + }); + + it("default alignment when hAlign is not set", async () => { + let table = await browser.$("#table"); + assert.ok(table.isExisting(), "Table exists"); + + const headerRow = await table.$("ui5-table-header-row"); + const headerCells = await headerRow.$$("ui5-table-header-cell"); + assert.equal(headerCells.length, 5, "5 columns exist"); + + const index = 0; + const headerCell = headerCells[index]; + const alignment = "left"; + assert.equal(await headerCell.getAttribute("id"), "productCol", "Correct cell"); + assert.equal(await headerCell.getAttribute("h-Align"), undefined, "hAlign not set"); + + const justifyContentHeaderCell = await headerCell.getCSSProperty("justify-content"); + assert.equal(justifyContentHeaderCell.value, alignment, "justify-content correctly set."); + + const tableRows = await table.$$("ui5-table-row"); + for (const row of tableRows) { + const rowCells = await row.$$("ui5-table-cell"); + const justifyContent = await rowCells[3].getCSSProperty("justify-content"); + + assert.equal(justifyContent.value, alignment, "justify-content correctly set."); + } + }); + + it("cells have same alignment as their headerCell", async () => { + let table = await browser.$("#table"); + assert.ok(table.isExisting(), "Table exists"); + + const headerRow = await table.$("ui5-table-header-row"); + const headerCells = await headerRow.$$("ui5-table-header-cell"); + assert.equal(headerCells.length, 5, "5 columns exist"); + + const cellIndex = 1; // second row is supplier row + const headerCell = headerCells[cellIndex]; + const id = await headerCell.getAttribute("id"); + const hAlign = await headerCell.getAttribute("h-Align"); + assert.equal(id, "supplierCol", "Correct cell"); + assert.ok(hAlign, "hAlign set"); + + const alignment = `center`; // alignment set to center in table example + const justifyContentHeaderCell = await headerCell.getCSSProperty("justify-content"); + assert.equal(justifyContentHeaderCell.value, alignment, "justify-content correctly set."); + + const tableRows = await table.$$("ui5-table-row"); + for (const row of tableRows) { + const rowCells = await row.$$("ui5-table-cell"); + const justifyContent = await rowCells[cellIndex].getCSSProperty("justify-content"); + + assert.equal(justifyContent.value, alignment, "justify-content correctly set."); + } + }); + + it("on changing the h-alignment of the headerCell, the h-alignment of subsequent cells must change as well", async () => { + let table = await browser.$("#table"); + assert.ok(table.isExisting(), "Table exists"); + + const headerRow = await table.$("ui5-table-header-row"); + const headerCells = await headerRow.$$("ui5-table-header-cell"); + assert.equal(headerCells.length, 5, "5 columns exist"); + + // test setup + const cellIndex = 1; // second row is supplier row + const headerCell = headerCells[cellIndex]; + const id = await headerCell.getAttribute("id"); + let hAlign = await headerCell.getAttribute("h-Align"); + assert.equal(id, "supplierCol", "Correct cell"); + assert.ok(hAlign, "hAlign set"); + + let alignment = `center`; // alignment set to center in table example + let justifyContentHeaderCell = await headerCell.getCSSProperty("justify-content"); + assert.equal(justifyContentHeaderCell.value, alignment, "justify-content correctly set."); + + // update the values + alignment = "left"; + await headerCell.setAttribute("h-Align", alignment); + hAlign = await headerCell.getAttribute("h-Align"); + justifyContentHeaderCell = await headerCell.getCSSProperty("justify-content"); + + assert.equal(id, "supplierCol", "Correct cell"); + assert.ok(hAlign, "hAlign set"); + assert.equal(justifyContentHeaderCell.value, alignment, "justify-content value updated."); + + const tableRows = await table.$$("ui5-table-row"); + for (const row of tableRows) { + const rowCells = await row.$$("ui5-table-cell"); + const justifyContent = await rowCells[cellIndex].getCSSProperty("justify-content"); + + assert.equal(justifyContent.value, alignment, "justify-content correctly set."); + } + }); + + it("on changing the h-alignment of a cell, the h-alignment of other cells must not change", async () => { + let table = await browser.$("#table"); + assert.ok(table.isExisting(), "Table exists"); + + const headerRow = await table.$("ui5-table-header-row"); + const headerCells = await headerRow.$$("ui5-table-header-cell"); + assert.equal(headerCells.length, 5, "5 columns exist"); + + const cellIndex = 1; // second row is supplier row + const headerCell = headerCells[cellIndex]; + const id = await headerCell.getAttribute("id"); + const hAlign = await headerCell.getAttribute("h-Align"); + assert.equal(id, "supplierCol", "Correct cell"); + assert.ok(hAlign, "hAlign set"); + + const alignment = `center`; // alignment set to center in table example + const justifyContentHeaderCell = await headerCell.getCSSProperty("justify-content"); + assert.equal(justifyContentHeaderCell.value, alignment, "justify-content value of the headerCell is correct."); + + const tableRows = await table.$$("ui5-table-row"); + const rowWithChangedCell = 0; + const cell = await tableRows[rowWithChangedCell].$$("ui5-table-cell")[cellIndex]; + let hAlignCell = await cell.getAttribute("h-Align"); + assert.equal(hAlignCell, null, "hAlign property of the cell is not set."); + + let justifyContentCell = await cell.getCSSProperty("justify-content"); + assert.equal(justifyContentCell.value, alignment, "justify-content of the cell matches the headerCell"); + + const customAlignmentCell = "left"; // alignment used for a single cell + await cell.setAttribute("h-Align", customAlignmentCell); + hAlignCell = await cell.getAttribute("h-Align"); + assert.notEqual(hAlignCell, null, "hAlign property of the cell is set now."); + + justifyContentCell = await cell.getCSSProperty("justify-content"); + assert.equal(justifyContentCell.value, customAlignmentCell, "justify-content was changed and now matches the custom cell alignment value."); + + for (const [index, row] of tableRows.entries()) { + const rowCells = await row.$$("ui5-table-cell"); + const justifyContent = await rowCells[cellIndex].getCSSProperty("justify-content"); + + // the alignment of every cell but the changed one still has to match the headerCell alignment + assert.equal(justifyContent.value, index === rowWithChangedCell ? customAlignmentCell : alignment, "justify-content correctly set."); + } + }); + + it("the h-alignment of a cell differs from the others, on changing the h-alignment of the headerCell, the h-alignment of other cells must change as well except of this one custom aligned cell", async () => { + let table = await browser.$("#table"); + assert.ok(table.isExisting(), "Table exists"); + + const headerRow = await table.$("ui5-table-header-row"); + const headerCells = await headerRow.$$("ui5-table-header-cell"); + assert.equal(headerCells.length, 5, "5 columns exist"); + + const cellIndex = 1; // second row is supplier row + const headerCell = headerCells[cellIndex]; + const id = await headerCell.getAttribute("id"); + const hAlign = await headerCell.getAttribute("h-Align"); + assert.equal(id, "supplierCol", "Correct cell"); + assert.ok(hAlign, "hAlign set"); + + let alignment = "center"; // alignment set to center in table example + let justifyContent = await headerCell.getCSSProperty("justify-content"); + assert.equal(justifyContent.value, alignment, "justify-content correctly set."); + + const tableRows = await table.$$("ui5-table-row"); + const rowWithChangedCell = 0; + const cell = await tableRows[rowWithChangedCell].$$("ui5-table-cell")[cellIndex]; + let justifyContentCell = await cell.getCSSProperty("justify-content"); + let hAlignCell = await cell.getAttribute("h-Align"); + assert.equal(hAlignCell, null, "hAlign property of the cell is not set."); + assert.equal(justifyContentCell.value, alignment, "justify-content value matches headerCell alignment"); + + const customAlignmentCell = "left"; // alignment used for a single cell + await cell.setAttribute("h-Align", customAlignmentCell); + hAlignCell = await cell.getAttribute("h-Align"); + + assert.notEqual(hAlignCell, null, "hAlign property of the cell is set now."); + + justifyContentCell = await cell.getCSSProperty("justify-content"); + assert.equal(justifyContentCell.value, customAlignmentCell, "justify-content of cell adjusted correctly."); + + alignment = "right" + await headerCell.setAttribute("h-Align", alignment); + hAlignCell = await cell.getAttribute("h-Align"); + justifyContent = await headerCell.getCSSProperty("justify-content"); + + assert.ok(hAlign, "hAlign set"); + assert.equal(justifyContent.value, alignment, "justify-content of headerCell changed correctly."); + + for (const [index, row] of tableRows.entries()) { + const rowCells = await row.$$("ui5-table-cell"); + const justifyContent = await rowCells[cellIndex].getCSSProperty("justify-content"); + + assert.equal(justifyContent.value, index === rowWithChangedCell ? customAlignmentCell : alignment, "justify-content correctly set."); + } + }); +}); + // Tests for the fixed header, whether it is shown correctly and behaves as expected describe("Table - Fixed Header", async () => { before(async () => { From d5e3840b5b615d96e502112c5f111c50c20433e9 Mon Sep 17 00:00:00 2001 From: Daniel Phillip Nowak Date: Wed, 12 Jun 2024 10:56:56 +0200 Subject: [PATCH 05/46] feat(ui5-grid): adding horizontal column alignment --- packages/main/src/TableCellBase.ts | 1 - packages/main/src/types/TableCellHorizontalAlign.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/main/src/TableCellBase.ts b/packages/main/src/TableCellBase.ts index 82798484ec2b..62be2919124f 100644 --- a/packages/main/src/TableCellBase.ts +++ b/packages/main/src/TableCellBase.ts @@ -35,7 +35,6 @@ abstract class TableCellBase extends UI5Element { /** * Determines the horizontal alignment of table cells. * Note: All values valid for justify-content can be used not just the ones inside the enum. - * @default "Left" * @public */ @property() diff --git a/packages/main/src/types/TableCellHorizontalAlign.ts b/packages/main/src/types/TableCellHorizontalAlign.ts index 03e65cdadea9..06b39225fd37 100644 --- a/packages/main/src/types/TableCellHorizontalAlign.ts +++ b/packages/main/src/types/TableCellHorizontalAlign.ts @@ -12,7 +12,7 @@ enum TableCellHorizontalAlign { /** * @public */ - Begin = "Start", + Start = "Start", /** * @public From 9d0f8362822ea1a834717884f0651e961420ec3e Mon Sep 17 00:00:00 2001 From: Daniel Phillip Nowak Date: Wed, 12 Jun 2024 10:56:56 +0200 Subject: [PATCH 06/46] feat(ui5-grid): adding horizontal column alignment --- packages/main/test/pages/HAlignTable.html | 4 -- .../main/Table/TableCell.mdx | 16 ++++++ .../main/Table/TableHeaderCell.mdx | 17 ++++++- .../docs/_samples/main/Table/HAlign/HAlign.md | 4 ++ .../docs/_samples/main/Table/HAlign/main.js | 4 ++ .../_samples/main/Table/HAlign/sample.html | 50 +++++++++++++++++++ 6 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 packages/website/docs/_samples/main/Table/HAlign/HAlign.md create mode 100644 packages/website/docs/_samples/main/Table/HAlign/main.js create mode 100644 packages/website/docs/_samples/main/Table/HAlign/sample.html diff --git a/packages/main/test/pages/HAlignTable.html b/packages/main/test/pages/HAlignTable.html index fa73aad2ee32..c77ab5fa176c 100644 --- a/packages/main/test/pages/HAlignTable.html +++ b/packages/main/test/pages/HAlignTable.html @@ -1,4 +1,3 @@ - @@ -11,7 +10,6 @@
- Product @@ -42,9 +40,7 @@ 29 EUR -
- diff --git a/packages/website/docs/_components_pages/main/Table/TableCell.mdx b/packages/website/docs/_components_pages/main/Table/TableCell.mdx index 18de9948c7b7..72fc8e9c548f 100644 --- a/packages/website/docs/_components_pages/main/Table/TableCell.mdx +++ b/packages/website/docs/_components_pages/main/Table/TableCell.mdx @@ -3,6 +3,22 @@ slug: ../../TableCell sidebar_class_name: newComponentBadge expComponentBadge --- +import HAlign from "../../../_samples/main/Table/HAlign/HAlign.md"; + <%COMPONENT_OVERVIEW%> <%COMPONENT_METADATA%> + +### Horizontal cell alignment + +Control the horizontal alignment of cells by utilizing the `hAlign` property. + +Please note that the behaviour of `hAlign` depends on where you set it: +1. `hAlign` is set on `TableHeaderCell` level
+Changes the horizontal alignment of the `TableHeaderCell` and their corresponding `TableCells`. +2. `hAlign` is set on `TableCell` level only
+The horizontal alignment is only changed for this one `TableCell` +3. `hAlign` is set on `TableHeaderCell` and `TableCell` level
+Changing the `hAlign` property on `TableHeaderCell` level changes only the `TableHeaderCell` itself and those `TableCells` without a `hAlign` property. + + \ No newline at end of file diff --git a/packages/website/docs/_components_pages/main/Table/TableHeaderCell.mdx b/packages/website/docs/_components_pages/main/Table/TableHeaderCell.mdx index c91da75d5a9f..786e6b63e8fa 100644 --- a/packages/website/docs/_components_pages/main/Table/TableHeaderCell.mdx +++ b/packages/website/docs/_components_pages/main/Table/TableHeaderCell.mdx @@ -5,6 +5,7 @@ sidebar_class_name: newComponentBadge expComponentBadge import Popin from "../../../_samples/main/Table/Popin/Popin.md"; import ColumnWidths from "../../../_samples/main/Table/ColumnWidths/ColumnWidths.md"; +import HAlign from "../../../_samples/main/Table/HAlign/HAlign.md"; <%COMPONENT_OVERVIEW%> @@ -16,4 +17,18 @@ import ColumnWidths from "../../../_samples/main/Table/ColumnWidths/ColumnWidths ## Popin Configuration - \ No newline at end of file + + +### Horizontal cell alignment + +Control the horizontal alignment of cells by utilizing the `hAlign` property. + +Please note that the behaviour of `hAlign` depends on where you set it: +1. `hAlign` is set on `TableHeaderCell` level
+Changes the horizontal alignment of the `TableHeaderCell` and their corresponding `TableCells`. +2. `hAlign` is set on `TableCell` level only
+The horizontal alignment is only changed for this one `TableCell` +3. `hAlign` is set on `TableHeaderCell` and `TableCell` level
+Changing the `hAlign` property on `TableHeaderCell` level changes only the `TableHeaderCell` itself and those `TableCells` without a `hAlign` property. + + \ No newline at end of file diff --git a/packages/website/docs/_samples/main/Table/HAlign/HAlign.md b/packages/website/docs/_samples/main/Table/HAlign/HAlign.md new file mode 100644 index 000000000000..17798ecc59ab --- /dev/null +++ b/packages/website/docs/_samples/main/Table/HAlign/HAlign.md @@ -0,0 +1,4 @@ +import html from '!!raw-loader!./sample.html'; +import js from '!!raw-loader!./main.js'; + + diff --git a/packages/website/docs/_samples/main/Table/HAlign/main.js b/packages/website/docs/_samples/main/Table/HAlign/main.js new file mode 100644 index 000000000000..5eef1b6cc847 --- /dev/null +++ b/packages/website/docs/_samples/main/Table/HAlign/main.js @@ -0,0 +1,4 @@ +import "@ui5/webcomponents/dist/Table.js"; +import "@ui5/webcomponents/dist/TableHeaderRow.js"; +import "@ui5/webcomponents/dist/TableHeaderCell.js"; +import "@ui5/webcomponents/dist/Label.js"; \ No newline at end of file diff --git a/packages/website/docs/_samples/main/Table/HAlign/sample.html b/packages/website/docs/_samples/main/Table/HAlign/sample.html new file mode 100644 index 000000000000..970abae533c5 --- /dev/null +++ b/packages/website/docs/_samples/main/Table/HAlign/sample.html @@ -0,0 +1,50 @@ + + + + + + + + Sample + + + +
+ + + + Product + Supplier + Dimensions + Weight + Price + + + Notebook Basic 15
HT-1000
+ Very Best Screens + 30 x 18 x 3 cm + 4.2 KG + 956 EUR +
+ + Notebook Basic 17
HT-1001
+ Smartcards + 29 x 17 x 3.1 cm + 4.5 KG + 1249 EUR +
+ + Notebook Basic 18
HT-1002
+ Technocom + 32 x 21 x 4 cm + 3.7 KG + 29 EUR +
+
+ +
+ + + + + From dc7c7d3b904f04033076d83397508e56665168ce Mon Sep 17 00:00:00 2001 From: Daniel Phillip Nowak Date: Wed, 12 Jun 2024 10:56:56 +0200 Subject: [PATCH 07/46] feat(ui5-grid): adding horizontal column alignment --- packages/main/src/Table.ts | 20 ++++----- packages/main/src/TableCell.ts | 10 +++++ packages/main/src/TableCellBase.ts | 7 +--- packages/main/src/TableHeaderCell.ts | 1 + packages/main/test/specs/Table.spec.js | 56 +++++++++++++++++++++++--- 5 files changed, 74 insertions(+), 20 deletions(-) diff --git a/packages/main/src/Table.ts b/packages/main/src/Table.ts index dc5badb1dbe2..2365f86aaf23 100644 --- a/packages/main/src/Table.ts +++ b/packages/main/src/Table.ts @@ -465,17 +465,19 @@ class Table extends UI5Element { } get styles() { - const tableStyle = { + const headerStyleMap = this.headerRow[0].cells.reduce((headerStyles, headerCell) => { + if (headerCell.hAlign !== undefined && Object.values(TableCellHorizontalAlign).includes(headerCell.hAlign as TableCellHorizontalAlign)) { + headerStyles[`--h-align-${headerCell._individualSlot}`] = headerCell.hAlign; + } + return headerStyles; + }, {} as { [key: string]: string }); + + return { table: { - "grid-template-columns": this._gridTemplateColumns, + "grid-template-columns": this._gridTemplateColumns, + ...headerStyleMap, }, - } as any; - - this.headerRow[0].cells.forEach(headerCell => { - tableStyle.table[`--h-align-${(headerCell as any)._individualSlot}`] = headerCell.hAlign || TableCellHorizontalAlign.Left; - }); - - return tableStyle; + }; } get _gridTemplateColumns() { diff --git a/packages/main/src/TableCell.ts b/packages/main/src/TableCell.ts index f749d1ff591b..0af702e9311b 100644 --- a/packages/main/src/TableCell.ts +++ b/packages/main/src/TableCell.ts @@ -5,6 +5,7 @@ import TableCellBase from "./TableCellBase.js"; import type TableRow from "./TableRow.js"; import type Table from "./Table.js"; import { LABEL_COLON } from "./generated/i18n/i18n-defaults.js"; +import TableCellHorizontalAlign from "./types/TableCellHorizontalAlign.js"; /** * @class @@ -30,6 +31,15 @@ import { LABEL_COLON } from "./generated/i18n/i18n-defaults.js"; template: TableCellTemplate, }) class TableCell extends TableCellBase { + onBeforeRendering() { + super.onBeforeRendering(); + if (this.hAlign && Object.values(TableCellHorizontalAlign).includes(this.hAlign as TableCellHorizontalAlign)) { + this.style.justifyContent = this.hAlign; + } else { + this.style.justifyContent = `var(--h-align-${(this as any)._individualSlot})`; + } + } + get _popinHeader() { const row = this.parentElement as TableRow; const table = row.parentElement as Table; diff --git a/packages/main/src/TableCellBase.ts b/packages/main/src/TableCellBase.ts index 62be2919124f..63f866b0c3ef 100644 --- a/packages/main/src/TableCellBase.ts +++ b/packages/main/src/TableCellBase.ts @@ -34,7 +34,7 @@ abstract class TableCellBase extends UI5Element { /** * Determines the horizontal alignment of table cells. - * Note: All values valid for justify-content can be used not just the ones inside the enum. + * @default "Start" * @public */ @property() @@ -57,11 +57,6 @@ abstract class TableCellBase extends UI5Element { } else { this.setAttribute("role", this.ariaRole); } - if (this.hAlign) { - this.style.justifyContent = this.hAlign; - } else { - this.style.justifyContent = `var(--h-align-${(this as any)._individualSlot})`; - } } getFocusDomRef() { diff --git a/packages/main/src/TableHeaderCell.ts b/packages/main/src/TableHeaderCell.ts index 74d5c3a04af5..e77b9bcbb1ac 100644 --- a/packages/main/src/TableHeaderCell.ts +++ b/packages/main/src/TableHeaderCell.ts @@ -78,6 +78,7 @@ class TableHeaderCell extends TableCellBase { protected ariaRole: string = "columnheader"; _popinWidth: number = 0; + _individualSlot?: string; onEnterDOM() { super.onEnterDOM(); diff --git a/packages/main/test/specs/Table.spec.js b/packages/main/test/specs/Table.spec.js index d3db9599f77a..1998ae2ab584 100644 --- a/packages/main/test/specs/Table.spec.js +++ b/packages/main/test/specs/Table.spec.js @@ -148,12 +148,58 @@ describe("Table - Horizontal alignment of cells", async () => { const index = 0; const headerCell = headerCells[index]; - const alignment = "left"; + const alignment = "normal"; assert.equal(await headerCell.getAttribute("id"), "productCol", "Correct cell"); assert.equal(await headerCell.getAttribute("h-Align"), undefined, "hAlign not set"); const justifyContentHeaderCell = await headerCell.getCSSProperty("justify-content"); + const style = await headerCell.getAttribute("style"); + const justifyContentHeaderCellUncomputed = style.match(/justify-content: ([^;]+)/)[1]; + const cssVariable = "var(--h-align-default-1)"; assert.equal(justifyContentHeaderCell.value, alignment, "justify-content correctly set."); + assert.equal(justifyContentHeaderCellUncomputed, cssVariable, "hAlign not set"); + + const tableRows = await table.$$("ui5-table-row"); + for (const row of tableRows) { + const rowCells = await row.$$("ui5-table-cell"); + const justifyContent = await rowCells[3].getCSSProperty("justify-content"); + + assert.equal(justifyContent.value, alignment, "justify-content correctly set."); + } + }); + + it("horizontal alignment if hAlign is set to a value not defined in TableCellHorizontalAlign", async () => { + let table = await browser.$("#table"); + assert.ok(table.isExisting(), "Table exists"); + + const headerRow = await table.$("ui5-table-header-row"); + const headerCells = await headerRow.$$("ui5-table-header-cell"); + assert.equal(headerCells.length, 5, "5 columns exist"); + + const index = 0; + const headerCell = headerCells[index]; + assert.equal(await headerCell.getAttribute("id"), "productCol", "Correct cell"); + + let alignment = "right"; + await headerCell.setAttribute("h-Align", "Right"); + + let justifyContentHeaderCell = await headerCell.getCSSProperty("justify-content"); + let style = await headerCell.getAttribute("style"); + let justifyContentHeaderCellUncomputed = style.match(/justify-content: ([^;]+)/)[1]; + const cssVariable = "var(--h-align-default-1)"; + assert.equal(justifyContentHeaderCell.value, alignment, "justify-content correctly set."); + assert.equal(justifyContentHeaderCellUncomputed, cssVariable, "hAlign set to css variable"); + + alignment = "normal"; + let hAlign = "valueNotDefinedInEnum"; + await headerCell.setAttribute("h-Align", hAlign); + + assert.equal(await headerCell.getAttribute("h-Align"), hAlign, "hAlign correctly set"); + justifyContentHeaderCell = await headerCell.getCSSProperty("justify-content"); + assert.equal(justifyContentHeaderCell.value, alignment, "justify-content correctly set."); + style = await headerCell.getAttribute("style"); + justifyContentHeaderCellUncomputed = style.match(/justify-content: ([^;]+)/)[1]; + assert.equal(justifyContentHeaderCellUncomputed, cssVariable, "hAlign set to css variable"); const tableRows = await table.$$("ui5-table-row"); for (const row of tableRows) { @@ -214,7 +260,7 @@ describe("Table - Horizontal alignment of cells", async () => { // update the values alignment = "left"; - await headerCell.setAttribute("h-Align", alignment); + await headerCell.setAttribute("h-Align", "Left"); hAlign = await headerCell.getAttribute("h-Align"); justifyContentHeaderCell = await headerCell.getCSSProperty("justify-content"); @@ -260,7 +306,7 @@ describe("Table - Horizontal alignment of cells", async () => { assert.equal(justifyContentCell.value, alignment, "justify-content of the cell matches the headerCell"); const customAlignmentCell = "left"; // alignment used for a single cell - await cell.setAttribute("h-Align", customAlignmentCell); + await cell.setAttribute("h-Align", "Left"); hAlignCell = await cell.getAttribute("h-Align"); assert.notEqual(hAlignCell, null, "hAlign property of the cell is set now."); @@ -304,7 +350,7 @@ describe("Table - Horizontal alignment of cells", async () => { assert.equal(justifyContentCell.value, alignment, "justify-content value matches headerCell alignment"); const customAlignmentCell = "left"; // alignment used for a single cell - await cell.setAttribute("h-Align", customAlignmentCell); + await cell.setAttribute("h-Align", "Left"); hAlignCell = await cell.getAttribute("h-Align"); assert.notEqual(hAlignCell, null, "hAlign property of the cell is set now."); @@ -313,7 +359,7 @@ describe("Table - Horizontal alignment of cells", async () => { assert.equal(justifyContentCell.value, customAlignmentCell, "justify-content of cell adjusted correctly."); alignment = "right" - await headerCell.setAttribute("h-Align", alignment); + await headerCell.setAttribute("h-Align", "Right"); hAlignCell = await cell.getAttribute("h-Align"); justifyContent = await headerCell.getCSSProperty("justify-content"); From a4fdc16e42e3710878156145252c0a09c46f8b6e Mon Sep 17 00:00:00 2001 From: Daniel Phillip Nowak Date: Wed, 12 Jun 2024 10:56:56 +0200 Subject: [PATCH 08/46] feat(ui5-grid): adding horizontal column alignment --- packages/main/src/Table.ts | 4 +- packages/main/src/TableCell.ts | 6 +- packages/main/src/TableCellBase.ts | 2 +- packages/main/src/TableHeaderCell.ts | 2 +- packages/main/src/TableHeaderRow.ts | 2 +- packages/main/test/pages/HAlignTable.html | 4 +- packages/main/test/specs/Table.spec.js | 76 +++++++++---------- .../main/Table/TableCell.mdx | 12 +-- .../main/Table/TableHeaderCell.mdx | 12 +-- .../_samples/main/Table/HAlign/sample.html | 6 +- 10 files changed, 63 insertions(+), 63 deletions(-) diff --git a/packages/main/src/Table.ts b/packages/main/src/Table.ts index 2365f86aaf23..7642a016e039 100644 --- a/packages/main/src/Table.ts +++ b/packages/main/src/Table.ts @@ -466,8 +466,8 @@ class Table extends UI5Element { get styles() { const headerStyleMap = this.headerRow[0].cells.reduce((headerStyles, headerCell) => { - if (headerCell.hAlign !== undefined && Object.values(TableCellHorizontalAlign).includes(headerCell.hAlign as TableCellHorizontalAlign)) { - headerStyles[`--h-align-${headerCell._individualSlot}`] = headerCell.hAlign; + if (headerCell.horizontalAlign !== undefined && Object.values(TableCellHorizontalAlign).includes(headerCell.horizontalAlign as TableCellHorizontalAlign)) { + headerStyles[`--horizontal-align-${headerCell._individualSlot}`] = headerCell.horizontalAlign; } return headerStyles; }, {} as { [key: string]: string }); diff --git a/packages/main/src/TableCell.ts b/packages/main/src/TableCell.ts index 0af702e9311b..0c9f02d98748 100644 --- a/packages/main/src/TableCell.ts +++ b/packages/main/src/TableCell.ts @@ -33,10 +33,10 @@ import TableCellHorizontalAlign from "./types/TableCellHorizontalAlign.js"; class TableCell extends TableCellBase { onBeforeRendering() { super.onBeforeRendering(); - if (this.hAlign && Object.values(TableCellHorizontalAlign).includes(this.hAlign as TableCellHorizontalAlign)) { - this.style.justifyContent = this.hAlign; + if (this.horizontalAlign && Object.values(TableCellHorizontalAlign).includes(this.horizontalAlign as TableCellHorizontalAlign)) { + this.style.justifyContent = this.horizontalAlign; } else { - this.style.justifyContent = `var(--h-align-${(this as any)._individualSlot})`; + this.style.justifyContent = `var(--horizontal-align-${(this as any)._individualSlot})`; } } diff --git a/packages/main/src/TableCellBase.ts b/packages/main/src/TableCellBase.ts index 63f866b0c3ef..4abc70a97c13 100644 --- a/packages/main/src/TableCellBase.ts +++ b/packages/main/src/TableCellBase.ts @@ -38,7 +38,7 @@ abstract class TableCellBase extends UI5Element { * @public */ @property() - hAlign?: `${TableCellHorizontalAlign}`; + horizontalAlign?: `${TableCellHorizontalAlign}`; protected ariaRole: string = "gridcell"; diff --git a/packages/main/src/TableHeaderCell.ts b/packages/main/src/TableHeaderCell.ts index e77b9bcbb1ac..c5c1933ea964 100644 --- a/packages/main/src/TableHeaderCell.ts +++ b/packages/main/src/TableHeaderCell.ts @@ -90,7 +90,7 @@ class TableHeaderCell extends TableCellBase { onBeforeRendering() { super.onBeforeRendering(); // overwrite setting of TableCellBase so that the TableHeaderCell always uses the slot variable - this.style.justifyContent = `var(--h-align-${(this as any)._individualSlot})`; + this.style.justifyContent = `var(--horizontal-align-${(this as any)._individualSlot})`; } } diff --git a/packages/main/src/TableHeaderRow.ts b/packages/main/src/TableHeaderRow.ts index 911a52ba15be..3c3d29bce8f3 100644 --- a/packages/main/src/TableHeaderRow.ts +++ b/packages/main/src/TableHeaderRow.ts @@ -56,7 +56,7 @@ class TableHeaderRow extends TableRowBase { type: HTMLElement, "default": true, invalidateOnChildChange: { - properties: ["width", "_popin", "hAlign"], + properties: ["width", "_popin", "horizontalAlign"], slots: false, }, individualSlots: true, diff --git a/packages/main/test/pages/HAlignTable.html b/packages/main/test/pages/HAlignTable.html index c77ab5fa176c..1a64c4933b0e 100644 --- a/packages/main/test/pages/HAlignTable.html +++ b/packages/main/test/pages/HAlignTable.html @@ -13,8 +13,8 @@ Product - Supplier - Dimensions + Supplier + Dimensions Weight Price diff --git a/packages/main/test/specs/Table.spec.js b/packages/main/test/specs/Table.spec.js index 1998ae2ab584..cdfd97ee890b 100644 --- a/packages/main/test/specs/Table.spec.js +++ b/packages/main/test/specs/Table.spec.js @@ -138,7 +138,7 @@ describe("Table - Horizontal alignment of cells", async () => { await browser.url(`test/pages/HAlignTable.html`); }); - it("default alignment when hAlign is not set", async () => { + it("default alignment when horizontalAlign is not set", async () => { let table = await browser.$("#table"); assert.ok(table.isExisting(), "Table exists"); @@ -150,14 +150,14 @@ describe("Table - Horizontal alignment of cells", async () => { const headerCell = headerCells[index]; const alignment = "normal"; assert.equal(await headerCell.getAttribute("id"), "productCol", "Correct cell"); - assert.equal(await headerCell.getAttribute("h-Align"), undefined, "hAlign not set"); + assert.equal(await headerCell.getAttribute("horizontal-align"), undefined, "horizontalAlign not set"); const justifyContentHeaderCell = await headerCell.getCSSProperty("justify-content"); const style = await headerCell.getAttribute("style"); const justifyContentHeaderCellUncomputed = style.match(/justify-content: ([^;]+)/)[1]; - const cssVariable = "var(--h-align-default-1)"; + const cssVariable = "var(--horizontal-align-default-1)"; assert.equal(justifyContentHeaderCell.value, alignment, "justify-content correctly set."); - assert.equal(justifyContentHeaderCellUncomputed, cssVariable, "hAlign not set"); + assert.equal(justifyContentHeaderCellUncomputed, cssVariable, "horizontalAlign not set"); const tableRows = await table.$$("ui5-table-row"); for (const row of tableRows) { @@ -168,7 +168,7 @@ describe("Table - Horizontal alignment of cells", async () => { } }); - it("horizontal alignment if hAlign is set to a value not defined in TableCellHorizontalAlign", async () => { + it("horizontal alignment if horizontalAlign is set to a value not defined in TableCellHorizontalAlign", async () => { let table = await browser.$("#table"); assert.ok(table.isExisting(), "Table exists"); @@ -181,25 +181,25 @@ describe("Table - Horizontal alignment of cells", async () => { assert.equal(await headerCell.getAttribute("id"), "productCol", "Correct cell"); let alignment = "right"; - await headerCell.setAttribute("h-Align", "Right"); + await headerCell.setAttribute("horizontal-align", "Right"); let justifyContentHeaderCell = await headerCell.getCSSProperty("justify-content"); let style = await headerCell.getAttribute("style"); let justifyContentHeaderCellUncomputed = style.match(/justify-content: ([^;]+)/)[1]; - const cssVariable = "var(--h-align-default-1)"; + const cssVariable = "var(--horizontal-align-default-1)"; assert.equal(justifyContentHeaderCell.value, alignment, "justify-content correctly set."); - assert.equal(justifyContentHeaderCellUncomputed, cssVariable, "hAlign set to css variable"); + assert.equal(justifyContentHeaderCellUncomputed, cssVariable, "horizontalAlign set to css variable"); alignment = "normal"; let hAlign = "valueNotDefinedInEnum"; - await headerCell.setAttribute("h-Align", hAlign); + await headerCell.setAttribute("horizontal-align", hAlign); - assert.equal(await headerCell.getAttribute("h-Align"), hAlign, "hAlign correctly set"); + assert.equal(await headerCell.getAttribute("horizontal-align"), hAlign, "horizontalAlign correctly set"); justifyContentHeaderCell = await headerCell.getCSSProperty("justify-content"); assert.equal(justifyContentHeaderCell.value, alignment, "justify-content correctly set."); style = await headerCell.getAttribute("style"); justifyContentHeaderCellUncomputed = style.match(/justify-content: ([^;]+)/)[1]; - assert.equal(justifyContentHeaderCellUncomputed, cssVariable, "hAlign set to css variable"); + assert.equal(justifyContentHeaderCellUncomputed, cssVariable, "horizontalAlign set to css variable"); const tableRows = await table.$$("ui5-table-row"); for (const row of tableRows) { @@ -221,9 +221,9 @@ describe("Table - Horizontal alignment of cells", async () => { const cellIndex = 1; // second row is supplier row const headerCell = headerCells[cellIndex]; const id = await headerCell.getAttribute("id"); - const hAlign = await headerCell.getAttribute("h-Align"); + const hAlign = await headerCell.getAttribute("horizontal-align"); assert.equal(id, "supplierCol", "Correct cell"); - assert.ok(hAlign, "hAlign set"); + assert.ok(hAlign, "horizontalAlign set"); const alignment = `center`; // alignment set to center in table example const justifyContentHeaderCell = await headerCell.getCSSProperty("justify-content"); @@ -238,7 +238,7 @@ describe("Table - Horizontal alignment of cells", async () => { } }); - it("on changing the h-alignment of the headerCell, the h-alignment of subsequent cells must change as well", async () => { + it("on changing the horizontal-alignment of the headerCell, the horizontal-alignment of subsequent cells must change as well", async () => { let table = await browser.$("#table"); assert.ok(table.isExisting(), "Table exists"); @@ -250,9 +250,9 @@ describe("Table - Horizontal alignment of cells", async () => { const cellIndex = 1; // second row is supplier row const headerCell = headerCells[cellIndex]; const id = await headerCell.getAttribute("id"); - let hAlign = await headerCell.getAttribute("h-Align"); + let hAlign = await headerCell.getAttribute("horizontal-align"); assert.equal(id, "supplierCol", "Correct cell"); - assert.ok(hAlign, "hAlign set"); + assert.ok(hAlign, "horizontalAlign set"); let alignment = `center`; // alignment set to center in table example let justifyContentHeaderCell = await headerCell.getCSSProperty("justify-content"); @@ -260,12 +260,12 @@ describe("Table - Horizontal alignment of cells", async () => { // update the values alignment = "left"; - await headerCell.setAttribute("h-Align", "Left"); - hAlign = await headerCell.getAttribute("h-Align"); + await headerCell.setAttribute("horizontal-align", "Left"); + hAlign = await headerCell.getAttribute("horizontal-align"); justifyContentHeaderCell = await headerCell.getCSSProperty("justify-content"); assert.equal(id, "supplierCol", "Correct cell"); - assert.ok(hAlign, "hAlign set"); + assert.ok(hAlign, "horizontalAlign set"); assert.equal(justifyContentHeaderCell.value, alignment, "justify-content value updated."); const tableRows = await table.$$("ui5-table-row"); @@ -277,7 +277,7 @@ describe("Table - Horizontal alignment of cells", async () => { } }); - it("on changing the h-alignment of a cell, the h-alignment of other cells must not change", async () => { + it("on changing the horizontal-alignment of a cell, the horizontal-alignment of other cells must not change", async () => { let table = await browser.$("#table"); assert.ok(table.isExisting(), "Table exists"); @@ -288,9 +288,9 @@ describe("Table - Horizontal alignment of cells", async () => { const cellIndex = 1; // second row is supplier row const headerCell = headerCells[cellIndex]; const id = await headerCell.getAttribute("id"); - const hAlign = await headerCell.getAttribute("h-Align"); + const hAlign = await headerCell.getAttribute("horizontal-align"); assert.equal(id, "supplierCol", "Correct cell"); - assert.ok(hAlign, "hAlign set"); + assert.ok(hAlign, "horizontalAlign set"); const alignment = `center`; // alignment set to center in table example const justifyContentHeaderCell = await headerCell.getCSSProperty("justify-content"); @@ -299,16 +299,16 @@ describe("Table - Horizontal alignment of cells", async () => { const tableRows = await table.$$("ui5-table-row"); const rowWithChangedCell = 0; const cell = await tableRows[rowWithChangedCell].$$("ui5-table-cell")[cellIndex]; - let hAlignCell = await cell.getAttribute("h-Align"); - assert.equal(hAlignCell, null, "hAlign property of the cell is not set."); + let hAlignCell = await cell.getAttribute("horizontal-align"); + assert.equal(hAlignCell, null, "horizontalAlign property of the cell is not set."); let justifyContentCell = await cell.getCSSProperty("justify-content"); assert.equal(justifyContentCell.value, alignment, "justify-content of the cell matches the headerCell"); const customAlignmentCell = "left"; // alignment used for a single cell - await cell.setAttribute("h-Align", "Left"); - hAlignCell = await cell.getAttribute("h-Align"); - assert.notEqual(hAlignCell, null, "hAlign property of the cell is set now."); + await cell.setAttribute("horizontal-align", "Left"); + hAlignCell = await cell.getAttribute("horizontal-align"); + assert.notEqual(hAlignCell, null, "horizontalAlign property of the cell is set now."); justifyContentCell = await cell.getCSSProperty("justify-content"); assert.equal(justifyContentCell.value, customAlignmentCell, "justify-content was changed and now matches the custom cell alignment value."); @@ -322,7 +322,7 @@ describe("Table - Horizontal alignment of cells", async () => { } }); - it("the h-alignment of a cell differs from the others, on changing the h-alignment of the headerCell, the h-alignment of other cells must change as well except of this one custom aligned cell", async () => { + it("the horizontal-alignment of a cell differs from the others, on changing the horizontal-alignment of the headerCell, the horizontal-alignment of other cells must change as well except of this one custom aligned cell", async () => { let table = await browser.$("#table"); assert.ok(table.isExisting(), "Table exists"); @@ -333,9 +333,9 @@ describe("Table - Horizontal alignment of cells", async () => { const cellIndex = 1; // second row is supplier row const headerCell = headerCells[cellIndex]; const id = await headerCell.getAttribute("id"); - const hAlign = await headerCell.getAttribute("h-Align"); + const hAlign = await headerCell.getAttribute("horizontal-align"); assert.equal(id, "supplierCol", "Correct cell"); - assert.ok(hAlign, "hAlign set"); + assert.ok(hAlign, "horizontalAlign set"); let alignment = "center"; // alignment set to center in table example let justifyContent = await headerCell.getCSSProperty("justify-content"); @@ -345,25 +345,25 @@ describe("Table - Horizontal alignment of cells", async () => { const rowWithChangedCell = 0; const cell = await tableRows[rowWithChangedCell].$$("ui5-table-cell")[cellIndex]; let justifyContentCell = await cell.getCSSProperty("justify-content"); - let hAlignCell = await cell.getAttribute("h-Align"); - assert.equal(hAlignCell, null, "hAlign property of the cell is not set."); + let hAlignCell = await cell.getAttribute("horizontal-align"); + assert.equal(hAlignCell, null, "horizontalAlign property of the cell is not set."); assert.equal(justifyContentCell.value, alignment, "justify-content value matches headerCell alignment"); const customAlignmentCell = "left"; // alignment used for a single cell - await cell.setAttribute("h-Align", "Left"); - hAlignCell = await cell.getAttribute("h-Align"); + await cell.setAttribute("horizontal-align", "Left"); + hAlignCell = await cell.getAttribute("horizontal-align"); - assert.notEqual(hAlignCell, null, "hAlign property of the cell is set now."); + assert.notEqual(hAlignCell, null, "horizontalAlign property of the cell is set now."); justifyContentCell = await cell.getCSSProperty("justify-content"); assert.equal(justifyContentCell.value, customAlignmentCell, "justify-content of cell adjusted correctly."); alignment = "right" - await headerCell.setAttribute("h-Align", "Right"); - hAlignCell = await cell.getAttribute("h-Align"); + await headerCell.setAttribute("horizontal-align", "Right"); + hAlignCell = await cell.getAttribute("horizontal-align"); justifyContent = await headerCell.getCSSProperty("justify-content"); - assert.ok(hAlign, "hAlign set"); + assert.ok(hAlign, "horizontalAlign set"); assert.equal(justifyContent.value, alignment, "justify-content of headerCell changed correctly."); for (const [index, row] of tableRows.entries()) { diff --git a/packages/website/docs/_components_pages/main/Table/TableCell.mdx b/packages/website/docs/_components_pages/main/Table/TableCell.mdx index 72fc8e9c548f..77c222924066 100644 --- a/packages/website/docs/_components_pages/main/Table/TableCell.mdx +++ b/packages/website/docs/_components_pages/main/Table/TableCell.mdx @@ -11,14 +11,14 @@ import HAlign from "../../../_samples/main/Table/HAlign/HAlign.md"; ### Horizontal cell alignment -Control the horizontal alignment of cells by utilizing the `hAlign` property. +Control the horizontal alignment of cells by utilizing the `horizontalAlign` property. -Please note that the behaviour of `hAlign` depends on where you set it: -1. `hAlign` is set on `TableHeaderCell` level
+Please note that the behaviour of `horizontalAlign` depends on where you set it: +1. `horizontalAlign` is set on `TableHeaderCell` level
Changes the horizontal alignment of the `TableHeaderCell` and their corresponding `TableCells`. -2. `hAlign` is set on `TableCell` level only
+2. `horizontalAlign` is set on `TableCell` level only
The horizontal alignment is only changed for this one `TableCell` -3. `hAlign` is set on `TableHeaderCell` and `TableCell` level
-Changing the `hAlign` property on `TableHeaderCell` level changes only the `TableHeaderCell` itself and those `TableCells` without a `hAlign` property. +3. `horizontalAlign` is set on `TableHeaderCell` and `TableCell` level
+Changing the `horizontalAlign` property on `TableHeaderCell` level changes only the `TableHeaderCell` itself and those `TableCells` without a `horizontalAlign` property. \ No newline at end of file diff --git a/packages/website/docs/_components_pages/main/Table/TableHeaderCell.mdx b/packages/website/docs/_components_pages/main/Table/TableHeaderCell.mdx index 786e6b63e8fa..053c39cda2da 100644 --- a/packages/website/docs/_components_pages/main/Table/TableHeaderCell.mdx +++ b/packages/website/docs/_components_pages/main/Table/TableHeaderCell.mdx @@ -21,14 +21,14 @@ import HAlign from "../../../_samples/main/Table/HAlign/HAlign.md"; ### Horizontal cell alignment -Control the horizontal alignment of cells by utilizing the `hAlign` property. +Control the horizontal alignment of cells by utilizing the `horizontalAlign` property. -Please note that the behaviour of `hAlign` depends on where you set it: -1. `hAlign` is set on `TableHeaderCell` level
+Please note that the behaviour of `horizontalAlign` depends on where you set it: +1. `horizontalAlign` is set on `TableHeaderCell` level
Changes the horizontal alignment of the `TableHeaderCell` and their corresponding `TableCells`. -2. `hAlign` is set on `TableCell` level only
+2. `horizontalAlign` is set on `TableCell` level only
The horizontal alignment is only changed for this one `TableCell` -3. `hAlign` is set on `TableHeaderCell` and `TableCell` level
-Changing the `hAlign` property on `TableHeaderCell` level changes only the `TableHeaderCell` itself and those `TableCells` without a `hAlign` property. +3. `horizontalAlign` is set on `TableHeaderCell` and `TableCell` level
+Changing the `horizontalAlign` property on `TableHeaderCell` level changes only the `TableHeaderCell` itself and those `TableCells` without a `horizontalAlign` property. \ No newline at end of file diff --git a/packages/website/docs/_samples/main/Table/HAlign/sample.html b/packages/website/docs/_samples/main/Table/HAlign/sample.html index 970abae533c5..fbe97336c48d 100644 --- a/packages/website/docs/_samples/main/Table/HAlign/sample.html +++ b/packages/website/docs/_samples/main/Table/HAlign/sample.html @@ -14,8 +14,8 @@ Product - Supplier - Dimensions + Supplier + Dimensions Weight Price @@ -29,7 +29,7 @@ Notebook Basic 17
HT-1001
Smartcards - 29 x 17 x 3.1 cm + 29 x 17 x 3.1 cm 4.5 KG 1249 EUR
From eabfb94eef44b4d697527efb0b269112f6fc50e9 Mon Sep 17 00:00:00 2001 From: Daniel Phillip Nowak Date: Mon, 9 Sep 2024 15:12:08 +0200 Subject: [PATCH 09/46] fix(ui5-table): skip horizontalAlign css variable in case headerRow or cells are empty --- packages/main/src/Table.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/main/src/Table.ts b/packages/main/src/Table.ts index edf6d7483cf3..c17e29aaf797 100644 --- a/packages/main/src/Table.ts +++ b/packages/main/src/Table.ts @@ -465,7 +465,7 @@ class Table extends UI5Element { } get styles() { - const headerStyleMap = this.headerRow[0].cells.reduce((headerStyles, headerCell) => { + const headerStyleMap = this.headerRow?.[0]?.cells?.reduce((headerStyles, headerCell) => { if (headerCell.horizontalAlign !== undefined && Object.values(TableCellHorizontalAlign).includes(headerCell.horizontalAlign as TableCellHorizontalAlign)) { headerStyles[`--horizontal-align-${headerCell._individualSlot}`] = headerCell.horizontalAlign; } From 87fb2f4e61c4d010b54925d209da912077a36377 Mon Sep 17 00:00:00 2001 From: Daniel Phillip Nowak Date: Fri, 13 Sep 2024 16:50:14 +0200 Subject: [PATCH 10/46] feat(ui5-table): minor adjustments --- packages/main/src/Table.ts | 11 +++++------ packages/main/src/TableCell.ts | 2 +- packages/main/src/TableCellBase.ts | 4 ++-- packages/main/src/TableHeaderCell.ts | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/main/src/Table.ts b/packages/main/src/Table.ts index c17e29aaf797..821ea745dbca 100644 --- a/packages/main/src/Table.ts +++ b/packages/main/src/Table.ts @@ -466,18 +466,17 @@ class Table extends UI5Element { get styles() { const headerStyleMap = this.headerRow?.[0]?.cells?.reduce((headerStyles, headerCell) => { - if (headerCell.horizontalAlign !== undefined && Object.values(TableCellHorizontalAlign).includes(headerCell.horizontalAlign as TableCellHorizontalAlign)) { + if (headerCell.horizontalAlign !== undefined) { headerStyles[`--horizontal-align-${headerCell._individualSlot}`] = headerCell.horizontalAlign; } return headerStyles; }, {} as { [key: string]: string }); - - return { + return { table: { - "grid-template-columns": this._gridTemplateColumns, - ...headerStyleMap, + "grid-template-columns": this._gridTemplateColumns, + ...headerStyleMap, }, - }; + }; } get _gridTemplateColumns() { diff --git a/packages/main/src/TableCell.ts b/packages/main/src/TableCell.ts index 0c9f02d98748..a5d6f8996c2c 100644 --- a/packages/main/src/TableCell.ts +++ b/packages/main/src/TableCell.ts @@ -33,7 +33,7 @@ import TableCellHorizontalAlign from "./types/TableCellHorizontalAlign.js"; class TableCell extends TableCellBase { onBeforeRendering() { super.onBeforeRendering(); - if (this.horizontalAlign && Object.values(TableCellHorizontalAlign).includes(this.horizontalAlign as TableCellHorizontalAlign)) { + if (this.horizontalAlign) { this.style.justifyContent = this.horizontalAlign; } else { this.style.justifyContent = `var(--horizontal-align-${(this as any)._individualSlot})`; diff --git a/packages/main/src/TableCellBase.ts b/packages/main/src/TableCellBase.ts index 4abc70a97c13..fc69223f0fe2 100644 --- a/packages/main/src/TableCellBase.ts +++ b/packages/main/src/TableCellBase.ts @@ -6,7 +6,7 @@ import property from "@ui5/webcomponents-base/dist/decorators/property.js"; import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; import { getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js"; import TableCellBaseStyles from "./generated/themes/TableCellBase.css.js"; -import type TableCellHorizontalAlign from "./types/TableCellHorizontalAlign.js"; +import TableCellHorizontalAlign from "./types/TableCellHorizontalAlign.js"; /** * @class @@ -34,7 +34,7 @@ abstract class TableCellBase extends UI5Element { /** * Determines the horizontal alignment of table cells. - * @default "Start" + * @default "Normal" * @public */ @property() diff --git a/packages/main/src/TableHeaderCell.ts b/packages/main/src/TableHeaderCell.ts index c5c1933ea964..23b911199497 100644 --- a/packages/main/src/TableHeaderCell.ts +++ b/packages/main/src/TableHeaderCell.ts @@ -90,7 +90,7 @@ class TableHeaderCell extends TableCellBase { onBeforeRendering() { super.onBeforeRendering(); // overwrite setting of TableCellBase so that the TableHeaderCell always uses the slot variable - this.style.justifyContent = `var(--horizontal-align-${(this as any)._individualSlot})`; + this.style.justifyContent = `var(--horizontal-align-${this._individualSlot})`; } } From 09a14cf96b40eefe2321afda5851c315705fd08f Mon Sep 17 00:00:00 2001 From: Daniel Phillip Nowak Date: Mon, 16 Sep 2024 09:42:01 +0200 Subject: [PATCH 11/46] fix(ui5-table): removed unused imports --- packages/main/src/Table.ts | 1 - packages/main/src/TableCell.ts | 1 - packages/main/src/TableCellBase.ts | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/main/src/Table.ts b/packages/main/src/Table.ts index 821ea745dbca..de12c370cf49 100644 --- a/packages/main/src/Table.ts +++ b/packages/main/src/Table.ts @@ -25,7 +25,6 @@ import { import BusyIndicator from "./BusyIndicator.js"; import TableCell from "./TableCell.js"; import { findVerticalScrollContainer, scrollElementIntoView, isFeature } from "./TableUtils.js"; -import TableCellHorizontalAlign from "./types/TableCellHorizontalAlign.js"; /** * Interface for components that can be slotted inside the features slot of the ui5-table. diff --git a/packages/main/src/TableCell.ts b/packages/main/src/TableCell.ts index a5d6f8996c2c..68725811ad8b 100644 --- a/packages/main/src/TableCell.ts +++ b/packages/main/src/TableCell.ts @@ -5,7 +5,6 @@ import TableCellBase from "./TableCellBase.js"; import type TableRow from "./TableRow.js"; import type Table from "./Table.js"; import { LABEL_COLON } from "./generated/i18n/i18n-defaults.js"; -import TableCellHorizontalAlign from "./types/TableCellHorizontalAlign.js"; /** * @class diff --git a/packages/main/src/TableCellBase.ts b/packages/main/src/TableCellBase.ts index fc69223f0fe2..58e0737595e2 100644 --- a/packages/main/src/TableCellBase.ts +++ b/packages/main/src/TableCellBase.ts @@ -6,7 +6,7 @@ import property from "@ui5/webcomponents-base/dist/decorators/property.js"; import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js"; import { getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js"; import TableCellBaseStyles from "./generated/themes/TableCellBase.css.js"; -import TableCellHorizontalAlign from "./types/TableCellHorizontalAlign.js"; +import type TableCellHorizontalAlign from "./types/TableCellHorizontalAlign.js"; /** * @class From 9b0f25ba5d31651c1935ab59e5257495ead295fb Mon Sep 17 00:00:00 2001 From: Daniel Phillip Nowak Date: Mon, 16 Sep 2024 15:27:03 +0200 Subject: [PATCH 12/46] feat(ui5-table): added note for horizontalAlign values --- packages/main/src/TableCellBase.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/main/src/TableCellBase.ts b/packages/main/src/TableCellBase.ts index 58e0737595e2..61eb0c3feb6f 100644 --- a/packages/main/src/TableCellBase.ts +++ b/packages/main/src/TableCellBase.ts @@ -34,6 +34,7 @@ abstract class TableCellBase extends UI5Element { /** * Determines the horizontal alignment of table cells. + * Note: All values valid for justify-content can be used not just the ones inside the enum. * @default "Normal" * @public */ From e93865893dcaf4294f6cf7bc9a266c070ee39e6e Mon Sep 17 00:00:00 2001 From: ilhan orhan Date: Mon, 16 Sep 2024 10:39:50 +0300 Subject: [PATCH 13/46] chore: migrate more tests to Cypress (#9863) --- .../cypress/specs/base/AriaLabelHelper.cy.ts | 305 ++++++++++++++++++ .../cypress/specs/base/IconCollection.cy.ts | 55 ++++ .../specs/base/css/redfish.custom.theme.css | 13 + .../base/IconCollectionInCustomTheme.html | 2 +- .../test/specs/base/AriaLabelHelper.spec.js | 137 -------- .../test/specs/base/IconCollection.spec.js | 70 ---- 6 files changed, 374 insertions(+), 208 deletions(-) create mode 100644 packages/main/cypress/specs/base/AriaLabelHelper.cy.ts create mode 100644 packages/main/cypress/specs/base/IconCollection.cy.ts create mode 100644 packages/main/cypress/specs/base/css/redfish.custom.theme.css delete mode 100644 packages/main/test/specs/base/AriaLabelHelper.spec.js delete mode 100644 packages/main/test/specs/base/IconCollection.spec.js diff --git a/packages/main/cypress/specs/base/AriaLabelHelper.cy.ts b/packages/main/cypress/specs/base/AriaLabelHelper.cy.ts new file mode 100644 index 000000000000..4b018805b158 --- /dev/null +++ b/packages/main/cypress/specs/base/AriaLabelHelper.cy.ts @@ -0,0 +1,305 @@ +import { html } from "lit"; +import "../../../src/Label.js"; +import "../../../src/Input.js"; + +describe("AriaLabelHelper", () => { + it("Label-for tests", () => { + cy.mount(html` + + Desc1 + Desc2 + Desc3 + + `); + + // assert + cy.get("#myInput") + .shadow() + .find("input") + .invoke("attr", "aria-label") + .should("eq", "Desc1 Desc2 Desc3 Desc4"); + + // act + cy.get("#lblDesc2") + .invoke("attr", "for", "other"); + + cy.get("#lblDesc3") + .invoke("remove"); + + // assert + cy.get("#myInput") + .shadow() + .find("input") + .invoke("attr", "aria-label") + .should("eq", "Desc1 Desc4"); + }); + + it("Input accessibleNameRef Tests", () => { + cy.mount(html` + FirstDesc + SecondDesc + ThirdDesc + + `); + + // assert + cy.get("#inputEnterName") + .shadow() + .find("input") + .invoke("attr", "aria-label") + .should("eq", "FirstDesc ThirdDesc"); + + // act - update text of referenced label + cy.get("#lblEnterName1") + .then($el => { + $el.get(0).innerHTML = "First Label Desc"; + }); + + // assert + cy.get("#inputEnterName") + .shadow() + .find("input") + .invoke("attr", "aria-label") + .should("eq", "First Label Desc ThirdDesc"); + + // act - update accessible-name-ref + cy.get("#inputEnterName") + .invoke("attr", "accessible-name-ref", "lblEnterName3 lblEnterName1"); + + // assert + cy.get("#inputEnterName") + .shadow() + .find("input") + .invoke("attr", "aria-label") + .should("eq", "ThirdDesc First Label Desc"); + + // act - update accessible-name-ref + cy.get("#inputEnterName") + .invoke("attr", "accessible-name-ref", "lblEnterName2"); + + // assert + cy.get("#inputEnterName") + .shadow() + .find("input") + .invoke("attr", "aria-label") + .should("eq", "SecondDesc"); + }); + + it("Input accessibleName and accessibleNameRef Tests", () => { + cy.mount(html` + Label for inputEnterDesc + Label to be added/removed as accessible-name-ref + + `); + + const INITIAL_ACCESSIBLE_NAME = "Some description added by accessibleName"; + const UPDATED_ACCESSIBLE_NAME = "Another description added by accessibleName"; + const ACCESSIBLE_NAME_REF = "lblEnterDesc3"; + const ACCESSIBLE_NAME_REF_TEXT = "Label to be added/removed as accessible-name-ref"; + + cy.get("#inputEnterDesc") + .shadow() + .find("input") + .as("input"); + + // assert + cy.get("@input") + .invoke("attr", "aria-label") + .should("eq", INITIAL_ACCESSIBLE_NAME); + + cy.get("#inputEnterDesc") + .invoke("attr", "accessible-name", UPDATED_ACCESSIBLE_NAME); + + // assert + cy.get("@input") + .invoke("attr", "aria-label") + .should("eq", UPDATED_ACCESSIBLE_NAME); + + // act - remove acccessible-name + cy.get("#inputEnterDesc") + .invoke("removeAttr", "accessible-name"); + + // assert - aria-label fallbacks to use the label's for, pointing to this input + cy.get("@input") + .invoke("attr", "aria-label") + .should("eq", "Label for inputEnterDesc"); + + // act - add acccessible-name-ref + cy.get("#inputEnterDesc") + .invoke("attr", "accessible-name-ref", ACCESSIBLE_NAME_REF); + + // assert - the text of the elment labelled with accessible-name-ref is used + cy.get("@input") + .invoke("attr", "aria-label") + .should("eq", ACCESSIBLE_NAME_REF_TEXT); + + // act - add acccessible-name once again + cy.get("#inputEnterDesc") + .invoke("attr", "accessible-name", INITIAL_ACCESSIBLE_NAME); + + // assert - the text of the elment labelled with accessible-name-ref is still used + cy.get("@input") + .invoke("attr", "aria-label") + .should("eq", ACCESSIBLE_NAME_REF_TEXT); + + // act - remove acccessible-name-ref + cy.get("#inputEnterDesc") + .invoke("removeAttr", "accessible-name-ref"); + + // assert - after acccessible-name-ref is removed, fallbacks to use acccessible-name + cy.get("@input") + .invoke("attr", "aria-label") + .should("eq", INITIAL_ACCESSIBLE_NAME); + + // act - remove acccessible-name + cy.get("#inputEnterDesc") + .invoke("removeAttr", "accessible-name"); + + // assert - aria-label fallbacks to use the label's for, pointing to this input + cy.get("@input") + .invoke("attr", "aria-label") + .should("eq", "Label for inputEnterDesc"); + + // act - remove ui5-label's for + cy.get("#lblEnterDesc1") + .invoke("removeAttr", "for"); + + // assert - aria-label is undefined + cy.get("@input") + .invoke("attr", "aria-label") + .should("eq", undefined); + }); + + it("Three inputs with same label accessibleNameRef Tests", () => { + cy.mount(html` + Label for testInput1 Desc + + + + `); + + const LBL_TEXT_CONTENT = "Label for testInput1 Desc"; + const LBL_TEXT_CONTENT_UPDATED = "Another description for testing"; + + cy.get("#testInput1") + .shadow() + .find("input") + .as("input1"); + + cy.get("#testInput2") + .shadow() + .find("input") + .as("input2"); + + cy.get("#testInput3") + .shadow() + .find("input") + .as("input3"); + + // assert + cy.get("@input1") + .invoke("attr", "aria-label") + .should("eq", LBL_TEXT_CONTENT); + + cy.get("@input2") + .invoke("attr", "aria-label") + .should("eq", LBL_TEXT_CONTENT); + + cy.get("@input3") + .invoke("attr", "aria-label") + .should("eq", LBL_TEXT_CONTENT); + + // act + + cy.get("#lblTestDesc") + .then($el => { + $el.get(0).innerHTML = LBL_TEXT_CONTENT_UPDATED; + }); + + // assert + cy.get("@input1") + .invoke("attr", "aria-label") + .should("eq", LBL_TEXT_CONTENT_UPDATED); + + cy.get("@input2") + .invoke("attr", "aria-label") + .should("eq", LBL_TEXT_CONTENT_UPDATED); + + cy.get("@input3") + .invoke("attr", "aria-label") + .should("eq", LBL_TEXT_CONTENT_UPDATED); + + // act - remove "for" attribute + cy.get("#lblTestDesc") + .invoke("removeAttr", "for"); + + // assert - aria-label is undefined + cy.get("@input1") + .invoke("attr", "aria-label") + .should("eq", undefined); + + // act - remove accessible-name-ref + cy.get("#testInput2") + .invoke("removeAttr", "accessible-name-ref"); + + // assert - aria-label is the existing accessible-name + cy.get("@input2") + .invoke("attr", "aria-label") + .should("eq", "Hello"); + + // act - remove accessible-name-ref + cy.get("#testInput3") + .invoke("removeAttr", "accessible-name-ref"); + + // assert - shouldn't be any aria-label + cy.get("@input3") + .invoke("attr", "aria-label") + .should("eq", undefined); + }); + + it("Tests generic html elements with for attribute", () => { + cy.mount(html` + + + +
Desc3
+ Desc4 + Desc5 + `); + + cy.get("#myInput2") + .shadow() + .find("input") + .as("input"); + + // assert + cy.get("@input") + .invoke("attr", "aria-label") + .should("eq", "Desc1 Desc2 Desc3 Desc4 Desc5"); + + // act + cy.get("#elId1") + .then($el => { + $el.get(0).innerHTML = `${$el.get(0).innerHTML}X`; + }); + + cy.get("#elId2") + .invoke("remove"); + + cy.get("#elId3") + .invoke("attr", "for", "other"); + + cy.get("#elId4") + .then($el => { + $el.get(0).innerHTML = `${$el.get(0).innerHTML}X`; + }); + + cy.get("#elId5") + .invoke("removeAttr", "for"); + + // assert + cy.get("@input") + .invoke("attr", "aria-label") + .should("eq", "Desc1X Desc4X"); + }); +}); diff --git a/packages/main/cypress/specs/base/IconCollection.cy.ts b/packages/main/cypress/specs/base/IconCollection.cy.ts new file mode 100644 index 000000000000..8fe2f07e7f75 --- /dev/null +++ b/packages/main/cypress/specs/base/IconCollection.cy.ts @@ -0,0 +1,55 @@ +import "./css/redfish.custom.theme.css"; +import getEffectiveIconCollection from "@ui5/webcomponents-base/dist/asset-registries/util/getIconCollectionByTheme.js"; +import { setTheme, isLegacyThemeFamily } from "@ui5/webcomponents-base/dist/config/Theme.js"; +import { html } from "lit"; +import "../../../src/Assets.js"; +import "../../../src/Icon.js"; + +setTheme("sap_fiori_3_dark"); + +describe("Icon collection", () => { + it("tests the icon collection in built-in themes", () => { + cy.mount(html``); + + cy.wrap({ getEffectiveIconCollection }) + .invoke("getEffectiveIconCollection") + .should("equal", "SAP-icons-v4"); + + cy.wrap({ isLegacyThemeFamily }) + .invoke("isLegacyThemeFamily") + .should("be.true"); + + // act + cy.wrap({ setTheme }) + .invoke("setTheme", "sap_horizon"); + + // assert + cy.wrap({ getEffectiveIconCollection }) + .invoke("getEffectiveIconCollection") + .should("equal", "SAP-icons-v5"); + + cy.wrap({ isLegacyThemeFamily }) + .invoke("isLegacyThemeFamily") + .should("be.false"); + }); + + it("tests the icon collection in built-in themes", () => { + cy.mount(html``); + + // act + cy.wrap({ setTheme }) + .invoke("setTheme", "redfish"); + + // assert + // The 'SAP-icons-v5' collection is correctly used in 'redfish' - extending 'sap_horizon' + cy.wrap({ getEffectiveIconCollection }) + .invoke("getEffectiveIconCollection") + .should("equal", "SAP-icons-v5"); + + // assert + // "The 'redfish' custom theme is not part of legacy theme family, as it's extending 'sap_horizon'. + cy.wrap({ isLegacyThemeFamily }) + .invoke("isLegacyThemeFamily") + .should("be.false"); + }); +}); diff --git a/packages/main/cypress/specs/base/css/redfish.custom.theme.css b/packages/main/cypress/specs/base/css/redfish.custom.theme.css new file mode 100644 index 000000000000..648a200fbc78 --- /dev/null +++ b/packages/main/cypress/specs/base/css/redfish.custom.theme.css @@ -0,0 +1,13 @@ +/** +* Copyright (c) 2012-2020 SAP SE or an SAP affiliate company. All rights reserved. +* +* Theming Engine 1.60.0 +* data:{"Path": "Base.baseLib.redfish.css_variables", "PathPattern": "/%frameworkId%/%libId%/%themeId%/%fileId%.css", "Extends": ["sap_horizon","sap_base_fiori","baseTheme"], "Tags": ["Horizon","LightColorScheme"], "Version": { "Build":"11.1.27.20210312160011", "Source": "11.1.27", "Engine": "1.60.0"}} +*/ +.sapThemeMetaData-Base-baseLib{background-image: url('data:text/plain;utf-8,{"Path": "Base.baseLib.redfish.css_variables", "PathPattern": "/%frameworkId%/%libId%/%themeId%/%fileId%.css", "Extends": ["sap_horizon","sap_base_fiori","baseTheme"], "Tags": ["Horizon","LightColorScheme"], "Version": { "Build":"11.1.27.20210312160011", "Source": "11.1.27", "Engine": "1.60.0"}}');} +:root { + --sapBrandColor: #f42015; + --sapBaseColor: #fff; + --sapBackgroundColor: #f7f7f7; + --sapTextColor: #000; +} diff --git a/packages/main/test/pages/base/IconCollectionInCustomTheme.html b/packages/main/test/pages/base/IconCollectionInCustomTheme.html index 3d1cb8d08414..57208952bb39 100644 --- a/packages/main/test/pages/base/IconCollectionInCustomTheme.html +++ b/packages/main/test/pages/base/IconCollectionInCustomTheme.html @@ -11,7 +11,7 @@ diff --git a/packages/main/test/specs/base/AriaLabelHelper.spec.js b/packages/main/test/specs/base/AriaLabelHelper.spec.js deleted file mode 100644 index 91bd3e759a81..000000000000 --- a/packages/main/test/specs/base/AriaLabelHelper.spec.js +++ /dev/null @@ -1,137 +0,0 @@ -import { assert } from "chai"; - -describe('AriaLabelHelper', () => { - before(async () => { - await browser.url(`test/pages/base/AriaLabelHelper.html`); - }); - - const getMessageAriaLabelAsExpected = (actual, expected) => { - return `aria-label ${actual} is as expected ${expected}.`; - }; - - const testInputAriaLabelMatchesLabels = async (inputId, labelIds) => { - const input = await browser.$(`#${inputId}`); - const innerInput = await input.shadow$('input'); - const actualAriaLabel = await innerInput.getAttribute('aria-label'); - - const promises = labelIds.map(async (labelId) => { - const label = await browser.$(`#${labelId}`); - return await label.getText(); - }); - const texts = await Promise.all(promises); - const expectedAriaLabel = texts.join(' '); - - assert.equal( - actualAriaLabel, - expectedAriaLabel, - getMessageAriaLabelAsExpected(actualAriaLabel, expectedAriaLabel) - ); - }; - - const testInputAriaLabelMatchesAccessibleName = async (inputId) => { - const input = await browser.$(`#${inputId}`); - const innerInput = await input.shadow$('input'); - const accessibleNameValue = await input.getAttribute('accessible-name'); - const actualAriaLabel = await innerInput.getAttribute('aria-label'); - assert.equal( - actualAriaLabel, - accessibleNameValue, - getMessageAriaLabelAsExpected(actualAriaLabel, accessibleNameValue) - ); - }; - - const testInputAriaLabelIsUndefined = async (inputId) => { - const input = await browser.$(`#${inputId}`); - const innerInput = await input.shadow$('input'); - const actualAriaLabel = await innerInput.getAttribute('aria-label'); - assert.strictEqual( - actualAriaLabel, - null, - `Aria Label is null. attr=${actualAriaLabel}` - ); - }; - - it('Label-for tests', async () => { - const btn = await browser.$('#btnChange'); - await testInputAriaLabelMatchesLabels('myInput', ['lblDesc1', 'lblDesc2', 'lblDesc3', 'lblDesc4']); - await btn.click(); - await testInputAriaLabelMatchesLabels('myInput', ['lblDesc1', 'lblDesc4']); - }); - - it('Input accessibleNameRef Tests', async () => { - const btnChangeDesc1 = await browser.$('#btnChange2'); //Change Desc lblEnterName1 - const btnChangeDesc2 = await browser.$('#btnChange22'); //Change Desc lblEnterName2 - const btnSwap = await browser.$('#btnChange3'); // Swap Accessible Name Ref 1 and 2 - const btnRemove = await browser.$('#btnChange35'); // Remove lblEnterName3 from accessible-name-ref - - await testInputAriaLabelMatchesLabels('inputEnterName', ['lblEnterName1', 'lblEnterName3']); - await btnChangeDesc1.click(); - await testInputAriaLabelMatchesLabels('inputEnterName', ['lblEnterName1', 'lblEnterName3']); - await btnSwap.click(); - await btnChangeDesc2.click(); - await testInputAriaLabelMatchesLabels('inputEnterName', ['lblEnterName2', 'lblEnterName3']); - await btnRemove.click(); - await testInputAriaLabelMatchesLabels('inputEnterName', ['lblEnterName2']); - }); - - it('Input accessibleName and accessibleNameRef Tests', async () => { - const toggleAccessibleName = await browser.$('#btnChange4'); // Toggle AccessibleName Value - const addRemoveAccessibleName = await browser.$('#btnChange5'); // Add/Remove AccessibleName Attribute - const addRemoveAccessibleNameRef = await browser.$('#btnChange6'); // Add/Remove Accessible Name Ref - const removeLabelForAttr = await browser.$('#btnChange65'); // Removes the for-attribute for the associated label - - await testInputAriaLabelMatchesAccessibleName('inputEnterDesc'); - await toggleAccessibleName.click(); // toggle the accessible-name - await testInputAriaLabelMatchesAccessibleName('inputEnterDesc'); - await addRemoveAccessibleName.click(); // remove accessible name - await testInputAriaLabelMatchesLabels('inputEnterDesc', ['lblEnterDesc1']); - await addRemoveAccessibleNameRef.click(); // add accessible-name-ref - await testInputAriaLabelMatchesLabels('inputEnterDesc', ['lblEnterDesc3']); - await addRemoveAccessibleName.click(); // add accessible-name - await testInputAriaLabelMatchesLabels('inputEnterDesc', ['lblEnterDesc3']); - await addRemoveAccessibleNameRef.click(); // remove accessible-name-ref - await testInputAriaLabelMatchesAccessibleName('inputEnterDesc'); - await addRemoveAccessibleName.click(); // remove accessible-name - await testInputAriaLabelMatchesLabels('inputEnterDesc', ['lblEnterDesc1']); - await removeLabelForAttr.click(); // remove label-for from DOM - await testInputAriaLabelIsUndefined('inputEnterDesc'); - }); - - it('Three inputs with same label accessibleNameRef Tests', async () => { - const addRemoveForAttribute = await browser.$('#btnChange71'); // Add/Remove For Attribute On Label - const removeAccessibleNameRef2 = await browser.$('#btnChange72'); // Remove AccessibleNameRef Attribute For Input 2 - const removeAccessibleNameRef3 = await browser.$('#btnChange73'); // Remove AccessibleNameRef Attribute For Input 3 - const btnChangeDesc = await browser.$('#btnChange74'); // Change Description - - await testInputAriaLabelMatchesLabels('testInput1', ['lblTestDesc']); - await testInputAriaLabelMatchesLabels('testInput2', ['lblTestDesc']); - await testInputAriaLabelMatchesLabels('testInput3', ['lblTestDesc']); - - await btnChangeDesc.click(); - await testInputAriaLabelMatchesLabels('testInput1', ['lblTestDesc']); - await testInputAriaLabelMatchesLabels('testInput2', ['lblTestDesc']); - await testInputAriaLabelMatchesLabels('testInput3', ['lblTestDesc']); - - await addRemoveForAttribute.click(); - await testInputAriaLabelIsUndefined('testInput1'); - await testInputAriaLabelMatchesLabels('testInput2', ['lblTestDesc']); - await testInputAriaLabelMatchesLabels('testInput3', ['lblTestDesc']); - - await removeAccessibleNameRef2.click(); - await testInputAriaLabelIsUndefined('testInput1'); - await testInputAriaLabelMatchesAccessibleName('testInput2'); - await testInputAriaLabelMatchesLabels('testInput3', ['lblTestDesc']); - - await removeAccessibleNameRef3.click(); - await testInputAriaLabelIsUndefined('testInput1'); - await testInputAriaLabelMatchesAccessibleName('testInput2'); - await testInputAriaLabelIsUndefined('testInput3'); - }); - - it('Tests generic html elements with for attribute', async () => { - const btnChange8 = await browser.$('#btnChange8'); - await testInputAriaLabelMatchesLabels('myInput2', ['elId1', 'elId2', 'elId3', 'elId4', 'elId5']); - await btnChange8.click(); - await testInputAriaLabelMatchesLabels('myInput2', ['elId1', 'elId4']); - }); -}); diff --git a/packages/main/test/specs/base/IconCollection.spec.js b/packages/main/test/specs/base/IconCollection.spec.js deleted file mode 100644 index 703a8c5049aa..000000000000 --- a/packages/main/test/specs/base/IconCollection.spec.js +++ /dev/null @@ -1,70 +0,0 @@ -import { assert } from "chai"; - -describe("Icon collection", () => { - before(async () => { - await browser.url("test/pages/base/IconCollection.html"); - }); - - it("Tests the icon collection in built-in themes", async () => { - const result = await browser.executeAsync(done => { - const bundle = window['sap-ui-webcomponents-bundle']; - - const res = {}; - res.iconCollection = bundle.getEffectiveIconCollection(); - res.isLegacyThemeFamily = bundle.configuration.isLegacyThemeFamily(); - done(res); - }); - - // assert: "SAP-icons-v4" is used in legacy "sap_fiori_3_dark" theme - assert.strictEqual(result.iconCollection, "SAP-icons-v4", - "The 'SAP-icons-v4' collection is correctly used in 'sap_fiori_3_dark' theme"); - assert.strictEqual(result.isLegacyThemeFamily, true, - "The 'sap_fiori_3_dark' is part of legacy theme family"); - - - // act: setTheme("sap_horizon") - await browser.executeAsync(async (done) => { - await window['sap-ui-webcomponents-bundle'].configuration.setTheme("sap_horizon"); - done(); - }); - - const result2 = await browser.executeAsync(done => { - const bundle = window['sap-ui-webcomponents-bundle']; - - const res = {}; - res.iconCollection = bundle.getEffectiveIconCollection(); - res.isLegacyThemeFamily = bundle.configuration.isLegacyThemeFamily(); - done(res); - }); - - // assert: "SAP-icons-v5" is used in latest "sap_horizon" theme - assert.strictEqual(result2.iconCollection, "SAP-icons-v5", - "The 'SAP-icons-v5' collection is correctly used in 'sap_horizon' theme"); - assert.strictEqual(result2.isLegacyThemeFamily, false, - "The 'sap_horizon' is not part of legacy theme family, it's the latest one"); - }); -}); - -describe("Icon collection in Custom Theme", () => { - before(async () => { - // The test page is using custom theme (based on "sap_horizon") - await browser.url("test/pages/base/IconCollectionInCustomTheme.html"); - }); - - it("Tests the icon collection in a custom theme", async () => { - const result = await browser.executeAsync(done => { - const bundle = window['sap-ui-webcomponents-bundle']; - - const res = {}; - res.iconCollection = bundle.getEffectiveIconCollection(); - res.isLegacyThemeFamily = bundle.configuration.isLegacyThemeFamily(); - - done(res); - }); - - assert.strictEqual(result.iconCollection, "SAP-icons-v5", - "The 'SAP-icons-v5' collection is correctly used in 'redfish' - extending 'sap_horizon'"); - assert.strictEqual(result.isLegacyThemeFamily, false, - "The 'redfish' custom theme is not part of legacy theme family, as it's extending 'sap_horizon'."); - }); -}); \ No newline at end of file From bdb339b16be4b8c296eebd394f3016662122c929 Mon Sep 17 00:00:00 2001 From: kskondov Date: Mon, 16 Sep 2024 11:09:55 +0300 Subject: [PATCH 14/46] fix(ui5-popover): correct opacity (#9839) The background of the modal popover was black which made the context page not visible. So opacity was added to grey out the background Fixes: #9823 --- packages/main/src/themes/Dialog.css | 2 +- packages/main/src/themes/Popover.css | 1 + packages/main/src/themes/base/Dialog-parameters.css | 1 - packages/main/src/themes/base/PopupBlockLayer-parameters.css | 1 + .../main/src/themes/sap_fiori_3_hcb/Dialog-parameters.css | 1 - .../themes/sap_fiori_3_hcb/PopupBlockLayer-parameters.css | 5 +++++ .../main/src/themes/sap_fiori_3_hcb/parameters-bundle.css | 2 +- .../main/src/themes/sap_fiori_3_hcw/Dialog-parameters.css | 1 - .../themes/sap_fiori_3_hcw/PopupBlockLayer-parameters.css | 5 +++++ .../main/src/themes/sap_fiori_3_hcw/parameters-bundle.css | 2 +- .../main/src/themes/sap_horizon_hcb/Dialog-parameters.css | 1 - .../themes/sap_horizon_hcb/PopupBlockLayer-parameters.css | 5 +++++ .../main/src/themes/sap_horizon_hcb/parameters-bundle.css | 2 +- .../main/src/themes/sap_horizon_hcw/Dialog-parameters.css | 1 - .../themes/sap_horizon_hcw/PopupBlockLayer-parameters.css | 5 +++++ .../main/src/themes/sap_horizon_hcw/parameters-bundle.css | 2 +- 16 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 packages/main/src/themes/sap_fiori_3_hcb/PopupBlockLayer-parameters.css create mode 100644 packages/main/src/themes/sap_fiori_3_hcw/PopupBlockLayer-parameters.css create mode 100644 packages/main/src/themes/sap_horizon_hcb/PopupBlockLayer-parameters.css create mode 100644 packages/main/src/themes/sap_horizon_hcw/PopupBlockLayer-parameters.css diff --git a/packages/main/src/themes/Dialog.css b/packages/main/src/themes/Dialog.css index 8cf4720a7568..d18c6aaa2616 100644 --- a/packages/main/src/themes/Dialog.css +++ b/packages/main/src/themes/Dialog.css @@ -152,7 +152,7 @@ :host::backdrop { background-color: var(--_ui5_popup_block_layer_background); - opacity: var(--_ui5_dialog_block_layer_opacity); + opacity: var(--_ui5_popup_block_layer_opacity); } .ui5-block-layer { diff --git a/packages/main/src/themes/Popover.css b/packages/main/src/themes/Popover.css index f500d7ee0d22..2fb213b92833 100644 --- a/packages/main/src/themes/Popover.css +++ b/packages/main/src/themes/Popover.css @@ -92,6 +92,7 @@ :host([modal])::backdrop { background-color: var(--_ui5_popup_block_layer_background); + opacity: var(--_ui5_popup_block_layer_opacity); } :host([modal]) .ui5-block-layer { diff --git a/packages/main/src/themes/base/Dialog-parameters.css b/packages/main/src/themes/base/Dialog-parameters.css index 21b62b15236c..8f647b107918 100644 --- a/packages/main/src/themes/base/Dialog-parameters.css +++ b/packages/main/src/themes/base/Dialog-parameters.css @@ -9,5 +9,4 @@ --_ui5_dialog_header_success_state_icon_color: var(--sapPositiveElementColor); --_ui5_dialog_header_warning_state_icon_color: var(--sapCriticalElementColor); --_ui5_dialog_header_state_line_height: 0.0625rem; - --_ui5_dialog_block_layer_opacity: 0.2; } diff --git a/packages/main/src/themes/base/PopupBlockLayer-parameters.css b/packages/main/src/themes/base/PopupBlockLayer-parameters.css index b5b0f0165b6c..2ecf160635a1 100644 --- a/packages/main/src/themes/base/PopupBlockLayer-parameters.css +++ b/packages/main/src/themes/base/PopupBlockLayer-parameters.css @@ -1,3 +1,4 @@ :root { --_ui5_popup_block_layer_background: var(--sapBlockLayer_Background); + --_ui5_popup_block_layer_opacity: 0.2; } diff --git a/packages/main/src/themes/sap_fiori_3_hcb/Dialog-parameters.css b/packages/main/src/themes/sap_fiori_3_hcb/Dialog-parameters.css index cf7a1122cda8..dfb0ce7d1d67 100644 --- a/packages/main/src/themes/sap_fiori_3_hcb/Dialog-parameters.css +++ b/packages/main/src/themes/sap_fiori_3_hcb/Dialog-parameters.css @@ -2,5 +2,4 @@ :root { --_ui5_dialog_header_state_line_height: 0.125rem; - --_ui5_dialog_block_layer_opacity: 0.3; } \ No newline at end of file diff --git a/packages/main/src/themes/sap_fiori_3_hcb/PopupBlockLayer-parameters.css b/packages/main/src/themes/sap_fiori_3_hcb/PopupBlockLayer-parameters.css new file mode 100644 index 000000000000..22509068636d --- /dev/null +++ b/packages/main/src/themes/sap_fiori_3_hcb/PopupBlockLayer-parameters.css @@ -0,0 +1,5 @@ +@import "../base/PopupBlockLayer-parameters.css"; + +:root { + --_ui5_popup_block_layer_opacity: 0.3; +} diff --git a/packages/main/src/themes/sap_fiori_3_hcb/parameters-bundle.css b/packages/main/src/themes/sap_fiori_3_hcb/parameters-bundle.css index c4f4e0eda090..dd134115f491 100644 --- a/packages/main/src/themes/sap_fiori_3_hcb/parameters-bundle.css +++ b/packages/main/src/themes/sap_fiori_3_hcb/parameters-bundle.css @@ -30,8 +30,8 @@ @import "./MessageStrip-parameters.css"; @import "./Panel-parameters.css"; @import "../base/Popover-parameters.css"; +@import "./PopupBlockLayer-parameters.css"; @import "./PopupsCommon-parameters.css"; -@import "../base/PopupBlockLayer-parameters.css"; @import "./ProgressIndicator-parameters.css"; @import "./RadioButton-parameters.css"; @import "../base/RatingIndicator-parameters.css"; diff --git a/packages/main/src/themes/sap_fiori_3_hcw/Dialog-parameters.css b/packages/main/src/themes/sap_fiori_3_hcw/Dialog-parameters.css index cf7a1122cda8..dfb0ce7d1d67 100644 --- a/packages/main/src/themes/sap_fiori_3_hcw/Dialog-parameters.css +++ b/packages/main/src/themes/sap_fiori_3_hcw/Dialog-parameters.css @@ -2,5 +2,4 @@ :root { --_ui5_dialog_header_state_line_height: 0.125rem; - --_ui5_dialog_block_layer_opacity: 0.3; } \ No newline at end of file diff --git a/packages/main/src/themes/sap_fiori_3_hcw/PopupBlockLayer-parameters.css b/packages/main/src/themes/sap_fiori_3_hcw/PopupBlockLayer-parameters.css new file mode 100644 index 000000000000..22509068636d --- /dev/null +++ b/packages/main/src/themes/sap_fiori_3_hcw/PopupBlockLayer-parameters.css @@ -0,0 +1,5 @@ +@import "../base/PopupBlockLayer-parameters.css"; + +:root { + --_ui5_popup_block_layer_opacity: 0.3; +} diff --git a/packages/main/src/themes/sap_fiori_3_hcw/parameters-bundle.css b/packages/main/src/themes/sap_fiori_3_hcw/parameters-bundle.css index 1439474b7bbc..cba172ae4184 100644 --- a/packages/main/src/themes/sap_fiori_3_hcw/parameters-bundle.css +++ b/packages/main/src/themes/sap_fiori_3_hcw/parameters-bundle.css @@ -29,8 +29,8 @@ @import "./MessageStrip-parameters.css"; @import "./Panel-parameters.css"; @import "../base/Popover-parameters.css"; +@import "./PopupBlockLayer-parameters.css"; @import "./PopupsCommon-parameters.css"; -@import "../base/PopupBlockLayer-parameters.css"; @import "./ProgressIndicator-parameters.css"; @import "./RadioButton-parameters.css"; @import "../base/RatingIndicator-parameters.css"; diff --git a/packages/main/src/themes/sap_horizon_hcb/Dialog-parameters.css b/packages/main/src/themes/sap_horizon_hcb/Dialog-parameters.css index cf7a1122cda8..dfb0ce7d1d67 100644 --- a/packages/main/src/themes/sap_horizon_hcb/Dialog-parameters.css +++ b/packages/main/src/themes/sap_horizon_hcb/Dialog-parameters.css @@ -2,5 +2,4 @@ :root { --_ui5_dialog_header_state_line_height: 0.125rem; - --_ui5_dialog_block_layer_opacity: 0.3; } \ No newline at end of file diff --git a/packages/main/src/themes/sap_horizon_hcb/PopupBlockLayer-parameters.css b/packages/main/src/themes/sap_horizon_hcb/PopupBlockLayer-parameters.css new file mode 100644 index 000000000000..22509068636d --- /dev/null +++ b/packages/main/src/themes/sap_horizon_hcb/PopupBlockLayer-parameters.css @@ -0,0 +1,5 @@ +@import "../base/PopupBlockLayer-parameters.css"; + +:root { + --_ui5_popup_block_layer_opacity: 0.3; +} diff --git a/packages/main/src/themes/sap_horizon_hcb/parameters-bundle.css b/packages/main/src/themes/sap_horizon_hcb/parameters-bundle.css index 41ad8c74114c..2a201b2850bf 100644 --- a/packages/main/src/themes/sap_horizon_hcb/parameters-bundle.css +++ b/packages/main/src/themes/sap_horizon_hcb/parameters-bundle.css @@ -30,8 +30,8 @@ @import "./MessageStrip-parameters.css"; @import "./Panel-parameters.css"; @import "../base/Popover-parameters.css"; +@import "./PopupBlockLayer-parameters.css"; @import "./PopupsCommon-parameters.css"; -@import "../base/PopupBlockLayer-parameters.css"; @import "./ProgressIndicator-parameters"; @import "./RadioButton-parameters.css"; @import "./RatingIndicator-parameters.css"; diff --git a/packages/main/src/themes/sap_horizon_hcw/Dialog-parameters.css b/packages/main/src/themes/sap_horizon_hcw/Dialog-parameters.css index cf7a1122cda8..dfb0ce7d1d67 100644 --- a/packages/main/src/themes/sap_horizon_hcw/Dialog-parameters.css +++ b/packages/main/src/themes/sap_horizon_hcw/Dialog-parameters.css @@ -2,5 +2,4 @@ :root { --_ui5_dialog_header_state_line_height: 0.125rem; - --_ui5_dialog_block_layer_opacity: 0.3; } \ No newline at end of file diff --git a/packages/main/src/themes/sap_horizon_hcw/PopupBlockLayer-parameters.css b/packages/main/src/themes/sap_horizon_hcw/PopupBlockLayer-parameters.css new file mode 100644 index 000000000000..22509068636d --- /dev/null +++ b/packages/main/src/themes/sap_horizon_hcw/PopupBlockLayer-parameters.css @@ -0,0 +1,5 @@ +@import "../base/PopupBlockLayer-parameters.css"; + +:root { + --_ui5_popup_block_layer_opacity: 0.3; +} diff --git a/packages/main/src/themes/sap_horizon_hcw/parameters-bundle.css b/packages/main/src/themes/sap_horizon_hcw/parameters-bundle.css index edb731ba3f49..dee5aa0a7a0c 100644 --- a/packages/main/src/themes/sap_horizon_hcw/parameters-bundle.css +++ b/packages/main/src/themes/sap_horizon_hcw/parameters-bundle.css @@ -29,8 +29,8 @@ @import "./MessageStrip-parameters.css"; @import "./Panel-parameters.css"; @import "../base/Popover-parameters.css"; +@import "./PopupBlockLayer-parameters.css"; @import "./PopupsCommon-parameters.css"; -@import "../base/PopupBlockLayer-parameters.css"; @import "../sap_horizon_hcb/ProgressIndicator-parameters"; @import "./RadioButton-parameters.css"; @import "./RatingIndicator-parameters.css"; From 0373d24de7ebab7dd7c3caa24221f32aad82ed2a Mon Sep 17 00:00:00 2001 From: Nayden Naydenov <31909318+nnaydenow@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:38:38 +0300 Subject: [PATCH 15/46] chore: cleanup Cypress tests (#9877) --- .../cypress/specs/base/AriaLabelHelper.cy.ts | 69 +++++++------------ .../cypress/specs/base/IconCollection.cy.ts | 7 +- 2 files changed, 28 insertions(+), 48 deletions(-) diff --git a/packages/main/cypress/specs/base/AriaLabelHelper.cy.ts b/packages/main/cypress/specs/base/AriaLabelHelper.cy.ts index 4b018805b158..6447c7fe670d 100644 --- a/packages/main/cypress/specs/base/AriaLabelHelper.cy.ts +++ b/packages/main/cypress/specs/base/AriaLabelHelper.cy.ts @@ -16,8 +16,7 @@ describe("AriaLabelHelper", () => { cy.get("#myInput") .shadow() .find("input") - .invoke("attr", "aria-label") - .should("eq", "Desc1 Desc2 Desc3 Desc4"); + .should("have.attr", "aria-label", "Desc1 Desc2 Desc3 Desc4"); // act cy.get("#lblDesc2") @@ -30,8 +29,7 @@ describe("AriaLabelHelper", () => { cy.get("#myInput") .shadow() .find("input") - .invoke("attr", "aria-label") - .should("eq", "Desc1 Desc4"); + .should("have.attr", "aria-label", "Desc1 Desc4"); }); it("Input accessibleNameRef Tests", () => { @@ -46,8 +44,7 @@ describe("AriaLabelHelper", () => { cy.get("#inputEnterName") .shadow() .find("input") - .invoke("attr", "aria-label") - .should("eq", "FirstDesc ThirdDesc"); + .should("have.attr", "aria-label", "FirstDesc ThirdDesc"); // act - update text of referenced label cy.get("#lblEnterName1") @@ -59,8 +56,7 @@ describe("AriaLabelHelper", () => { cy.get("#inputEnterName") .shadow() .find("input") - .invoke("attr", "aria-label") - .should("eq", "First Label Desc ThirdDesc"); + .should("have.attr", "aria-label", "First Label Desc ThirdDesc"); // act - update accessible-name-ref cy.get("#inputEnterName") @@ -70,8 +66,7 @@ describe("AriaLabelHelper", () => { cy.get("#inputEnterName") .shadow() .find("input") - .invoke("attr", "aria-label") - .should("eq", "ThirdDesc First Label Desc"); + .should("have.attr", "aria-label", "ThirdDesc First Label Desc"); // act - update accessible-name-ref cy.get("#inputEnterName") @@ -81,8 +76,7 @@ describe("AriaLabelHelper", () => { cy.get("#inputEnterName") .shadow() .find("input") - .invoke("attr", "aria-label") - .should("eq", "SecondDesc"); + .should("have.attr", "aria-label", "SecondDesc"); }); it("Input accessibleName and accessibleNameRef Tests", () => { @@ -104,8 +98,7 @@ describe("AriaLabelHelper", () => { // assert cy.get("@input") - .invoke("attr", "aria-label") - .should("eq", INITIAL_ACCESSIBLE_NAME); + .should("have.attr", "aria-label", INITIAL_ACCESSIBLE_NAME); cy.get("#inputEnterDesc") .invoke("attr", "accessible-name", UPDATED_ACCESSIBLE_NAME); @@ -130,8 +123,7 @@ describe("AriaLabelHelper", () => { // assert - the text of the elment labelled with accessible-name-ref is used cy.get("@input") - .invoke("attr", "aria-label") - .should("eq", ACCESSIBLE_NAME_REF_TEXT); + .should("have.attr", "aria-label", ACCESSIBLE_NAME_REF_TEXT); // act - add acccessible-name once again cy.get("#inputEnterDesc") @@ -139,8 +131,7 @@ describe("AriaLabelHelper", () => { // assert - the text of the elment labelled with accessible-name-ref is still used cy.get("@input") - .invoke("attr", "aria-label") - .should("eq", ACCESSIBLE_NAME_REF_TEXT); + .should("have.attr", "aria-label", ACCESSIBLE_NAME_REF_TEXT); // act - remove acccessible-name-ref cy.get("#inputEnterDesc") @@ -148,8 +139,7 @@ describe("AriaLabelHelper", () => { // assert - after acccessible-name-ref is removed, fallbacks to use acccessible-name cy.get("@input") - .invoke("attr", "aria-label") - .should("eq", INITIAL_ACCESSIBLE_NAME); + .should("have.attr", "aria-label", INITIAL_ACCESSIBLE_NAME); // act - remove acccessible-name cy.get("#inputEnterDesc") @@ -157,8 +147,7 @@ describe("AriaLabelHelper", () => { // assert - aria-label fallbacks to use the label's for, pointing to this input cy.get("@input") - .invoke("attr", "aria-label") - .should("eq", "Label for inputEnterDesc"); + .should("have.attr", "aria-label", "Label for inputEnterDesc"); // act - remove ui5-label's for cy.get("#lblEnterDesc1") @@ -166,8 +155,7 @@ describe("AriaLabelHelper", () => { // assert - aria-label is undefined cy.get("@input") - .invoke("attr", "aria-label") - .should("eq", undefined); + .should("not.have.attr", "aria-label"); }); it("Three inputs with same label accessibleNameRef Tests", () => { @@ -198,16 +186,13 @@ describe("AriaLabelHelper", () => { // assert cy.get("@input1") - .invoke("attr", "aria-label") - .should("eq", LBL_TEXT_CONTENT); + .should("have.attr", "aria-label", LBL_TEXT_CONTENT); cy.get("@input2") - .invoke("attr", "aria-label") - .should("eq", LBL_TEXT_CONTENT); + .should("have.attr", "aria-label", LBL_TEXT_CONTENT); cy.get("@input3") - .invoke("attr", "aria-label") - .should("eq", LBL_TEXT_CONTENT); + .should("have.attr", "aria-label", LBL_TEXT_CONTENT); // act @@ -218,16 +203,13 @@ describe("AriaLabelHelper", () => { // assert cy.get("@input1") - .invoke("attr", "aria-label") - .should("eq", LBL_TEXT_CONTENT_UPDATED); + .should("have.attr", "aria-label", LBL_TEXT_CONTENT_UPDATED); cy.get("@input2") - .invoke("attr", "aria-label") - .should("eq", LBL_TEXT_CONTENT_UPDATED); + .should("have.attr", "aria-label", LBL_TEXT_CONTENT_UPDATED); cy.get("@input3") - .invoke("attr", "aria-label") - .should("eq", LBL_TEXT_CONTENT_UPDATED); + .should("have.attr", "aria-label", LBL_TEXT_CONTENT_UPDATED); // act - remove "for" attribute cy.get("#lblTestDesc") @@ -235,8 +217,7 @@ describe("AriaLabelHelper", () => { // assert - aria-label is undefined cy.get("@input1") - .invoke("attr", "aria-label") - .should("eq", undefined); + .should("not.have.attr", "aria-label"); // act - remove accessible-name-ref cy.get("#testInput2") @@ -244,8 +225,7 @@ describe("AriaLabelHelper", () => { // assert - aria-label is the existing accessible-name cy.get("@input2") - .invoke("attr", "aria-label") - .should("eq", "Hello"); + .should("have.attr", "aria-label", "Hello"); // act - remove accessible-name-ref cy.get("#testInput3") @@ -253,8 +233,7 @@ describe("AriaLabelHelper", () => { // assert - shouldn't be any aria-label cy.get("@input3") - .invoke("attr", "aria-label") - .should("eq", undefined); + .should("not.have.attr", "aria-label"); }); it("Tests generic html elements with for attribute", () => { @@ -274,8 +253,7 @@ describe("AriaLabelHelper", () => { // assert cy.get("@input") - .invoke("attr", "aria-label") - .should("eq", "Desc1 Desc2 Desc3 Desc4 Desc5"); + .should("have.attr", "aria-label", "Desc1 Desc2 Desc3 Desc4 Desc5"); // act cy.get("#elId1") @@ -299,7 +277,6 @@ describe("AriaLabelHelper", () => { // assert cy.get("@input") - .invoke("attr", "aria-label") - .should("eq", "Desc1X Desc4X"); + .should("have.attr", "aria-label", "Desc1X Desc4X"); }); }); diff --git a/packages/main/cypress/specs/base/IconCollection.cy.ts b/packages/main/cypress/specs/base/IconCollection.cy.ts index 8fe2f07e7f75..2716fa411083 100644 --- a/packages/main/cypress/specs/base/IconCollection.cy.ts +++ b/packages/main/cypress/specs/base/IconCollection.cy.ts @@ -5,9 +5,12 @@ import { html } from "lit"; import "../../../src/Assets.js"; import "../../../src/Icon.js"; -setTheme("sap_fiori_3_dark"); - describe("Icon collection", () => { + before(() => { + cy.wrap({ setTheme }) + .invoke("setTheme", "sap_fiori_3_dark"); + }); + it("tests the icon collection in built-in themes", () => { cy.mount(html``); From 989923701812d52eff3d65df8311d38b88e54682 Mon Sep 17 00:00:00 2001 From: Petar Dimov <32839090+dimovpetar@users.noreply.github.com> Date: Mon, 16 Sep 2024 22:59:31 +0300 Subject: [PATCH 16/46] test(ui5-tabcontainer): stabilize test (#9883) --- packages/main/test/pageobjects/TabContainerTestPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/main/test/pageobjects/TabContainerTestPage.js b/packages/main/test/pageobjects/TabContainerTestPage.js index 6523f4fbfdbe..8ac77294d6bd 100644 --- a/packages/main/test/pageobjects/TabContainerTestPage.js +++ b/packages/main/test/pageobjects/TabContainerTestPage.js @@ -58,8 +58,8 @@ class TabContainerTestPage { } async focusItem(tabId) { - await browser.executeAsync((tabId, done) => { - document.getElementById(tabId).focus(); + await browser.executeAsync(async (tabId, done) => { + await document.getElementById(tabId).focus(); done(); }, tabId); } From 1473cf5a394ca7d52f86b6457105fd0339440ac8 Mon Sep 17 00:00:00 2001 From: Vladislav Tasev Date: Tue, 17 Sep 2024 11:05:02 +0300 Subject: [PATCH 17/46] chore: export InvalidationInfo type (#9881) --- packages/base/src/UI5Element.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/base/src/UI5Element.ts b/packages/base/src/UI5Element.ts index 472f227d6e82..0b6571360e52 100644 --- a/packages/base/src/UI5Element.ts +++ b/packages/base/src/UI5Element.ts @@ -1293,6 +1293,7 @@ export { }; export type { ChangeInfo, + InvalidationInfo, Renderer, RendererOptions, }; From 44caf0d942c143d2580b093a7faecca1d4f2bf68 Mon Sep 17 00:00:00 2001 From: Siyana Todorova <72251110+s-todorova@users.noreply.github.com> Date: Tue, 17 Sep 2024 11:27:14 +0300 Subject: [PATCH 18/46] docs(ui5-dialog): improve basic sample (#9876) The samples now showcase achieving a confirmation dialog with two buttons like in opneui5, by using ui5-toolbar and ui5-toolbar-buttons. BGSOFUIRODOPI-3324 --- packages/main/src/themes/Dialog.css | 4 ++ packages/main/test/pages/Dialog.html | 49 +++++++++++++++++++ .../docs/_samples/main/Dialog/Basic/main.js | 2 + .../_samples/main/Dialog/Basic/sample.html | 18 +++++-- .../main/Dialog/DraggableAndResizable/main.js | 2 + .../Dialog/DraggableAndResizable/sample.html | 6 +-- .../_samples/main/Dialog/WithState/main.js | 2 + .../main/Dialog/WithState/sample.html | 6 +-- 8 files changed, 79 insertions(+), 10 deletions(-) diff --git a/packages/main/src/themes/Dialog.css b/packages/main/src/themes/Dialog.css index d18c6aaa2616..6105faad10ca 100644 --- a/packages/main/src/themes/Dialog.css +++ b/packages/main/src/themes/Dialog.css @@ -150,6 +150,10 @@ box-shadow: none; } +::slotted([slot="footer"][ui5-toolbar]) { + border: 0; +} + :host::backdrop { background-color: var(--_ui5_popup_block_layer_background); opacity: var(--_ui5_popup_block_layer_opacity); diff --git a/packages/main/test/pages/Dialog.html b/packages/main/test/pages/Dialog.html index 634b5ba85585..46b0f9a564f2 100644 --- a/packages/main/test/pages/Dialog.html +++ b/packages/main/test/pages/Dialog.html @@ -615,6 +615,43 @@ Close +
+
+ Confirmation dialog with Toolbar + +
+
+ Username + +
+
+ Password + +
+
+ Email + +
+
+ Address + +
+
+ + + + + + +


@@ -863,6 +900,18 @@ window["dialogAutofocus"].open = false; }); + window["btnConfirmationDialog"].addEventListener("click", function () { + window["confirmationDialog"].open = true; + }); + + window["btnConfirmationDialogSubmit"].addEventListener("click", function () { + window["confirmationDialog"].open = false; + }) + + window["btnConfirmationDialogCancel"].addEventListener("click", function () { + window["confirmationDialog"].open = false; + }) + document.getElementById("theme-switch").addEventListener("ui5-selection-change", event => { window["sap-ui-webcomponents-bundle"].configuration.setTheme(event.detail.selectedItems[0].getAttribute("data-ui5-theme-name")); }); diff --git a/packages/website/docs/_samples/main/Dialog/Basic/main.js b/packages/website/docs/_samples/main/Dialog/Basic/main.js index 14839550a965..e8b57d5341fa 100644 --- a/packages/website/docs/_samples/main/Dialog/Basic/main.js +++ b/packages/website/docs/_samples/main/Dialog/Basic/main.js @@ -3,6 +3,8 @@ import "@ui5/webcomponents/dist/Button.js"; import "@ui5/webcomponents/dist/Input.js"; import "@ui5/webcomponents/dist/Link.js"; import "@ui5/webcomponents/dist/Label.js"; +import "@ui5/webcomponents/dist/Toolbar.js" +import "@ui5/webcomponents/dist/ToolbarButton.js" var dialogOpener = document.getElementById("dialogOpener"); var dialog = document.getElementById("dialog"); diff --git a/packages/website/docs/_samples/main/Dialog/Basic/sample.html b/packages/website/docs/_samples/main/Dialog/Basic/sample.html index 4109b14a7aef..29f7fd4b3df6 100644 --- a/packages/website/docs/_samples/main/Dialog/Basic/sample.html +++ b/packages/website/docs/_samples/main/Dialog/Basic/sample.html @@ -33,10 +33,20 @@
-
-
- Register -
+ + + + + + diff --git a/packages/website/docs/_samples/main/Dialog/DraggableAndResizable/main.js b/packages/website/docs/_samples/main/Dialog/DraggableAndResizable/main.js index f08a72998313..5c2eb0c38497 100644 --- a/packages/website/docs/_samples/main/Dialog/DraggableAndResizable/main.js +++ b/packages/website/docs/_samples/main/Dialog/DraggableAndResizable/main.js @@ -1,5 +1,7 @@ import "@ui5/webcomponents/dist/Dialog.js"; import "@ui5/webcomponents/dist/Button.js"; +import "@ui5/webcomponents/dist/Toolbar.js"; +import "@ui5/webcomponents/dist/ToolbarButton.js"; var dialogOpener = document.getElementById("dialogOpener"); var dialog = document.getElementById("dialog"); diff --git a/packages/website/docs/_samples/main/Dialog/DraggableAndResizable/sample.html b/packages/website/docs/_samples/main/Dialog/DraggableAndResizable/sample.html index 3e53385b294f..f3826e330bd1 100644 --- a/packages/website/docs/_samples/main/Dialog/DraggableAndResizable/sample.html +++ b/packages/website/docs/_samples/main/Dialog/DraggableAndResizable/sample.html @@ -17,9 +17,9 @@

Move this dialog around the screen by dragging it by its header.

Resize this dialog by dragging it by its resize handle.

These features are available only on Desktop.

-
- OK -
+ + + diff --git a/packages/website/docs/_samples/main/Dialog/WithState/main.js b/packages/website/docs/_samples/main/Dialog/WithState/main.js index 3c3255b2463a..41a8572acb9d 100644 --- a/packages/website/docs/_samples/main/Dialog/WithState/main.js +++ b/packages/website/docs/_samples/main/Dialog/WithState/main.js @@ -1,6 +1,8 @@ import "@ui5/webcomponents/dist/Dialog.js"; import "@ui5/webcomponents/dist/Button.js"; import "@ui5/webcomponents/dist/Text.js"; +import "@ui5/webcomponents/dist/Toolbar.js"; +import "@ui5/webcomponents/dist/ToolbarButton.js"; var dialogOpener = document.getElementById("dialogOpener"); var dialog = document.getElementById("dialog"); diff --git a/packages/website/docs/_samples/main/Dialog/WithState/sample.html b/packages/website/docs/_samples/main/Dialog/WithState/sample.html index fb1cbc5617d4..d3ddb7ced209 100644 --- a/packages/website/docs/_samples/main/Dialog/WithState/sample.html +++ b/packages/website/docs/_samples/main/Dialog/WithState/sample.html @@ -15,9 +15,9 @@ Dialog with state -
- Close -
+ + +
From d4bf6143147197408ab1671b219ba58c0e35a9a4 Mon Sep 17 00:00:00 2001 From: Ivaylo Plashkov Date: Tue, 17 Sep 2024 13:02:26 +0300 Subject: [PATCH 19/46] feat(selectionAssistant): introduce SelectionAssistant (#9797) * feat(selectionAssistant): introdce selection assistant to cover AI scenarios * feat(selectionAssistant): minor improvements * feat(selectionAssistant): minor improvements * feat(selectionAssistant): apply feedback * feat(selectionAssistant): correct lint * feat(selectionAssistant): apply feedback * feat(selectionAssistant): correct lint --- packages/base/src/util/SelectionAssistant.ts | 112 ++++++++++++++++ packages/main/src/bundle.common.bootstrap.ts | 2 + .../test/pages/Input-SelectionAssistant.html | 126 ++++++++++++++++++ .../pages/TextArea-SelectionAssistant.html | 99 ++++++++++++++ .../InputSelectionAssistant/Basic/Basic.md | 4 + .../InputSelectionAssistant/Basic/main.js | 90 +++++++++++++ .../InputSelectionAssistant/Basic/sample.html | 39 ++++++ .../Selection Assistant.mdx | 24 ++++ .../TextAreaSelectionAssistant/Basic/Basic.md | 4 + .../TextAreaSelectionAssistant/Basic/main.js | 70 ++++++++++ .../Basic/sample.html | 31 +++++ 11 files changed, 601 insertions(+) create mode 100644 packages/base/src/util/SelectionAssistant.ts create mode 100644 packages/main/test/pages/Input-SelectionAssistant.html create mode 100644 packages/main/test/pages/TextArea-SelectionAssistant.html create mode 100644 packages/website/docs/components/patterns/SelectionAssistant/InputSelectionAssistant/Basic/Basic.md create mode 100644 packages/website/docs/components/patterns/SelectionAssistant/InputSelectionAssistant/Basic/main.js create mode 100644 packages/website/docs/components/patterns/SelectionAssistant/InputSelectionAssistant/Basic/sample.html create mode 100644 packages/website/docs/components/patterns/SelectionAssistant/Selection Assistant.mdx create mode 100644 packages/website/docs/components/patterns/SelectionAssistant/TextAreaSelectionAssistant/Basic/Basic.md create mode 100644 packages/website/docs/components/patterns/SelectionAssistant/TextAreaSelectionAssistant/Basic/main.js create mode 100644 packages/website/docs/components/patterns/SelectionAssistant/TextAreaSelectionAssistant/Basic/sample.html diff --git a/packages/base/src/util/SelectionAssistant.ts b/packages/base/src/util/SelectionAssistant.ts new file mode 100644 index 000000000000..51f43fbae81e --- /dev/null +++ b/packages/base/src/util/SelectionAssistant.ts @@ -0,0 +1,112 @@ +import getEffectiveScrollbarStyle from "../util/getEffectiveScrollbarStyle.js"; + +const copyAndApplyStyles = (element: HTMLElement, copiedElement: HTMLElement) => { + const computedStyles = getComputedStyle(element); + + for (let i = 0; i < computedStyles.length; i++) { + const propertyName = computedStyles[i]; + copiedElement.style.setProperty(propertyName, computedStyles.getPropertyValue(propertyName)); + } + + element.tagName === "INPUT" && setInputSpecificStyles(copiedElement); + + copiedElement.style.position = "absolute"; + copiedElement.style.left = `${element.getBoundingClientRect().left}px`; + copiedElement.style.top = `${element.getBoundingClientRect().top}px`; + + setUnInteractableStyles(copiedElement); + + document.body.appendChild(copiedElement); +}; + +const setUnInteractableStyles = (element: HTMLElement) => { + element.style.position = "absolute"; + element.style.userSelect = "none"; + element.style.pointerEvents = "none"; + element.style.zIndex = "-1"; + element.style.opacity = "0"; +}; + +const setInputSpecificStyles = (element: HTMLElement) => { + element.style.whiteSpace = "nowrap"; + element.style.overflowX = "auto"; + element.style.overflowY = "hidden"; +}; + +const applyScrollStylings = () => { + const sheet = new CSSStyleSheet(); + const styles = getEffectiveScrollbarStyle(); + + sheet.replaceSync(styles); + + document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet]; +}; + +const createCopy = () => { + const copiedElement = document.createElement("div"); + + copiedElement.id = "ui5-selection-mirror"; + copiedElement.contentEditable = "true"; + + applyScrollStylings(); + + document.body.appendChild(copiedElement); +}; + +const applyScrollPosition = (element: HTMLElement, copiedElement: HTMLElement) => { + copiedElement.scrollTop = element.scrollTop; + copiedElement.scrollLeft = element.scrollLeft; +}; + +const getSelectionCoordinates = (element: HTMLInputElement | HTMLTextAreaElement, mirror: HTMLDivElement) => { + const { selectionStart, selectionEnd } = element; + const selectedText = element.value.slice( + selectionStart!, + element.selectionEnd!, + ); + const range = document.createRange(); + + range.setStart(mirror.firstChild!, selectionEnd! - 1); + range.setEnd(mirror.firstChild!, selectionEnd!); + + applyScrollPosition(element, mirror); + + const rangeRect = range.getBoundingClientRect(); + const rectObject = { + x: rangeRect.x, + y: rangeRect.y, + width: rangeRect.width, + height: rangeRect.height, + top: rangeRect.top, + right: rangeRect.right, + bottom: rangeRect.bottom, + left: rangeRect.left, + }; + + document.body.removeChild(mirror); + + return { ...rectObject, selectedText }; +}; + +const getElementSelection = (element: HTMLElement) => { + const innerElement = element.shadowRoot!.querySelector("textarea") + || element.shadowRoot!.querySelector("input"); + + if (!document.getElementById("ui5-selection-mirror")) { + createCopy(); + } + + const copiedElement = document.getElementById( + "ui5-selection-mirror", + )!; + + copiedElement.textContent = innerElement!.value; + + if (innerElement) { + copyAndApplyStyles(innerElement, copiedElement)!; + } + + return getSelectionCoordinates(innerElement!, copiedElement as HTMLDivElement); +}; + +export default getElementSelection; diff --git a/packages/main/src/bundle.common.bootstrap.ts b/packages/main/src/bundle.common.bootstrap.ts index 1e9441ac95a9..810655cf1d79 100644 --- a/packages/main/src/bundle.common.bootstrap.ts +++ b/packages/main/src/bundle.common.bootstrap.ts @@ -73,6 +73,7 @@ import { attachDirectionChange } from "@ui5/webcomponents-base/dist/locale/direc import ResizeHandler from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js"; import announce from "@ui5/webcomponents-base/dist/util/InvisibleMessage.js"; import { ignoreCustomElements, shouldIgnoreCustomElement } from "@ui5/webcomponents-base/dist/IgnoreCustomElements.js"; +import getElementSelection from "@ui5/webcomponents-base/dist/util/SelectionAssistant.js"; import * as defaultTexts from "./generated/i18n/i18n-defaults.js"; @@ -106,6 +107,7 @@ const testAssets = { invisibleMessage: { announce, }, + getElementSelection, getLocaleData, applyDirection, attachDirectionChange, diff --git a/packages/main/test/pages/Input-SelectionAssistant.html b/packages/main/test/pages/Input-SelectionAssistant.html new file mode 100644 index 000000000000..0dea6bafaeaf --- /dev/null +++ b/packages/main/test/pages/Input-SelectionAssistant.html @@ -0,0 +1,126 @@ + + + + + + + + Input-SelectionAssisstant + + + + + Input with selection assistant. +
+ +
+
+ + Input with native API. +
+ + + + + + + + + \ No newline at end of file diff --git a/packages/main/test/pages/TextArea-SelectionAssistant.html b/packages/main/test/pages/TextArea-SelectionAssistant.html new file mode 100644 index 000000000000..d153b17dec03 --- /dev/null +++ b/packages/main/test/pages/TextArea-SelectionAssistant.html @@ -0,0 +1,99 @@ + + + + + + + + TextArea-SelectionAssisstant + + + + + TextArea with Selection Assistant. +
+ + + + + + + + + \ No newline at end of file diff --git a/packages/website/docs/components/patterns/SelectionAssistant/InputSelectionAssistant/Basic/Basic.md b/packages/website/docs/components/patterns/SelectionAssistant/InputSelectionAssistant/Basic/Basic.md new file mode 100644 index 000000000000..17798ecc59ab --- /dev/null +++ b/packages/website/docs/components/patterns/SelectionAssistant/InputSelectionAssistant/Basic/Basic.md @@ -0,0 +1,4 @@ +import html from '!!raw-loader!./sample.html'; +import js from '!!raw-loader!./main.js'; + + diff --git a/packages/website/docs/components/patterns/SelectionAssistant/InputSelectionAssistant/Basic/main.js b/packages/website/docs/components/patterns/SelectionAssistant/InputSelectionAssistant/Basic/main.js new file mode 100644 index 000000000000..b62f6a161d9a --- /dev/null +++ b/packages/website/docs/components/patterns/SelectionAssistant/InputSelectionAssistant/Basic/main.js @@ -0,0 +1,90 @@ +import '@ui5/webcomponents/dist/Icon.js'; +import '@ui5/webcomponents/dist/Label.js'; +import '@ui5/webcomponents/dist/Input.js'; +import '@ui5/webcomponents/dist/Text.js'; +import '@ui5/webcomponents/dist/Button.js'; +import '@ui5/webcomponents/dist/Toast.js'; +import '@ui5/webcomponents-icons/dist/ai.js'; +import getElementSelection from "@ui5/webcomponents-base/dist/util/SelectionAssistant.js"; + +const nativeInput = document.getElementById('ai-native-input'); +const input = document.getElementById('ai-input'); +const button = document.getElementById('btn'); +const toast = [...document.getElementsByTagName("ui5-toast")][0]; + +const repositionButtonAtSelection = (rect) => { + button.style.left = `${rect.left + rect.width}px`; + button.style.top = `${rect.top + rect.height}px`; + showButton(); +}; + +const repositionButtonAtInput = (rect) => { + button.style.left = `${rect.left + rect.width + 4}px`; + button.style.top = `${rect.top}px`; + showButton(); +}; + +const showButton = () => { + button.style.zIndex = '100'; + button.style.display = 'inline-block'; +}; + +const hideButton = () => { + button.style.display = 'none'; +}; + +input.addEventListener('ui5-select', (e) => { + const selectionRect = getElementSelection(input); + const inputRect = input.getBoundingClientRect(); + + if (selectionRect.bottom > inputRect.bottom || selectionRect.right > inputRect.right) { + repositionButtonAtInput(inputRect); + } else { + repositionButtonAtSelection(selectionRect); + } +}); + +input.addEventListener('mousedown', (e) => { + hideButton(); +}); + +input.addEventListener('ui5-scroll', (e) => { + hideButton(); +}); + +input.addEventListener('focusout', (e) => { + if (e.relatedTarget !== button) { + hideButton(); + } +}); + +nativeInput.addEventListener('ui5-select', (e) => { + const inputRect = nativeInput.getBoundingClientRect(); + repositionButtonAtInput(inputRect); +}); + +nativeInput.addEventListener('click', (e) => { + hideButton(); +}); + +nativeInput.addEventListener('ui5-scroll', (e) => { + hideButton(); +}); + +nativeInput.addEventListener('focusout', (e) => { + if (e.relatedTarget !== button) { + hideButton(); + } +}); + +button.addEventListener('focusout', (e) => { + hideButton(); +}); + +button.addEventListener('click', (e) => { + const selectedText = document.getSelection().toString(); + const message = `The selected text equals to: "${selectedText}"`; + + toast.textContent = message; + toast.open = true; +}); \ No newline at end of file diff --git a/packages/website/docs/components/patterns/SelectionAssistant/InputSelectionAssistant/Basic/sample.html b/packages/website/docs/components/patterns/SelectionAssistant/InputSelectionAssistant/Basic/sample.html new file mode 100644 index 000000000000..793e9ec5bef8 --- /dev/null +++ b/packages/website/docs/components/patterns/SelectionAssistant/InputSelectionAssistant/Basic/sample.html @@ -0,0 +1,39 @@ + + + + + + + + + Sample + + + + + + Input with selection assistant. +
+ +
+
+ + Input with native API. +
+ + + + + + + + + + \ No newline at end of file diff --git a/packages/website/docs/components/patterns/SelectionAssistant/Selection Assistant.mdx b/packages/website/docs/components/patterns/SelectionAssistant/Selection Assistant.mdx new file mode 100644 index 000000000000..210f8ed98cf1 --- /dev/null +++ b/packages/website/docs/components/patterns/SelectionAssistant/Selection Assistant.mdx @@ -0,0 +1,24 @@ +--- +sidebar_class_name: newComponentBadge expComponentBadge +--- + +import Input from "../../../components/patterns/SelectionAssistant/InputSelectionAssistant/Basic/Basic.md"; +import TextArea from "../../../components/patterns/SelectionAssistant/TextAreaSelectionAssistant/Basic/Basic.md"; + +**Note:** The util is in an experimental state + +### ES6 Module Import + +`import getElementSelection from "@ui5/webcomponents-base/dist/util/SelectionAssistant.js";` + +The sample demonstrates the usage of SelectionAssistant, which enhances user interactions by +returning the +coordinates of the selected text on the select event. This utility enables developers to create advanced +AI-powered scenarios by easily capturing and utilizing the exact coordinates of user-selected text. + +## Input with Selection Assistant sample + + +## TextArea with Selection Assistant sample +