From de069411e0d4e523c44ff03db84faf101474b89e Mon Sep 17 00:00:00 2001 From: ec1467 <190760328+ec1467@users.noreply.github.com> Date: Fri, 13 Dec 2024 15:11:08 +0000 Subject: [PATCH 1/2] feat(canary-web-components): added action-element, tests and stories --- .../ic-data-table/ic-data-table.css | 10 + .../ic-data-table/ic-data-table.stories.mdx | 11 + .../ic-data-table/ic-data-table.tsx | 248 +++++++++++------- .../components/ic-data-table/story-data.ts | 53 ++++ .../__snapshots__/ic-data-table.spec.tsx.snap | 216 +++++++++++++++ .../test/basic/ic-data-table.spec.tsx | 47 ++++ 6 files changed, 488 insertions(+), 97 deletions(-) diff --git a/packages/canary-web-components/src/components/ic-data-table/ic-data-table.css b/packages/canary-web-components/src/components/ic-data-table/ic-data-table.css index 0cd280422b..b4e59b7d25 100644 --- a/packages/canary-web-components/src/components/ic-data-table/ic-data-table.css +++ b/packages/canary-web-components/src/components/ic-data-table/ic-data-table.css @@ -374,6 +374,16 @@ td.table-density-spacious { width: 100%; } +.action-element { + display: flex; + justify-content: right; +} + +.cell-grid-wrapper { + display: grid; + grid-template-columns: auto auto; +} + @media screen and (min-width: 577px) { .column-header-inner-container { display: flex; diff --git a/packages/canary-web-components/src/components/ic-data-table/ic-data-table.stories.mdx b/packages/canary-web-components/src/components/ic-data-table/ic-data-table.stories.mdx index 834ed74a42..114b613a75 100644 --- a/packages/canary-web-components/src/components/ic-data-table/ic-data-table.stories.mdx +++ b/packages/canary-web-components/src/components/ic-data-table/ic-data-table.stories.mdx @@ -36,6 +36,7 @@ import { UpdatingData, SlottedPagination, LargeDataSet, + ActionElement, DevArea, } from "./story-data"; import readme from "./readme.md"; @@ -1644,6 +1645,16 @@ To set the min and max width of a table cell, set the `table-layout` attribute t ``` +### Action Element + +The example below demonstrates action elements appearing in table cells after being passed in via the data. + + + + {ActionElement()} + + + #### Development Area diff --git a/packages/canary-web-components/src/components/ic-data-table/ic-data-table.tsx b/packages/canary-web-components/src/components/ic-data-table/ic-data-table.tsx index eaef433860..5ac74863bc 100644 --- a/packages/canary-web-components/src/components/ic-data-table/ic-data-table.tsx +++ b/packages/canary-web-components/src/components/ic-data-table/ic-data-table.tsx @@ -393,6 +393,7 @@ export class DataTable { componentDidRender(): void { this.fixCellTooltips(); + this.adjustWidthForActionElement(); } private runHeaderResizeObserver = () => { @@ -1228,6 +1229,126 @@ export class DataTable { return {}; }; + private adjustWidthForActionElement = () => { + const elements = this.el.shadowRoot.querySelectorAll(".action-element"); + elements.forEach((element) => { + const width = (element.firstChild as HTMLElement).getBoundingClientRect() + .width; + const gridWrapper: HTMLElement = element.closest(".cell-grid-wrapper"); + gridWrapper.style.gridTemplateColumns = `auto calc(${width}px + var(--ic-space-xs))`; + }); + }; + + private createCellContent = ( + columnProps: IcDataTableColumnObject, + cell: any, + cellSlotName: string, + rowOptions: any, + rowAlignment: string, + hasIcon: boolean, + currentRowHeight: number, + cellValue: (key: string) => any, + rowEmphasis: string + ) => ( + + {isSlotUsed(this.el, cellSlotName) ? ( + + ) : ( + + {isSlotUsed(this.el, `${cellSlotName}-icon`) ? ( + + ) : ( + (hasIcon || columnProps?.icon?.onAllCells) && + (cellValue("icon") || columnProps?.icon?.icon) && ( + + ) + )} + {columnProps?.dataType !== "element" && + !isSlotUsed(this.el, cellSlotName) && ( + + {this.isObject(cell) && columnProps?.dataType !== "date" ? ( + Object.keys(cell).includes("href") ? ( + + {cellValue("data")} + + ) : ( + cellValue("data") + ) + ) : ( + this.getCellContent(cell, columnProps?.dataType) + )} + + )} + + )} + + ); + private createCells = (row: IcDataTableDataType, rowIndex: number) => { const rowValues = Object.values(row); const rowKeys = Object.keys(row); @@ -1286,104 +1407,37 @@ export class DataTable { }} style={{ ...this.getColumnWidth(columnProps.columnWidth) }} > - + {this.createCellContent( columnProps, - rowOptions?.textWrap, - cell - ), - ...this.getColumnWidth(columnProps?.columnWidth), - }} - data-row-height={ - this.truncationPattern || currentRowHeight - ? this.setRowHeight(currentRowHeight) - : null - } - > - {isSlotUsed(this.el, cellSlotName) ? ( - - ) : ( - - {isSlotUsed(this.el, `${cellSlotName}-icon`) ? ( - - ) : ( - (hasIcon || columnProps?.icon?.onAllCells) && - (cellValue("icon") || columnProps?.icon?.icon) && ( - - ) - )} - {columnProps?.dataType !== "element" && - !isSlotUsed(this.el, cellSlotName) && ( - - {this.isObject(cell) && - columnProps?.dataType !== "date" ? ( - Object.keys(cell).includes("href") ? ( - - {cellValue("data")} - - ) : ( - cellValue("data") - ) - ) : ( - this.getCellContent(cell, columnProps?.dataType) - )} - - )} - - )} - + cell, + cellSlotName, + rowOptions, + rowAlignment, + hasIcon, + currentRowHeight, + cellValue, + rowEmphasis + )} + + + ) : ( + this.createCellContent( + columnProps, + cell, + cellSlotName, + rowOptions, + rowAlignment, + hasIcon, + currentRowHeight, + cellValue, + rowEmphasis + ) + )} ); } diff --git a/packages/canary-web-components/src/components/ic-data-table/story-data.ts b/packages/canary-web-components/src/components/ic-data-table/story-data.ts index e5bb7847cb..7337b4a490 100644 --- a/packages/canary-web-components/src/components/ic-data-table/story-data.ts +++ b/packages/canary-web-components/src/components/ic-data-table/story-data.ts @@ -960,6 +960,56 @@ export const DATA_REACT_ELEMENTS_WITH_ICONS = [ }, ]; +export const ACTION_DATA_ELEMENTS = [ + { + firstName: { + data: "Joe", + actionElement: ` `, + }, + lastName: "Bloggs", + age: 31, + jobTitle: "Developer", + address: "1 Main Street, Town, County, Postcode", + }, + { + firstName: "Sarah", + lastName: "Jane", + age: 28, + jobTitle: { + data: "Senior Software Developer, Site Reliability Engineering", + actionElement: ``, + }, + address: "2 Main Street, Town, Country, Postcode", + }, + { + firstName: "Mark", + lastName: "Smith", + age: { + data: 45, + actionElement: ` `, + }, + jobTitle: "Team Lead", + address: "12 Key Street, Town, Country, Postcode", + }, + { + firstName: "Naomi", + lastName: "Kens", + age: 32, + jobTitle: "Analyst", + address: "8 Side Street, Town, Country, Postcode", + }, + { + firstName: "Luke", + lastName: "Sky", + age: 18, + jobTitle: "Junior Developer", + address: { + data: "5 New Street, Town, Country, Postcode", + actionElement: ``, + }, + }, +]; + export const createDataTableElement = ( caption: string, columns: IcDataTableColumnObject[] = COLS, @@ -1417,6 +1467,9 @@ export const SlottedPagination = (): HTMLIcDataTableElement => { return dataTable; }; +export const ActionElement = (): HTMLElement => + createDataTableElement("Action Element", COLS, ACTION_DATA_ELEMENTS); + export const DevArea = (): HTMLElement => { const dataTable = createDataTableElement( "Basic Table", diff --git a/packages/canary-web-components/src/components/ic-data-table/test/basic/__snapshots__/ic-data-table.spec.tsx.snap b/packages/canary-web-components/src/components/ic-data-table/test/basic/__snapshots__/ic-data-table.spec.tsx.snap index 67eef922f9..f68c7aad60 100644 --- a/packages/canary-web-components/src/components/ic-data-table/test/basic/__snapshots__/ic-data-table.spec.tsx.snap +++ b/packages/canary-web-components/src/components/ic-data-table/test/basic/__snapshots__/ic-data-table.spec.tsx.snap @@ -2747,6 +2747,222 @@ exports[`ic-data-table should render a slotted icon instead of an icon defined i `; +exports[`ic-data-table should render an action element if present in the data set 1`] = ` + + + + + + + test table + + + + + + + Name + + + + + + + Age + + + + + + + Department + + + + + + + Employee number + + + + + + + + + 1 + + + + + + John Smith + + + + + + + + + + + + + + + + 36 + + + + + + + Accounts + + + + + + + 2 + + + + + Sally Jones + + + + + + + 32 + + + + + + + Engineering + + + + + + + 3 + + + + + Tim Rayes + + + + + + + 41 + + + + + + + Sales + + + + + + + 4 + + + + + Luke Fisher + + + + + + + 23 + + + + + + + Engineering + + + + + + + 5 + + + + + Jane Lock + + + + + + + 34 + + + + + + + Engineering + + + + + + + 6 + + + + + Margaret Hale + + + + + + + 45 + + + + + + + HR + + + + + + + + + + +`; + exports[`ic-data-table should render column icon on all column cells if onAllCells is true, except ones that already have a custom icon 1`] = ` diff --git a/packages/canary-web-components/src/components/ic-data-table/test/basic/ic-data-table.spec.tsx b/packages/canary-web-components/src/components/ic-data-table/test/basic/ic-data-table.spec.tsx index eb2b951b05..a0df95ddcb 100644 --- a/packages/canary-web-components/src/components/ic-data-table/test/basic/ic-data-table.spec.tsx +++ b/packages/canary-web-components/src/components/ic-data-table/test/basic/ic-data-table.spec.tsx @@ -369,6 +369,38 @@ const dataWithIcons = [ { name: name5, age: 45, department: "HR", employeeNumber: 6 }, ]; +const dataWithActionElement = [ + { + header: { title: 1 }, + name: { + data: name1, + actionElement: ` `, + }, + age: 36, + department: "Accounts", + }, + { + header: { title: 2 }, + name: name2, + age: 32, + department: "Engineering", + }, + { header: { title: 3 }, name: "Tim Rayes", age: 41, department: "Sales" }, + { + header: { title: 4 }, + name: name3, + age: "23", + department: "Engineering", + }, + { + header: { title: 5 }, + name: name4, + age: 34, + department: "Engineering", + }, + { header: { title: 6 }, name: name5, age: 45, department: "HR" }, +]; + describe(icDataTable, () => { it("should render", async () => { const page = await newSpecPage({ @@ -1323,4 +1355,19 @@ describe(icDataTable, () => { expect(page.root).toMatchSnapshot(); }); + + it("should render an action element if present in the data set", async () => { + const page = await newSpecPage({ + components: [DataTable], + template: () => ( + + ), + }); + + expect(page.root).toMatchSnapshot(); + }); }); From e9676140eb274d416d2e52eae92f98abbe3a57ad Mon Sep 17 00:00:00 2001 From: ec1467 <190760328+ec1467@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:33:10 +0000 Subject: [PATCH 2/2] feat(canary-react): add action-element tests and story --- .../IcDataTable/IcDataTable.cy.tsx | 64 +++++++++++++++++++ .../src/stories/ic-data-table.stories.mdx | 55 +++++++++++++++- 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/packages/canary-react/src/component-tests/IcDataTable/IcDataTable.cy.tsx b/packages/canary-react/src/component-tests/IcDataTable/IcDataTable.cy.tsx index ec42810d47..666a43301d 100644 --- a/packages/canary-react/src/component-tests/IcDataTable/IcDataTable.cy.tsx +++ b/packages/canary-react/src/component-tests/IcDataTable/IcDataTable.cy.tsx @@ -39,6 +39,7 @@ import { textWrapCell, textWrapColumns, textWrapRow, + ACTION_DATA_ELEMENTS, } from "@ukic/canary-web-components/src/components/ic-data-table/story-data"; import { @@ -1072,6 +1073,69 @@ describe("IcDataTables", () => { }); }); +it("should render an element in the table cell if the data prop contains the actionElement key", () => { + mount( + + ); + cy.viewport(1024, 768); + + cy.checkHydrated(DATA_TABLE_SELECTOR); + + cy.findShadowEl(DATA_TABLE_SELECTOR, "td") + .eq(0) + .find("span") + .should(HAVE_CLASS, "action-element") + .find("ic-button") + .should("be.visible"); +}); + +it("should not render an element in the table cell if the data prop does not contain the actionElement key", () => { + mount( + + ); + cy.viewport(1024, 768); + + cy.checkHydrated(DATA_TABLE_SELECTOR); + + cy.findShadowEl(DATA_TABLE_SELECTOR, "td") + .eq(1) + .find("span") + .should("not.exist"); +}); + +it("should apply styling to the cell container if an action element is present in the cell", () => { + mount( + + ); + + cy.viewport(1024, 768); + + cy.checkHydrated(DATA_TABLE_SELECTOR); + + cy.findShadowEl(DATA_TABLE_SELECTOR, "td") + .eq(0) + .find("div") + .eq(0) + .should(HAVE_CLASS, "cell-grid-wrapper") + .should(HAVE_CSS, "grid-template-columns", "156.797px 32px"); + + cy.findShadowEl(DATA_TABLE_SELECTOR, "span") + .should(HAVE_CLASS, "action-element") + .should(HAVE_CSS, "justify-content", "right"); +}); + describe("IcDataTables with IcPaginationBar", () => { beforeEach(() => { cy.injectAxe(); diff --git a/packages/canary-react/src/stories/ic-data-table.stories.mdx b/packages/canary-react/src/stories/ic-data-table.stories.mdx index b1ac40b31d..663f8c071a 100644 --- a/packages/canary-react/src/stories/ic-data-table.stories.mdx +++ b/packages/canary-react/src/stories/ic-data-table.stories.mdx @@ -27,7 +27,8 @@ import { ICON_COLS, COLUMNS_TEXT_WRAP, TEXT_WRAP_LONG_DATA, - COLS_WIDTH + COLS_WIDTH, + ACTION_DATA_ELEMENTS } from "../../../canary-web-components/src/components/ic-data-table/story-data"; import { mdiAccountGroup, mdiImage, mdiDelete } from "@mdi/js"; @@ -2077,6 +2078,58 @@ const DataTable = () => { }; ``` +### Action Element + +An example with action elements passed in via the data + + + + + + + +#### Action Element code example + +```jsx +import * as React from "react"; +import { IcDataTable } from "@ukic/canary-react"; +import { IcDataTableColumnObject } from "@ukic/canary-web-components"; + +const COLS: IcDataTableColumnObject[] = [ + { key: "firstName", title: "First name", dataType: "string", columnWidth: "15%" }, + { key: "lastName", title: "Last name", dataType: "string", columnWidth: "300px" }, + { + key: "age", + title: "Age", + dataType: "number", + columnWidth: { + maxWidth: "100px", + }, + }, + ... +]; + +const DATA = [ + { + firstName: { + data: "Joe", + actionElement: ` `, + }, + lastName: "Bloggs", + age: 30, + jobTitle: "Developer", + address: "1 Main Street, Town, County, Postcode", + }, + ... +]; + +const DataTable = () => ( + +); + +export default DataTable; +``` + ### Playground example Go to the separate page for the playground example to view the prop controls.