From 7041b19c60aa5bf5458d28c8b4e8373a14643e23 Mon Sep 17 00:00:00 2001 From: Ignacio Becerra Date: Tue, 20 Jun 2023 14:09:48 -0700 Subject: [PATCH] feat(data-table): sync with @carbon/react v11 (#10516) * feat(data-table): sync with v11 * fix(data-table): fix to compile * fix(table): more compile error fix * fix(data-table): address some comments * fix(data-table): sizes, checkbox, skeleton * chore(data-table): restructuring story directory * fix(data-table): overflow hover on menu * fix(data-table): updated docs * feat(data-table): dispatching elements * fix(data-table): single story folder and example update * fix(data-table): cleanup * fix(data-table): addressed comments * fix(data-table): addressed comments * fix(data-table): addressed final comments * fix(data-table): refactored styles for attribute within host * fix(data-table): emit tokens --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../basic/components/data-table/cdn.html | 53 +- .../basic/components/data-table/data.js | 95 -- .../basic/components/data-table/defs.js | 30 - .../basic/components/data-table/index.html | 51 +- .../data-table/sortable-pagination.js | 152 --- .../components/data-table/sortable-table.js | 98 -- .../basic/components/data-table/src/index.js | 5 +- .../examples/codesandbox/data-table/.babelrc | 25 - .../codesandbox/data-table/.gitignore | 22 - .../codesandbox/data-table/index.html | 36 - .../codesandbox/data-table/package.json | 24 - .../data-table/sandbox.config.json | 3 - .../codesandbox/data-table/src/data.js | 85 -- .../data-table/src/demo-data-table.js | 460 --------- .../codesandbox/data-table/src/index.js | 18 - .../src/components/button/button.scss | 21 +- .../src/components/button/button.ts | 21 +- .../src/components/checkbox/checkbox.scss | 16 + .../src/components/checkbox/checkbox.ts | 16 +- .../components/data-table/_table-action.scss | 82 +- .../components/data-table/_table-core.scss | 282 +++++- .../data-table/_table-expandable.scss | 57 +- .../data-table/_table-selection.scss | 56 +- .../components/data-table/_table-sizes.scss | 182 +++- .../data-table/_table-skeleton.scss | 27 - .../components/data-table/_table-sort.scss | 26 +- .../data-table/data-table-story.mdx | 336 ------- .../data-table/data-table-story.scss | 14 - .../components/data-table/data-table-story.ts | 916 ------------------ .../src/components/data-table/data-table.scss | 7 +- .../src/components/data-table/defs.ts | 15 - .../src/components/data-table/index.ts | 11 +- .../stories/data-table-basic-story.ts | 269 +++++ .../stories/data-table-batch-actions-story.ts | 278 ++++++ .../stories/data-table-dynamic-story.ts | 354 +++++++ .../stories/data-table-expansion-story.ts | 354 +++++++ .../stories/data-table-filtering-story.ts | 236 +++++ .../stories/data-table-selection-story.ts | 359 +++++++ .../stories/data-table-skeleton-story.ts | 63 ++ .../stories/data-table-sorting-story.ts | 203 ++++ .../data-table/stories/data-table-story.mdx | 521 ++++++++++ .../stories/data-table-toolbar-story.ts | 691 +++++++++++++ .../src/components/data-table/stories/data.ts | 88 -- .../components/data-table/stories/types.ts | 36 - .../data-table/table-batch-actions.ts | 22 +- .../src/components/data-table/table-body.ts | 31 +- .../data-table/table-cell-content.ts | 29 + .../src/components/data-table/table-cell.ts | 32 +- .../components/data-table/table-expand-row.ts | 141 --- .../data-table/table-expanded-row.ts | 43 +- .../src/components/data-table/table-head.ts | 12 +- .../data-table/table-header-cell-skeleton.ts | 22 - .../data-table/table-header-cell.ts | 47 +- .../data-table/table-header-description.ts | 29 + .../data-table/table-header-expand-row.ts | 45 - .../components/data-table/table-header-row.ts | 6 +- ...cell-skeleton.ts => table-header-title.ts} | 18 +- .../src/components/data-table/table-row.ts | 312 +++++- .../components/data-table/table-skeleton.ts | 164 ++++ .../data-table/table-toolbar-content.ts | 17 +- .../data-table/table-toolbar-search.ts | 28 +- .../components/data-table/table-toolbar.ts | 49 +- .../src/components/data-table/table.ts | 874 ++++++++++++++++- .../components/overflow-menu/overflow-menu.ts | 41 + .../components/radio-button/radio-button.scss | 12 +- .../components/radio-button/radio-button.ts | 18 + .../src/components/tooltip/tooltip.scss | 26 + .../src/components/tooltip/tooltip.ts | 22 +- .../carbon-web-components/src/index.ts | 4 + .../tests/spec/data-table_spec.ts | 84 +- 70 files changed, 5779 insertions(+), 3043 deletions(-) delete mode 100644 web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/data.js delete mode 100644 web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/defs.js delete mode 100644 web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/sortable-pagination.js delete mode 100644 web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/sortable-table.js delete mode 100644 web-components/packages/carbon-web-components/examples/codesandbox/data-table/.babelrc delete mode 100644 web-components/packages/carbon-web-components/examples/codesandbox/data-table/.gitignore delete mode 100644 web-components/packages/carbon-web-components/examples/codesandbox/data-table/index.html delete mode 100644 web-components/packages/carbon-web-components/examples/codesandbox/data-table/package.json delete mode 100644 web-components/packages/carbon-web-components/examples/codesandbox/data-table/sandbox.config.json delete mode 100644 web-components/packages/carbon-web-components/examples/codesandbox/data-table/src/data.js delete mode 100644 web-components/packages/carbon-web-components/examples/codesandbox/data-table/src/demo-data-table.js delete mode 100644 web-components/packages/carbon-web-components/examples/codesandbox/data-table/src/index.js delete mode 100644 web-components/packages/carbon-web-components/src/components/data-table/_table-skeleton.scss delete mode 100644 web-components/packages/carbon-web-components/src/components/data-table/data-table-story.mdx delete mode 100644 web-components/packages/carbon-web-components/src/components/data-table/data-table-story.scss delete mode 100644 web-components/packages/carbon-web-components/src/components/data-table/data-table-story.ts create mode 100644 web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-basic-story.ts create mode 100644 web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-batch-actions-story.ts create mode 100644 web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-dynamic-story.ts create mode 100644 web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-expansion-story.ts create mode 100644 web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-filtering-story.ts create mode 100644 web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-selection-story.ts create mode 100644 web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-skeleton-story.ts create mode 100644 web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-sorting-story.ts create mode 100644 web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-story.mdx create mode 100644 web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-toolbar-story.ts delete mode 100644 web-components/packages/carbon-web-components/src/components/data-table/stories/data.ts delete mode 100644 web-components/packages/carbon-web-components/src/components/data-table/stories/types.ts create mode 100644 web-components/packages/carbon-web-components/src/components/data-table/table-cell-content.ts delete mode 100644 web-components/packages/carbon-web-components/src/components/data-table/table-expand-row.ts delete mode 100644 web-components/packages/carbon-web-components/src/components/data-table/table-header-cell-skeleton.ts create mode 100644 web-components/packages/carbon-web-components/src/components/data-table/table-header-description.ts delete mode 100644 web-components/packages/carbon-web-components/src/components/data-table/table-header-expand-row.ts rename web-components/packages/carbon-web-components/src/components/data-table/{table-cell-skeleton.ts => table-header-title.ts} (50%) create mode 100644 web-components/packages/carbon-web-components/src/components/data-table/table-skeleton.ts diff --git a/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/cdn.html b/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/cdn.html index 64254449404e..3c9c4e3ca4a5 100644 --- a/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/cdn.html +++ b/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/cdn.html @@ -19,33 +19,27 @@ } - -

Default tabe

- Foo - Bar - Baz + Name + Status - Foo1 - Bar1 - Baz1 + Load Balancer 1 + Disabled - Foo2 - Bar2 - Baz2 + Load Balancer 2 + Starting - Foo3 - Bar3 - Baz3 + Load Balancer 3 + Active @@ -56,14 +50,27 @@

Default tabe

Sortable table

- - -
-
-
- -

Sortable table with pagination

-
- + + + + Name + Status + + + + + Load Balancer 1 + Disabled + + + Load Balancer 2 + Starting + + + Load Balancer 3 + Active + + + diff --git a/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/data.js b/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/data.js deleted file mode 100644 index c1e1784d6702..000000000000 --- a/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/data.js +++ /dev/null @@ -1,95 +0,0 @@ -/** - * @license - * - * Copyright IBM Corp. 2022 - * - * This source code is licensed under the Apache-2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ -export const columns = [ - { - id: 'name', - title: 'Name', - sortCycle: 'bi-states-from-ascending', - }, - { - id: 'protocol', - title: 'Protocol', - }, - { - id: 'port', - title: 'Port', - sortCycle: 'tri-states-from-ascending', - }, - { - id: 'rule', - title: 'Rule', - }, - { - id: 'attachedGroups', - title: 'Attached Groups', - }, - { - id: 'status', - title: 'Status', - }, -]; - -export const rows = [ - { - id: 1, - name: 'Load Balancer 1', - protocol: 'HTTP', - port: 80, - rule: 'Round Robin', - attachedGroups: "Maureen's VM Groups", - status: 'Active', - }, - { - id: 2, - name: 'Load Balancer 2', - protocol: 'HTTPS', - port: 443, - rule: 'Round Robin', - attachedGroups: "Maureen's VM Groups", - status: 'Active', - }, - { - id: 3, - selected: true, - name: 'Load Balancer 3', - protocol: 'HTTP', - port: 80, - rule: 'Round Robin', - attachedGroups: "Maureen's VM Groups", - status: 'Active', - }, - { - id: 4, - name: 'Load Balancer 4', - protocol: 'HTTP', - port: 80, - rule: 'Round Robin', - attachedGroups: "Maureen's VM Groups", - status: 'Active', - }, - { - id: 5, - name: 'Load Balancer 5', - protocol: 'HTTPS', - port: 443, - rule: 'Round Robin', - attachedGroups: "Maureen's VM Groups", - status: 'Active', - }, - { - id: 6, - selected: true, - name: 'Load Balancer 6', - protocol: 'HTTP', - port: 80, - rule: 'Round Robin', - attachedGroups: "Maureen's VM Groups", - status: 'Active', - }, -]; diff --git a/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/defs.js b/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/defs.js deleted file mode 100644 index a17a2c0db919..000000000000 --- a/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/defs.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @license - * - * Copyright IBM Corp. 2022 - * - * This source code is licensed under the Apache-2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - -/** - * Table sort state. - */ -const TABLE_SORT_DIRECTION = { - /** - * Not sorted. - */ - NONE: 'none', - - /** - * Sorted ascendingly. - */ - ASCENDING: 'ascending', - - /** - * Sorted descendingly. - */ - DESCENDING: 'descending', -}; - -export default TABLE_SORT_DIRECTION; diff --git a/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/index.html b/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/index.html index f4bf8d70c290..460f2d5e7e05 100644 --- a/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/index.html +++ b/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/index.html @@ -25,26 +25,22 @@

Default tabe

- Foo - Bar - Baz + Name + Status - Foo1 - Bar1 - Baz1 + Load Balancer 1 + Disabled - Foo2 - Bar2 - Baz2 + Load Balancer 2 + Starting - Foo3 - Bar3 - Baz3 + Load Balancer 3 + Active @@ -55,14 +51,27 @@

Default tabe

Sortable table

- - -
-
-
- -

Sortable table with pagination

-
- + + + + Name + Status + + + + + Load Balancer 1 + Disabled + + + Load Balancer 2 + Starting + + + Load Balancer 3 + Active + + + diff --git a/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/sortable-pagination.js b/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/sortable-pagination.js deleted file mode 100644 index a4a6290b1164..000000000000 --- a/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/sortable-pagination.js +++ /dev/null @@ -1,152 +0,0 @@ -/* eslint-disable no-use-before-define */ - -/** - * @license - * - * Copyright IBM Corp. 2022, 2023 - * - * This source code is licensed under the Apache-2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - -import TABLE_SORT_DIRECTION from './defs.js'; -import { columns, rows } from './data.js'; - -// event listener for the table sorting event -// triggered when user clicks on the sorting icon of the header cell -document.addEventListener('cds-table-header-cell-sort', ({ defaultPrevented, detail, target }) => { - if (!defaultPrevented) { - const { columnId } = target.dataset; - const { sortDirection: direction } = detail; - // Sets the sorting as user desires - const sortInfo = { - columnId, - direction, - }; - - table.state.setSortInfo(sortInfo); - } -}); - -// event listener for pagination event -// triggered when change in the row number the current page starts with -document.addEventListener('cds-pagination-changed-current', ({ detail }) => { - table.state.setStart(detail.start); -}); - -// event listener for pagination event -// triggered after the number of rows per page is changed -document.addEventListener('cds-page-sizes-select-changed', ({ detail }) => { - table.state.setPageSize(detail.value); -}); - -// returns pagination component -function _renderPagination() { - const { pageSize, start } = table.state; - if (typeof pageSize === 'undefined') { - return undefined; - } - - return ` - - - - - - - - `; -} - -const collator = new Intl.Collator('en'); - -const table = () => { - const { sortInfo, start, pageSize } = table.state; - const { columnId: sortColumnId, direction: sortDirection } = sortInfo; - // sorting logic, returns the sorted rows to render - const sortedRows = rows.slice().sort((lhs, rhs) => { - const lhsValue = lhs[sortInfo.columnId]; - const rhsValue = rhs[sortInfo.columnId]; - return (sortInfo.direction === 'ascending' ? 1 : -1) * collator.compare(lhsValue, rhsValue); - }); - - if (typeof pageSize === 'undefined') { - return undefined; - } - - return ` - - - - ${columns - .map((column) => { - const { id: columnId, sortCycle, title } = column; - const sortDirectionForThisCell = - sortCycle && (columnId === sortColumnId ? sortDirection : TABLE_SORT_DIRECTION.NONE); - return `${title}`; - }) - .join('')} - - - - ${sortedRows - .slice(start, start + pageSize) - .map((row) => { - const { id: rowId } = row; - return ` - - ${columns - .map((column) => { - const { id: columnId } = column; - return `${row[columnId]}`; - }) - .join('')} - `; - }) - .join('')} - - - ${_renderPagination()} - `; -}; - -table.state = { - start: 0, - setStart: (start) => { - setState(() => { - table.state.start = start; - }); - }, - pageSize: 3, - setPageSize: (pageSize) => { - setState(() => { - table.state.pageSize = pageSize; - }); - }, - sortInfo: { - columnId: 'name', - direction: TABLE_SORT_DIRECTION.ASCENDING, - }, - setSortInfo: (sortInfo) => { - setState(() => { - table.state.sortInfo = sortInfo; - }); - }, -}; - -function setState(callback) { - callback(); - updateTree(); // extracted function -} - -const updateTree = () => { - document.getElementById('sortable-pagination').innerHTML = table(); -}; - -updateTree(); diff --git a/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/sortable-table.js b/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/sortable-table.js deleted file mode 100644 index 948f63834008..000000000000 --- a/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/sortable-table.js +++ /dev/null @@ -1,98 +0,0 @@ -/* eslint-disable no-use-before-define */ - -/** - * @license - * - * Copyright IBM Corp. 2022, 2023 - * - * This source code is licensed under the Apache-2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - -import TABLE_SORT_DIRECTION from './defs.js'; -import { columns, rows } from './data.js'; - -// event listener for the sorting event -document.addEventListener('cds-table-header-cell-sort', ({ defaultPrevented, detail, target }) => { - if (!defaultPrevented) { - const { columnId } = target.dataset; - const { sortDirection: direction } = detail; - // Sets the sorting as user desires - const sortInfo = { - columnId, - direction, - }; - - table.state.setSortInfo(sortInfo); - } -}); - -const collator = new Intl.Collator('en'); - -const table = () => { - // sorting logic, returns the sorted rows to render - const sortedRows = rows.slice().sort((lhs, rhs) => { - const lhsValue = lhs[table.state.sortInfo.columnId]; - const rhsValue = rhs[table.state.sortInfo.columnId]; - return (table.state.sortInfo.direction === 'ascending' ? 1 : -1) * collator.compare(lhsValue, rhsValue); - }); - - const { columnId: sortColumnId, direction: sortDirection } = table.state.sortInfo; - - return ` - - - - ${columns - .map((column) => { - const { id: columnId, sortCycle, title } = column; - const sortDirectionForThisCell = - sortCycle && (columnId === sortColumnId ? sortDirection : TABLE_SORT_DIRECTION.NONE); - return `${title}`; - }) - .join('')} - - - - ${sortedRows - .map((row) => { - const { id: rowId } = row; - return ` - - ${columns - .map((column) => { - const { id: columnId } = column; - return `${row[columnId]}`; - }) - .join('')} - `; - }) - .join('')} - - `; -}; - -table.state = { - sortInfo: { - columnId: 'name', - direction: TABLE_SORT_DIRECTION.ASCENDING, - }, - setSortInfo: (sortInfo) => { - setState(() => { - table.state.sortInfo = sortInfo; - }); - }, -}; - -function setState(callback) { - callback(); - updateTree(); // extracted function -} - -const updateTree = () => { - document.getElementById('sortable-table').innerHTML = table(); -}; - -updateTree(); diff --git a/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/src/index.js b/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/src/index.js index a54ebc9e05ab..2584d14f0aa1 100644 --- a/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/src/index.js +++ b/web-components/packages/carbon-web-components/examples/codesandbox/basic/components/data-table/src/index.js @@ -1,11 +1,10 @@ /** * @license * - * Copyright IBM Corp. 2020, 2022 + * Copyright IBM Corp. 2020, 2023 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ -import '@carbon/web-components/es/components/data-table/index.js'; -import '@carbon/web-components/es/components/pagination/index.js'; +import '@carbon/web-components/es/components/data-table/index.js'; \ No newline at end of file diff --git a/web-components/packages/carbon-web-components/examples/codesandbox/data-table/.babelrc b/web-components/packages/carbon-web-components/examples/codesandbox/data-table/.babelrc deleted file mode 100644 index a32aaca59b93..000000000000 --- a/web-components/packages/carbon-web-components/examples/codesandbox/data-table/.babelrc +++ /dev/null @@ -1,25 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "modules": false, - "targets": [ - "last 1 version", - "Firefox ESR", - "not opera > 0", - "not op_mini > 0", - "not op_mob > 0", - "not android > 0", - "not edge > 0", - "not ie > 0", - "not ie_mob > 0" - ] - } - ] - ], - "plugins": [ - ["@babel/plugin-proposal-decorators", { "decoratorsBeforeExport": true }], - ["@babel/plugin-transform-runtime", { "version": "7.3.0" }] - ] -} diff --git a/web-components/packages/carbon-web-components/examples/codesandbox/data-table/.gitignore b/web-components/packages/carbon-web-components/examples/codesandbox/data-table/.gitignore deleted file mode 100644 index d94d6e13e948..000000000000 --- a/web-components/packages/carbon-web-components/examples/codesandbox/data-table/.gitignore +++ /dev/null @@ -1,22 +0,0 @@ -# See https://help.github.com/ignore-files/ for more about ignoring files. - -# dependencies -/node_modules - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.cache -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/web-components/packages/carbon-web-components/examples/codesandbox/data-table/index.html b/web-components/packages/carbon-web-components/examples/codesandbox/data-table/index.html deleted file mode 100644 index e3ae6646b8fb..000000000000 --- a/web-components/packages/carbon-web-components/examples/codesandbox/data-table/index.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - carbon-web-components example - - - - - -

Hello World! 👋

-
- -
- - diff --git a/web-components/packages/carbon-web-components/examples/codesandbox/data-table/package.json b/web-components/packages/carbon-web-components/examples/codesandbox/data-table/package.json deleted file mode 100644 index c5bd8351d342..000000000000 --- a/web-components/packages/carbon-web-components/examples/codesandbox/data-table/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "carbon-web-components-getting-started", - "version": "0.1.0", - "private": true, - "description": "Sample project for getting started with the Web Components from the Carbon Design System.", - "license": "Apache-2", - "main": "index.html", - "scripts": { - "build": "parcel build index.html", - "start": "parcel index.html --no-hmr" - }, - "dependencies": { - "@carbon/web-components": "latest", - "lit": "^2.6.0", - "lodash-es": "^4.17.0" - }, - "devDependencies": { - "@babel/core": "^7.10.0", - "@babel/plugin-proposal-decorators": "^7.10.0", - "@babel/plugin-transform-runtime": "^7.10.0", - "@types/lodash-es": "^4.17.0", - "parcel-bundler": "^1.10.0" - } -} diff --git a/web-components/packages/carbon-web-components/examples/codesandbox/data-table/sandbox.config.json b/web-components/packages/carbon-web-components/examples/codesandbox/data-table/sandbox.config.json deleted file mode 100644 index a4df8557d7bf..000000000000 --- a/web-components/packages/carbon-web-components/examples/codesandbox/data-table/sandbox.config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "template": "node" -} diff --git a/web-components/packages/carbon-web-components/examples/codesandbox/data-table/src/data.js b/web-components/packages/carbon-web-components/examples/codesandbox/data-table/src/data.js deleted file mode 100644 index be62357705b7..000000000000 --- a/web-components/packages/carbon-web-components/examples/codesandbox/data-table/src/data.js +++ /dev/null @@ -1,85 +0,0 @@ -/** - * @license - * - * Copyright IBM Corp. 2020, 2022 - * - * This source code is licensed under the Apache-2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - -import { TABLE_SORT_DIRECTION } from '@carbon/web-components/es/components/data-table/table-header-cell'; - -export const columns = [ - { - id: 'name', - title: 'Name', - sortCycle: 'bi-states-from-ascending', - }, - { - id: 'protocol', - title: 'Protocol', - }, - { - id: 'port', - title: 'Port', - sortCycle: 'tri-states-from-ascending', - }, - { - id: 'rule', - title: 'Rule', - }, - { - id: 'attachedGroups', - title: 'Attached Groups', - }, - { - id: 'status', - title: 'Status', - }, -]; - -export const rows = [ - { - id: 1, - name: 'Load Balancer 1', - protocol: 'HTTP', - port: 80, - rule: 'Round Robin', - attachedGroups: "Maureen's VM Groups", - status: 'Active', - }, - { - id: 2, - name: 'Load Balancer 2', - protocol: 'HTTPS', - port: 443, - rule: 'Round Robin', - attachedGroups: "Maureen's VM Groups", - status: 'Active', - }, - { - id: 3, - selected: true, - name: 'Load Balancer 3', - protocol: 'HTTP', - port: 80, - rule: 'Round Robin', - attachedGroups: "Maureen's VM Groups", - status: 'Active', - }, -]; - -export const rowsMany = Array.from(new Array(50)) - .map((_item, i) => - rows.map((row, j) => ({ - ...row, - id: i * 3 + j, - name: `Load Balancer ${String(i * 3 + j + 1).padStart(3, '0')}`, - })) - ) - .flat(); - -export const sortInfo = { - columnId: 'name', - direction: TABLE_SORT_DIRECTION.ASCENDING, -}; diff --git a/web-components/packages/carbon-web-components/examples/codesandbox/data-table/src/demo-data-table.js b/web-components/packages/carbon-web-components/examples/codesandbox/data-table/src/demo-data-table.js deleted file mode 100644 index f4314549eabe..000000000000 --- a/web-components/packages/carbon-web-components/examples/codesandbox/data-table/src/demo-data-table.js +++ /dev/null @@ -1,460 +0,0 @@ -/** - * @license - * - * Copyright IBM Corp. 2020, 2023 - * - * This source code is licensed under the Apache-2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - -import debounce from 'lodash-es/debounce'; -import { repeat } from 'lit/directives/repeat.js'; -import { html, property, customElement, LitElement } from 'lit'; -import ifNonNull from '@carbon/web-components/es/globals/directives/if-non-null'; -import TrashCan from '@carbon/web-components/es/icons/trash-can/16'; -import Download16 from '@carbon/web-components/es/icons/download/16'; -import Settings16 from '@carbon/web-components/es/icons/settings/16'; -import '@carbon/web-components/es/components/button/button'; -import '@carbon/web-components/es/components/overflow-menu/overflow-menu'; -import '@carbon/web-components/es/components/overflow-menu/overflow-menu-body'; -import '@carbon/web-components/es/components/overflow-menu/overflow-menu-item'; -import '@carbon/web-components/es/components/pagination/pagination'; -import '@carbon/web-components/es/components/pagination/page-sizes-select'; -import '@carbon/web-components/es/components/pagination/pages-select'; -import { TABLE_COLOR_SCHEME, TABLE_SIZE } from '@carbon/web-components/es/components/data-table/table'; -import '@carbon/web-components/es/components/data-table/table-head'; -import '@carbon/web-components/es/components/data-table/table-header-row'; -import { TABLE_SORT_DIRECTION } from '@carbon/web-components/es/components/data-table/table-header-cell'; -import '@carbon/web-components/es/components/data-table/table-body'; -import '@carbon/web-components/es/components/data-table/table-row'; -import '@carbon/web-components/es/components/data-table/table-cell'; -import '@carbon/web-components/es/components/data-table/table-header-expand-row'; -import '@carbon/web-components/es/components/data-table/table-expand-row'; -import '@carbon/web-components/es/components/data-table/table-expanded-row'; -import '@carbon/web-components/es/components/data-table/table-toolbar'; -import '@carbon/web-components/es/components/data-table/table-toolbar-content'; -import '@carbon/web-components/es/components/data-table/table-toolbar-search'; -import '@carbon/web-components/es/components/data-table/table-batch-actions'; -import '@carbon/web-components/es/components/data-table/table-header-cell-skeleton'; -import '@carbon/web-components/es/components/data-table/table-cell-skeleton'; - -/** - * @param row A table row. - * @param searchString A search string. - * @returns `true` if the given table row matches the given search string. - */ -const doesRowMatchSearchString = (row, searchString) => - Object.keys(row).some((key) => key !== 'id' && String(row[key] ?? '').indexOf(searchString) >= 0); - -/** - * A class to manage table states, like selection and sorting. - * DEMONSTRATION-PURPOSE ONLY. - * Data/state handling in data table tends to involve lots of application-specific logics - * and thus abstracting everything in a library won't be a good return on investment - * vs. letting users copy code here and implement features that fit their needs. - */ -// @ts-ignore `BXCEDemoDataTable` is used (only) for type reference -// eslint-disable-next-line @typescript-eslint/no-unused-vars -@customElement('cds-ce-demo-data-table') -class BXCEDemoDataTable extends LitElement { - /** - * The debounced handler for user-initiated change in search string. - * @type {Function} - */ - _handleChangeSearchString = null; - - /** - * The table sorting info reflecting user-initiated changes. - * @type {TDemoSortInfo} - */ - _sortInfo = null; - - /** - * The table rows reflecting selection. - * @type {TDemoTableRow[]} - */ - _rows = null; - - /** - * The table rows reflecting selection and filtering. - * @type {TDemoTableRow[]} - */ - _filteredRows = null; - - /** - * The search string. - * @type {string} - */ - _searchString = ''; - - /** - * Unique ID used for form elements. - */ - _uniqueId = Math.random().toString(36).slice(2); - - /** - * @param lhs A value. - * @param rhs Another value. - * @returns - * * `0` if the given two values are equal - * * A negative value to sort `lhs` to an index lower than `rhs` - * * A positive value to sort `rhs` to an index lower than `lhs` - */ - _compare(lhs, rhs) { - if (typeof lhs === 'number' && typeof rhs === 'number') { - return lhs - rhs; - } - return this.collator.compare(lhs, rhs); - } - - /** - * Handles Cancel button in batch action bar. - */ - _handleCancelSelection() { - const { _rows: oldRows, _searchString: searchString } = this; - this._rows = this._rows.map((row) => - searchString && !doesRowMatchSearchString(row, searchString) ? row : { ...row, selected: false } - ); - this.requestUpdate('_rows', oldRows); - } - - /** - * Handles user-initiated change in search string. - * @param {CustomEvent} event The event. - */ - _handleChangeSearchStringImpl({ detail }) { - const { _searchString: oldSearchString } = this; - this._searchString = detail.value; - this.requestUpdate('_searchString', oldSearchString); - } - - /** - * Handles an event to change in selection of rows, fired from ``. - * @param {CustomEvent} event The event. - */ - _handleChangeSelection({ defaultPrevented, detail, target }) { - if (!defaultPrevented) { - const { rowId: changedRowId } = target.dataset; - const { selected } = detail; - const { _rows: oldRows } = this; - this._rows = oldRows.map((row) => (Number(changedRowId) !== row.id ? row : { ...row, selected })); - this.requestUpdate('_rows', oldRows); - } - } - - /** - * Handles an event to change in selection of all rows, fired from ``. - * @param {CustomEvent} event The event. - */ - _handleChangeSelectionAll({ defaultPrevented, detail }) { - if (!defaultPrevented) { - const { selected } = detail; - const { _rows: oldRows, _searchString: searchString } = this; - this._rows = this._rows.map((row) => - searchString && !doesRowMatchSearchString(row, searchString) ? row : { ...row, selected } - ); - this.requestUpdate('_rows', oldRows); - } - } - - /** - * Handles an event to sort rows, fired from ``. - * @param {CustomEvent} event The event. - */ - _handleChangeSort({ defaultPrevented, detail, target }) { - if (!defaultPrevented) { - const { columnId } = target.dataset; - const { sortDirection: direction } = detail; - const { _sortInfo: oldSortInfo } = this; - if (direction === TABLE_SORT_DIRECTION.NONE && columnId !== 'name') { - // Resets the sorting, given non-primary sorting column has got in non-sorting state - this._sortInfo = this.sortInfo; - } else { - // Sets the sorting as user desires - this._sortInfo = { - columnId, - direction, - }; - } - this.requestUpdate('_sortInfo', oldSortInfo); - } - } - - /** - * Handles `cds-pagination-changed-current` event on the pagination UI. - * @param {CustomEvent} event The event. - */ - _handleChangeStart({ detail }) { - this.start = detail.start; - } - - /** - * Handles `cds-pages-select-changed` event on the pagination UI. - * @param {CustomEvent} event The event. - */ - _handleChangePageSize({ detail }) { - this.pageSize = detail.value; - } - - /** - * Handles Delete batch action button. - */ - _handleDeleteRows() { - const { _rows: oldRows, _searchString: searchString } = this; - this._rows = oldRows.filter((row) => !row.selected || !doesRowMatchSearchString(row, searchString)); - this.requestUpdate('_rows', oldRows); - } - - /** - * Handles Download batch action button. - * @param {CustomEvent} event The event. - */ - _handleDownloadRows({ target }) { - const blob = new Blob([JSON.stringify(this._filteredRows.filter((row) => row.selected))], { type: 'application/json' }); - target.href = URL.createObjectURL(blob); - this._handleCancelSelection(); - } - - /** - * @returns The content of the pagination UI. - */ - _renderPagination() { - const { - pageSize, - start, - _filteredRows: filteredRows, - _handleChangeStart: handleChangeStart, - _handleChangePageSize: handleChangePageSize, - } = this; - if (typeof pageSize === 'undefined') { - return undefined; - } - return html` - - - - - - - - - `; - } - - /** - * The g11n collator to use. - */ - @property({ attribute: false }) - collator = new Intl.Collator(); - - /** - * Data table columns. - */ - @property({ attribute: false }) - columns = undefined; - - /** - * Data table rows. - */ - @property({ attribute: false }) - rows = undefined; - - /** - * Table sorting info. - */ - @property({ attribute: false }) - sortInfo = undefined; - - /** - * `true` if the the table should support selection UI. - */ - @property({ type: Boolean, reflect: true, attribute: 'has-selection' }) - hasSelection = false; - - /** - * Number of items per page. - */ - @property({ type: Number, attribute: 'page-size' }) - pageSize = undefined; - - /** - * The table size. - */ - @property({ reflect: true }) - size = TABLE_SIZE.REGULAR; - - /** - * The table color scheme. - */ - @property({ reflect: true, attribute: 'color-scheme' }) - colorScheme = TABLE_COLOR_SCHEME.REGULAR; - - /** - * The row number where current page start with, index that starts with zero. - */ - @property({ type: Number }) - start = 0; - - connectedCallback() { - super.connectedCallback(); - if (this._handleChangeSearchString) { - this._handleChangeSearchString.cancel(); - } - this._handleChangeSearchString = debounce(this._handleChangeSearchStringImpl, 500); - } - - disconnectedCallback() { - if (this._handleChangeSearchString) { - this._handleChangeSearchString.cancel(); - this._handleChangeSearchString = undefined; - } - super.disconnectedCallback(); - } - - shouldUpdate(changedProperties) { - if (changedProperties.has('sortInfo')) { - this._sortInfo = this.sortInfo; - } - if (changedProperties.has('rows')) { - this._rows = this.rows; - } - if (changedProperties.has('rows') || changedProperties.has('_rows') || changedProperties.has('_searchString')) { - const { pageSize, start, _rows: rows, _searchString: searchString } = this; - this._filteredRows = !searchString ? rows : rows.filter((row) => doesRowMatchSearchString(row, searchString)); - const count = this._filteredRows.length; - if (count > 0 && start >= count) { - this.start = Math.max(start - Math.ceil((start - count) / pageSize) * pageSize, 0); - } - } - return true; - } - - render() { - const { - id: elementId, - colorScheme, - hasSelection, - pageSize = Infinity, - start = 0, - size, - columns, - _filteredRows: filteredRows, - _handleCancelSelection: handleCancelSelection, - _handleDeleteRows: handleDeleteRows, - _handleDownloadRows: handleDownloadRows, - } = this; - const selectionAllName = !hasSelection ? undefined : `__cds-ce-demo-data-table_select-all_${elementId || this._uniqueId}`; - const selectedRowsCountInFiltered = filteredRows.filter(({ selected }) => selected).length; - const selectedAllInFiltered = selectedRowsCountInFiltered > 0 && filteredRows.length === selectedRowsCountInFiltered; - const hasBatchActions = hasSelection && selectedRowsCountInFiltered > 0; - const { columnId: sortColumnId, direction: sortDirection } = this._sortInfo; - const sortedRows = - sortDirection === TABLE_SORT_DIRECTION.NONE - ? filteredRows - : filteredRows - .slice() - .sort( - (lhs, rhs) => this.constructor.collationFactors[sortDirection] * this._compare(lhs[sortColumnId], rhs[sortColumnId]) - ); - return html` - - - Delete ${TrashCan({ slot: 'icon' })} - - Download ${Download16({ slot: 'icon' })} - - - - - - ${Settings16({ slot: 'icon' })} - - Action 1 - Action 2 - Action 3 - - - Primary Button - - - - - - ${repeat( - columns, - ({ id: columnId }) => columnId, - ({ id: columnId, sortCycle, title }) => { - const sortDirectionForThisCell = - sortCycle && (columnId === sortColumnId ? sortDirection : TABLE_SORT_DIRECTION.NONE); - return html` - - ${title} - - `; - } - )} - - - - ${repeat( - sortedRows.slice(start, start + pageSize), - ({ id: rowId }) => rowId, - (row) => { - const { id: rowId, selected } = row; - const selectionName = !hasSelection ? undefined : `__cds-ce-demo-data-table_${elementId || this._uniqueId}_${rowId}`; - const selectionValue = !hasSelection ? undefined : 'selected'; - return html` - - ${repeat( - columns, - ({ id: columnId }) => columnId, - ({ id: columnId }) => html` ${row[columnId]} ` - )} - - `; - } - )} - - - ${this._renderPagination()} - `; - } - - /** - * The map of how sorting direction affects sorting order. - */ - static collationFactors = { - [TABLE_SORT_DIRECTION.ASCENDING]: 1, - [TABLE_SORT_DIRECTION.DESCENDING]: -1, - }; -} - -export default BXCEDemoDataTable; diff --git a/web-components/packages/carbon-web-components/examples/codesandbox/data-table/src/index.js b/web-components/packages/carbon-web-components/examples/codesandbox/data-table/src/index.js deleted file mode 100644 index c085e6530107..000000000000 --- a/web-components/packages/carbon-web-components/examples/codesandbox/data-table/src/index.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @license - * - * Copyright IBM Corp. 2020 - * - * This source code is licensed under the Apache-2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - -import './demo-data-table'; -import { rowsMany as rows, columns, sortInfo } from './data'; - -document.addEventListener('DOMContentLoaded', () => { - const dataTable = document.getElementById('data-table'); - dataTable.rows = rows; - dataTable.columns = columns; - dataTable.sortInfo = sortInfo; -}); diff --git a/web-components/packages/carbon-web-components/src/components/button/button.scss b/web-components/packages/carbon-web-components/src/components/button/button.scss index 513f749f34fa..06cac9c28808 100644 --- a/web-components/packages/carbon-web-components/src/components/button/button.scss +++ b/web-components/packages/carbon-web-components/src/components/button/button.scss @@ -8,8 +8,9 @@ $css--plex: true !default; @use '@carbon/styles/scss/config' as *; +@use '@carbon/styles/scss/spacing' as *; @use '@carbon/styles/scss/utilities/convert' as *; -@use '@carbon/styles/scss/components/button'; +@use '@carbon/styles/scss/components/button' as *; @use '@carbon/styles/scss/components/popover/index'; @use '@carbon/styles/scss/components/code-snippet/code-snippet' as *; @use '@carbon/styles/scss/components/tooltip'; @@ -49,6 +50,24 @@ $css--plex: true !default; } } } + + &[batch-action] { + .#{$prefix}--btn { + padding: $button-padding-ghost; + + &:focus { + outline: 2px solid $layer; + outline-offset: -#{$spacing-01}; + } + } + + &[has-main-content] { + ::slotted([slot='icon']) { + position: static; + margin-left: $spacing-02; + } + } + } } :host(#{$prefix}-button[kind='ghost']:hover) .#{$prefix}--btn--ghost, diff --git a/web-components/packages/carbon-web-components/src/components/button/button.ts b/web-components/packages/carbon-web-components/src/components/button/button.ts index 590f60bcda2e..a67dc4535202 100644 --- a/web-components/packages/carbon-web-components/src/components/button/button.ts +++ b/web-components/packages/carbon-web-components/src/components/button/button.ts @@ -45,11 +45,6 @@ class CDSButton extends HostListenerMixin(FocusMixin(LitElement)) { */ private _hasIcon = false; - /** - * `true` if there is a non-icon content. - */ - private _hasMainContent = false; - /** * Handles `slotchange` event. */ @@ -60,7 +55,7 @@ class CDSButton extends HostListenerMixin(FocusMixin(LitElement)) { .some( (node) => node.nodeType !== Node.TEXT_NODE || node!.textContent!.trim() ); - this[name === 'icon' ? '_hasIcon' : '_hasMainContent'] = hasContent; + this[name === 'icon' ? '_hasIcon' : 'hasMainContent'] = hasContent; this.requestUpdate(); } @@ -114,6 +109,12 @@ class CDSButton extends HostListenerMixin(FocusMixin(LitElement)) { @property({ type: Boolean, reflect: true }) autofocus = false; + /** + * `true` if the button is being used within a data table batch action toolbar + */ + @property({ type: Boolean, reflect: true, attribute: 'batch-action' }) + batchAction = false; + /** * Specify an optional className to be added to your Button */ @@ -138,6 +139,12 @@ class CDSButton extends HostListenerMixin(FocusMixin(LitElement)) { @property({ reflect: true }) download!: string; + /** + * `true` if there is a non-icon content. + */ + @property({ reflect: true, attribute: 'has-main-content', type: Boolean }) + hasMainContent = false; + /** * Link `href`. If present, this button is rendered as ``. */ @@ -255,7 +262,7 @@ class CDSButton extends HostListenerMixin(FocusMixin(LitElement)) { tooltipText, type, _hasIcon: hasIcon, - _hasMainContent: hasMainContent, + hasMainContent, _handleSlotChange: handleSlotChange, } = this; diff --git a/web-components/packages/carbon-web-components/src/components/checkbox/checkbox.scss b/web-components/packages/carbon-web-components/src/components/checkbox/checkbox.scss index 6fda87a421ab..bfb7854db156 100644 --- a/web-components/packages/carbon-web-components/src/components/checkbox/checkbox.scss +++ b/web-components/packages/carbon-web-components/src/components/checkbox/checkbox.scss @@ -8,6 +8,8 @@ $css--plex: true !default; @use '@carbon/styles/scss/config' as *; +@use '@carbon/styles/scss/spacing' as *; +@use '@carbon/styles/scss/theme' as *; @use '@carbon/styles/scss/utilities'; @use '@carbon/styles/scss/components/form'; @use '@carbon/styles/scss/components/checkbox'; @@ -19,6 +21,20 @@ $css--plex: true !default; &[readonly] { @extend .#{$prefix}--checkbox-wrapper--readonly; } + + &[data-table] { + margin: 0; + + &[hide-checkbox] { + pointer-events: none; + + .#{$prefix}--checkbox-label::before, + .#{$prefix}--checkbox-label::after { + background-color: transparent; + border-color: transparent; + } + } + } } :host(#{$prefix}-checkbox-skeleton) { diff --git a/web-components/packages/carbon-web-components/src/components/checkbox/checkbox.ts b/web-components/packages/carbon-web-components/src/components/checkbox/checkbox.ts index b686b4b19ef5..c8b5aaaf72ad 100644 --- a/web-components/packages/carbon-web-components/src/components/checkbox/checkbox.ts +++ b/web-components/packages/carbon-web-components/src/components/checkbox/checkbox.ts @@ -42,6 +42,7 @@ class CDSCheckbox extends FocusMixin(FormMixin(LitElement)) { bubbles: true, composed: true, detail: { + checked, indeterminate, }, }) @@ -68,15 +69,28 @@ class CDSCheckbox extends FocusMixin(FormMixin(LitElement)) { /** * Specify whether the underlying input should be checked */ - @property({ type: Boolean, reflect: true }) + @property({ type: Boolean, reflect: true, attribute: 'checked' }) checked = false; + /** + * Specify if checkbox is being used in a data table + */ + @property({ type: Boolean, reflect: true, attribute: 'data-table' }) + dataTable = false; + /** * Specify whether the Checkbox should be disabled */ @property({ type: Boolean, reflect: true }) disabled = false; + /** + * Specify whether the checkbox should be present in the DOM, + * but invisible and uninteractable. Used for data-table purposes. + */ + @property({ type: Boolean, reflect: true, attribute: 'hide-checkbox' }) + hideCheckbox = false; + /** * Specify whether the label should be hidden, or not */ diff --git a/web-components/packages/carbon-web-components/src/components/data-table/_table-action.scss b/web-components/packages/carbon-web-components/src/components/data-table/_table-action.scss index c83c90e8357f..a7c7f8e8cb34 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/_table-action.scss +++ b/web-components/packages/carbon-web-components/src/components/data-table/_table-action.scss @@ -20,6 +20,9 @@ :host(#{$prefix}-table-toolbar) { @extend .#{$prefix}--table-toolbar; + + display: table-caption; + z-index: 1; } :host(#{$prefix}-table-toolbar-content) { @@ -28,28 +31,12 @@ clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); ::slotted(#{$prefix}-overflow-menu) { - @include button-reset; - display: flex; cursor: pointer; - height: $spacing-04; - width: $spacing-04; - padding: $spacing-05; + height: $spacing-09; + width: $spacing-09; transition: background-color $duration-fast-02 motion(entrance, productive); } - - ::slotted(#{$prefix}-overflow-menu:hover) { - background-color: $background-hover; - } - - ::slotted(#{$prefix}-overflow-menu[disabled]:hover) { - background-color: transparent; - } - - .#{$prefix}--search .#{$prefix}--search-input { - // For tool bar animation with (esp.) persistent search box - background-color: transparent; - } } :host(#{$prefix}-table-toolbar-content[has-batch-actions]) { @@ -71,25 +58,47 @@ height: 100%; .#{$prefix}--search-magnifier { - height: $spacing-04; - width: $spacing-04; - padding: $spacing-05; left: 0; cursor: pointer; pointer-events: all; transition: background $duration-fast-02 motion(entrance, productive); + + &-icon { + height: auto; + width: auto; + } } - .#{$prefix}--search-close { - height: $spacing-04; - width: $spacing-04; + .#{$prefix}--search-input { + border-bottom: 0; + } + .#{$prefix}--search-close { &::before { - top: 2px; - height: calc(100% - 4px); + width: 0; background-color: $background-hover; } + + :hover { + background-color: none; + } } + + :hover { + background-color: none; + } + } + + svg { + left: 0; + } +} + +:host(#{$prefix}-table-toolbar-search[size='xs']), +:host(#{$prefix}-table-toolbar-search[size='sm']) { + svg { + left: $spacing-03; + padding: 0; } } @@ -99,16 +108,25 @@ flex: auto; } -:host(#{$prefix}-table-toolbar-search[persistent]) { - @extend .#{$prefix}--toolbar-search-container-persistent; +:host(#{$prefix}-table-toolbar-search[size='xs'][expanded]), +:host(#{$prefix}-table-toolbar-search[size='sm'][expanded]) { + svg { + left: $spacing-05; + } +} + +:host(#{$prefix}-table-toolbar-search[persistent]:hover), +:host(#{$prefix}-table-toolbar-search[persistent]:hover) { + .#{$prefix}--search-input { + background-color: $field-hover; + } } :host(#{$prefix}-table-batch-actions) { @extend .#{$prefix}--batch-actions; - box-sizing: border-box; -} -:host(#{$prefix}-table-batch-actions[active]) { - @extend .#{$prefix}--batch-actions--active; + &[active] { + @extend .#{$prefix}--batch-actions--active; + } } diff --git a/web-components/packages/carbon-web-components/src/components/data-table/_table-core.scss b/web-components/packages/carbon-web-components/src/components/data-table/_table-core.scss index bd07ff7a0fde..8b021f69c181 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/_table-core.scss +++ b/web-components/packages/carbon-web-components/src/components/data-table/_table-core.scss @@ -10,6 +10,8 @@ // :host(#{$prefix}-table) { + @include layer-tokens.emit-layer-tokens(1); + @extend .#{$prefix}--data-table; display: table; @@ -30,18 +32,69 @@ } } +:host(#{$prefix}-table[use-static-width]) { + width: auto; +} + +:host(#{$prefix}-table[sticky-header]) { + ::slotted(#{$prefix}-table-head), + ::slotted(#{$prefix}-table-body) { + display: flex; + flex-direction: column; + will-change: transform; + } + + .#{$prefix}--data-table-content { + display: block; + overflow-y: scroll; + } +} + +:host(#{$prefix}-table[with-header]) { + .#{$prefix}--data-table-header { + display: table-caption; + } +} + +:host(#{$prefix}-table-head[sticky-header]) { + position: sticky; + z-index: 1; + top: 0; + width: 100%; + will-change: transform; + + ::slotted(#{$prefix}-table-header-row) { + display: flex; + width: 100%; + } +} + +:host(#{$prefix}-table-header-cell[sort-direction]) { + height: $spacing-09; + position: relative; +} + +:host(#{$prefix}-table-header-cell[sticky-header]) { + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + border-bottom: 1px solid $layer-active; +} + +:host(#{$prefix}-table-header-cell[sticky-header]:not([is-sortable])) { + padding-top: rem(14px); +} + // // Common style for table cell and table header cell // :host(#{$prefix}-table-header-row) ::slotted(#{$prefix}-table-header-cell), -:host(#{$prefix}-table-header-row) - ::slotted(#{$prefix}-table-header-cell-skeleton), :host(#{$prefix}-table-header-row) .#{$prefix}--table-column-checkbox, :host(#{$prefix}-table-header-expand-row) ::slotted(#{$prefix}-table-header-cell), -:host(#{$prefix}-table-header-expand-row) - ::slotted(#{$prefix}-table-header-cell-skeleton), :host(#{$prefix}-table-header-expand-row) .#{$prefix}--table-expand, :host(#{$prefix}-table-header-expand-row) .#{$prefix}--table-column-checkbox, :host(#{$prefix}-table-row) ::slotted(#{$prefix}-table-cell), @@ -67,9 +120,7 @@ // // Table header cell // - -:host(#{$prefix}-table-header-row), -:host(#{$prefix}-table-header-expand-row) { +:host(#{$prefix}-table-header-row) { outline: none; ::slotted(#{$prefix}-table-header-cell), @@ -81,29 +132,62 @@ outline: none; } - ::slotted(#{$prefix}-table-header-cell:last-of-type), - ::slotted(#{$prefix}-table-header-cell-skeleton:last-of-type) { - position: relative; - width: auto; - } - ::slotted(#{$prefix}-table-header-cell), - ::slotted(#{$prefix}-table-header-cell-skeleton), .#{$prefix}--table-expand, .#{$prefix}--table-column-checkbox { - padding-left: $spacing-04; - padding-right: $spacing-04; + padding-left: $spacing-05; + padding-right: $spacing-05; + text-align: left; vertical-align: middle; } -} -:host(#{$prefix}-table-header-row) { ::slotted(#{$prefix}-table-header-cell:first-of-type), ::slotted(#{$prefix}-table-header-cell-skeleton:first-of-type) { padding-left: $spacing-05; } } +:host(#{$prefix}-table-header-row:not([batch-expansion])) { + .#{$prefix}--table-expand__button { + display: none; + } +} + +:host(#{$prefix}-table-header-row[selection-name]) { + .#{$prefix}--table-expand { + display: table-cell; + } +} + +:host(#{$prefix}-table-header-row[sticky-header]) { + #{$prefix}-checkbox { + margin: 0; + padding-top: $spacing-04; + border-bottom: 1px solid $layer-active; + } +} + +:host(#{$prefix}-table-header-row[expandable]), +:host(#{$prefix}-table-row[expandable]), +:host(#{$prefix}-table-header-row[selection-name]), +:host(#{$prefix}-table-row[selection-name]) { + .#{$prefix}--table-expand { + height: $spacing-07; + width: $spacing-07; + + padding: $spacing-03; + padding-right: 0; + } +} + +:host(#{$prefix}-table-header-row[expanded]), +:host(#{$prefix}-table-row[expanded]) { + transition: transform $duration-moderate-01 motion(standard, productive); + .#{$prefix}--table-expand__svg { + transform: rotate(270deg); + } +} + // // Table row // @@ -119,10 +203,55 @@ } } +:host(#{$prefix}-table-body[sticky-header]) { + ::slotted(#{$prefix}-table-row), + ::slotted(#{$prefix}-table-expand-row) { + display: flex; + } +} + // // Table cell // +:host(#{$prefix}-table-cell) { + border-top: 1px solid $layer-01; + border-bottom: 1px solid $border-subtle; + padding: 0 $spacing-05; + + ::slotted(#{$prefix}-overflow-menu:hover) { + background-color: none; + } +} + +:host(#{$prefix}-table-cell[overflow-menu-on-hover]) { + ::slotted(*) { + opacity: 0; + } +} + +:host(#{$prefix}-table-cell-content) { + @include type-style('label-01'); + display: block; +} + +:host(#{$prefix}-table-row[sticky-header]), +:host(#{$prefix}-table-expand-row[sticky-header]) { + ::slotted(#{$prefix}-table-cell), + ::slotted(#{$prefix}-table-cell-skeleton) { + width: 100%; + padding-top: rem(14px); + } +} + +:host(#{$prefix}-table-row[size='xl']), +:host(#{$prefix}-table-expand-row[size='xl']) { + ::slotted(#{$prefix}-table-cell), + ::slotted(#{$prefix}-table-cell-skeleton) { + vertical-align: top; + } +} + :host(#{$prefix}-table-row), :host(#{$prefix}-table-expand-row) { ::slotted(#{$prefix}-table-cell), @@ -130,10 +259,6 @@ .#{$prefix}--table-expand, .#{$prefix}--table-column-checkbox { color: $text-secondary; - border-top: 1px solid $layer-01; - border-bottom: 1px solid $border-subtle-01; - padding: rem(14px) $spacing-04; - padding-bottom: rem(13px); vertical-align: middle; } } @@ -150,27 +275,103 @@ } } -:host(#{$prefix}-table-row[even]) { +:host(#{$prefix}-table-row[expandable]), +:host(#{$prefix}-table-row[selection-name]) { + .#{$prefix}--table-expand { + display: table-cell; + border-top: 1px solid $layer-01; + border-bottom: 1px solid $border-subtle; + padding-right: 0; + transition: transform $duration-moderate-01 motion(standard, productive); + } +} + +:host(#{$prefix}-table-row[expandable][expanded]), +:host(#{$prefix}-table-row[selection-name][expanded]) { + .#{$prefix}--table-expand__svg { + transform: rotate(270deg); + } +} + +:host(#{$prefix}-table-row[expandable][expanded]), +:host(#{$prefix}-table-row[selection-name][expandable][expanded]), +:host(#{$prefix}-table-row[expandable][expanded][highlighted]), +:host(#{$prefix}-table-row[selection-name][expandable][expanded][highlighted]) + :host(#{$prefix}-table-row[expandable][expanded]:hover), +:host(#{$prefix}-table-row[selection-name][expandable][expanded]:hover) { + .#{$prefix}--table-expand { + border-bottom-color: transparent; + } +} + +:host(#{$prefix}-table-row[expandable][expanded][selected]), +:host(#{$prefix}-table-row[selection-name][expandable][expanded][selected]) { + ::slotted(#{$prefix}-table-cell), + .#{$prefix}--table-expand, + .#{$prefix}--table-column-checkbox { + background-color: $layer-selected; + + border-top-color: $layer-active; + border-bottom-color: $border-subtle; + } +} + +:host(#{$prefix}-table-row[expandable][expanded][selected][highlighted]), +:host( + #{$prefix}-table-row[selection-name][expandable][expanded][selected][highlighted] + ), +:host(#{$prefix}-table-row[expandable][expanded][selected]:hover), +:host( + #{$prefix}-table-row[selection-name][expandable][expanded][selected]:hover + ) { + ::slotted(#{$prefix}-table-cell), + .#{$prefix}--table-expand, + .#{$prefix}--table-column-checkbox { + background-color: $layer-selected-hover; + } +} + +:host(#{$prefix}-table-row[expandable][selected]), +:host(#{$prefix}-table-row[selection-name][expandable][selected]) { + ::slotted(#{$prefix}-table-cell), + .#{$prefix}--table-expand, + .#{$prefix}--table-column-checkbox { + border-bottom-color: $border-subtle; + } +} + +:host(#{$prefix}-table-row[expandable][highlighted]), +:host(#{$prefix}-table-row[selection-name][expandable][highlighted]), +:host(#{$prefix}-table-row[expandable]:hover), +:host(#{$prefix}-table-row[selection-name][expandable]:hover) { + ::slotted(#{$prefix}-table-cell), + .#{$prefix}--table-expand, + .#{$prefix}--table-column-checkbox { + border-bottom-color: $border-subtle; + } +} + +:host(#{$prefix}-table-row[odd]) { + .#{$prefix}--table-expand, + .#{$prefix}--table-column-checkbox, ::slotted(#{$prefix}-table-cell), ::slotted(#{$prefix}-table-cell-skeleton) { border-bottom: 1px solid $layer-01; } } -:host(#{$prefix}-table-row[odd]) { +:host(#{$prefix}-table-row[even]) { + .#{$prefix}--table-expand, + .#{$prefix}--table-column-checkbox, ::slotted(#{$prefix}-table-cell), ::slotted(#{$prefix}-table-cell-skeleton) { background-color: $layer-accent-01; border-bottom: 1px solid $layer-accent-01; border-top: 1px solid $layer-accent-01; - - // TODO: confirm if this token is deprecated - // background-color: $data-table-zebra-color; - // border-bottom: 1px solid $data-table-zebra-color; - // border-top: 1px solid $data-table-zebra-color; } } +:host(#{$prefix}-table-row[highlighted]), :host(#{$prefix}-table-row:hover), :host(#{$prefix}-table-expand-row:hover) { ::slotted(#{$prefix}-table-cell), @@ -184,8 +385,11 @@ } } +:host(#{$prefix}-table-row[highlighted]), :host(#{$prefix}-table-row[even]:hover), :host(#{$prefix}-table-row[odd]:hover) { + .#{$prefix}--table-expand, + .#{$prefix}--table-column-checkbox, ::slotted(#{$prefix}-table-cell), ::slotted(#{$prefix}-table-cell-skeleton) { background-color: $background-hover; @@ -193,3 +397,23 @@ border-top: 1px solid $layer-hover-01; } } + +:host(#{$prefix}-table-header-title) { + @extend .#{$prefix}--data-table-header__title; + + display: block; +} + +:host(#{$prefix}-table-header-description) { + @extend .#{$prefix}--data-table-header__description; + + display: block; + + @include breakpoint(md) { + max-width: 50ch; + } + + @include breakpoint(lg) { + max-width: 80ch; + } +} diff --git a/web-components/packages/carbon-web-components/src/components/data-table/_table-expandable.scss b/web-components/packages/carbon-web-components/src/components/data-table/_table-expandable.scss index d2d04960c09f..c2affac8049f 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/_table-expandable.scss +++ b/web-components/packages/carbon-web-components/src/components/data-table/_table-expandable.scss @@ -5,59 +5,52 @@ // LICENSE file in the root directory of this source tree. // -:host(#{$prefix}-table-expand-row), -:host(#{$prefix}-table-header-expand-row) { - .#{$prefix}--table-expand { - display: table-cell; - width: 2.5rem; - height: 3rem; - vertical-align: middle; - padding: 0; - border-bottom: 1px solid $border-subtle-01; - transition: transform $duration-moderate-01 motion(standard, productive); - } - - &[expanded] .#{$prefix}--table-expand { - border-bottom: 1px solid transparent; - - .#{$prefix}--table-expand__svg { - transform: rotate(270deg); - } - } -} - :host(#{$prefix}-table-expanded-row) { display: table-row; - transition: height $duration-moderate-02 motion(standard), - background-color $duration-fast-02 motion(standard); + height: 0; + transition: height $duration-moderate-01 motion(standard); - &[expanded] { - height: $spacing-09; + ::slotted(*) { + color: $text-secondary; } td { - padding-left: $spacing-10; + border-bottom-color: $border-subtle; vertical-align: middle; transition: all $duration-fast-02 motion(standard, productive); + padding: 0; + padding-left: $spacing-10; .#{$prefix}--child-row-inner-container { overflow: hidden; height: 0; } } +} - &[expanded] td { - border-bottom: 1px solid $border-subtle-01; - padding-top: rem(14px); - padding-bottom: rem(13px); +:host(#{$prefix}-table-expanded-row[expanded]) { + height: $spacing-09; + + td { + border-bottom: 1px solid $border-subtle; height: auto; .#{$prefix}--child-row-inner-container { height: auto; } } +} + +:host(#{$prefix}-table-expanded-row:hover), +:host(#{$prefix}-table-expanded-row[selected]), +:host(#{$prefix}-table-expanded-row[highlighted]) { + background-color: $background-hover; - &[highlighted] { - background-color: $background-hover; + ::slotted(*) { + color: $text-primary; } } + +:host(#{$prefix}-table-expanded-row[selected][highlighted]) { + background-color: $layer-selected; +} diff --git a/web-components/packages/carbon-web-components/src/components/data-table/_table-selection.scss b/web-components/packages/carbon-web-components/src/components/data-table/_table-selection.scss index 75c9d2d14c9d..166915f35b78 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/_table-selection.scss +++ b/web-components/packages/carbon-web-components/src/components/data-table/_table-selection.scss @@ -8,36 +8,46 @@ // // Selection check box // +:host(#{$prefix}-table-header-row) { + .#{$prefix}--table-column-checkbox { + border-top: none; + border-bottom: none; -:host(#{$prefix}-table-header-row) .#{$prefix}--table-column-checkbox { - position: relative; - background-color: $border-subtle-01; - // 16px padding left + 8px padding right + 20px checkbox width - width: rem(44px); - transition: background-color $duration-fast-01 motion(entrance, productive); -} - -:host(#{$prefix}-table-row) .#{$prefix}--table-column-checkbox { - .#{$prefix}--checkbox-label { padding-left: $spacing-05; + padding-right: $spacing-02; + transition: background-color $duration-fast-01 motion(entrance, productive); + + .#{$prefix}--checkbox-label { + width: 20px; + } } } -:host(#{$prefix}-table-header-row) .#{$prefix}--table-column-checkbox:hover { - // Highlight "select all" columns like sorting column - background-color: $data-table-column-hover; -} +:host(#{$prefix}-table-row) { + .#{$prefix}--table-column-checkbox { + padding-left: $spacing-05; + padding-right: $spacing-02; + border-top: 1px solid $layer-01; + border-bottom: 1px solid $border-subtle; -:host(#{$prefix}-table-row[size='tall']) .#{$prefix}--table-column-checkbox { - padding-top: rem(12px); + .#{$prefix}--checkbox-label { + padding-left: $spacing-05; + } + } } -// -// Selected rows -// +:host(#{$prefix}-table-row:hover) { + .#{$prefix}--table-column-checkbox { + border-top-color: $layer-hover-01; + border-bottom-color: $layer-hover-01; + background-color: $background-hover; + } +} :host(#{$prefix}-table-row[selected]) { + .#{$prefix}--table-column-checkbox, ::slotted(#{$prefix}-table-cell), + .#{$prefix}--table-expand, .#{$prefix}--table-column-checkbox { color: $text-primary; background-color: $layer-accent-01; @@ -48,10 +58,16 @@ } :host(#{$prefix}-table-row[selected]:hover) { + .#{$prefix}--table-column-checkbox, ::slotted(#{$prefix}-table-cell), + .#{$prefix}--table-expand, .#{$prefix}--table-column-checkbox { background-color: $data-table-column-hover; - border-top-color: $data-table-column-hover; border-bottom-color: $data-table-column-hover; } } + +:host(#{$prefix}-table-expanded-row[filtered]), +:host(#{$prefix}-table-row[filtered]) { + display: none !important; +} diff --git a/web-components/packages/carbon-web-components/src/components/data-table/_table-sizes.scss b/web-components/packages/carbon-web-components/src/components/data-table/_table-sizes.scss index bd810aa13cf3..369efaffe3dc 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/_table-sizes.scss +++ b/web-components/packages/carbon-web-components/src/components/data-table/_table-sizes.scss @@ -5,8 +5,6 @@ // LICENSE file in the root directory of this source tree. // -// TODO: consider consolidating size classes with css vars - // // "xs" table size variant // @@ -16,11 +14,22 @@ height: $spacing-06; } +:host(#{$prefix}-table-header-row[size='xs']), :host(#{$prefix}-table-row[size='xs']) { - ::slotted(#{$prefix}-table-cell), - ::slotted(#{$prefix}-table-cell-skeleton) { + ::slotted(#{$prefix}-table-header-cell) { + height: $spacing-06; + } + + ::slotted(#{$prefix}-table-cell) { padding-top: rem(2px); padding-bottom: rem(2px); + padding-left: $spacing-05; + } + + .#{$prefix}--table-expand { + padding-top: 0; + padding-bottom: 0; + height: $spacing-06; } } @@ -33,11 +42,25 @@ height: $spacing-07; } +:host(#{$prefix}-table-header-row[size='sm']), :host(#{$prefix}-table-row[size='sm']) { - ::slotted(#{$prefix}-table-cell), - ::slotted(#{$prefix}-table-cell-skeleton) { + ::slotted(#{$prefix}-table-header-cell) { + height: $spacing-07; + } + + ::slotted(#{$prefix}-table-cell) { padding-top: rem(7px); padding-bottom: rem(6px); + padding-left: $spacing-05; + } + + .#{$prefix}--table-expand { + padding-top: 0; + padding-bottom: 0; + } + + ::slotted(#{$prefix}-overflow-menu) { + height: $spacing-07; } } @@ -50,11 +73,24 @@ height: $spacing-08; } -:host(#{$prefix}-table-row[size='md']) { - ::slotted(#{$prefix}-table-cell), - ::slotted(#{$prefix}-table-cell-skeleton) { - padding-top: rem(4px); - padding-bottom: rem(4px); +:host(#{$prefix}-table-row[size='md']), +:host(#{$prefix}-table-header-row[size='md']) { + ::slotted(#{$prefix}-table-header-cell) { + height: $spacing-08; + } + ::slotted(#{$prefix}-table-cell) { + padding-top: rem(7px); + padding-left: $spacing-05; + padding-bottom: rem(6px); + } + + .#{$prefix}--table-expand { + padding-top: $spacing-02; + padding-bottom: $spacing-02; + } + + ::slotted(#{$prefix}-overflow-menu) { + height: $spacing-08; } } @@ -67,13 +103,6 @@ height: $spacing-09; } -:host(#{$prefix}-table-row[size='lg']) { - ::slotted(#{$prefix}-table-cell), - ::slotted(#{$prefix}-table-cell-skeleton) { - padding-top: rem(16px); - } -} - // // "xl" table size variant // @@ -83,9 +112,122 @@ height: $spacing-10; } +:host(#{$prefix}-table-header-row[size='xl']) { + ::slotted(#{$prefix}-table-header-cell) { + vertical-align: top; + padding-top: $spacing-05; + } +} + +:host(#{$prefix}-table-header-row[size='xl']), :host(#{$prefix}-table-row[size='xl']) { - ::slotted(#{$prefix}-table-cell), - ::slotted(#{$prefix}-table-cell-skeleton) { + ::slotted(#{$prefix}-table-cell) { padding-top: rem(16px); } + + .#{$prefix}--table-expand { + padding-bottom: 1.375rem; + } + + .#{$prefix}--table-expand, + .#{$prefix}--table-column-checkbox { + vertical-align: top; + padding-top: rem(10px); + } + + &[radio] { + .#{$prefix}--table-column-checkbox { + padding-top: $spacing-05; + } + } +} + +:host(#{$prefix}-table-expanded-row[size='xl'][expanded]) { + td { + padding-top: $spacing-05; + padding-right: $spacing-05; + padding-bottom: $spacing-03; + } +} + +// Small persistent toolbar +:host(#{$prefix}-table-toolbar[size='xs']), +:host(#{$prefix}-table-toolbar-content[size='xs']) { + &, + ::slotted(#{$prefix}-table-toolbar-search), + ::slotted(#{$prefix}-overflow-menu), + ::slotted(#{$prefix}-button) { + min-height: $spacing-07; + height: $spacing-07; + } + ::slotted(#{$prefix}-table-toolbar-search), + ::slotted(#{$prefix}-overflow-menu) { + width: 2rem; + } +} + +:host(#{$prefix}-table-toolbar[size='sm']), +:host(#{$prefix}-table-toolbar-content[size='sm']) { + &, + ::slotted(#{$prefix}-table-toolbar-search), + ::slotted(#{$prefix}-overflow-menu), + ::slotted(#{$prefix}-button) { + min-height: $spacing-07; + height: $spacing-07; + } + + ::slotted(#{$prefix}-table-toolbar-search), + ::slotted(#{$prefix}-overflow-menu) { + width: 2rem; + } +} + +// +// "selection" and "expandable" sizes +// +:host(#{$prefix}-table-header-row[selection-name][expandable]), +:host(#{$prefix}-table-row[selection-name][expandable]) { + ::slotted(#{$prefix}-table-header-cell), + ::slotted(#{$prefix}-table-cell) { + padding-left: $spacing-03; + } +} + +:host(#{$prefix}-table-header-row[selection-name][expandable][size='xs']), +:host(#{$prefix}-table-row[selection-name][expandable][size='xs']) { + .#{$prefix}--table-column-checkbox { + padding: 0 rem(6px); + } +} + +:host(#{$prefix}-table-header-row[selection-name][expandable][size='sm']), +:host(#{$prefix}-table-row[selection-name][expandable][size='sm']), +:host(#{$prefix}-table-header-row[selection-name][expandable][size='md']), +:host(#{$prefix}-table-row[selection-name][expandable][size='md']) { + .#{$prefix}--table-column-checkbox { + padding: rem(3px) rem(6px); + } +} + +:host(#{$prefix}-table-header-row[selection-name][expandable][size='xl']) { + .#{$prefix}--table-column-checkbox { + padding-left: rem(6px); + padding-right: rem(6px); + } +} + +// +// table cell size for overflow menu +// +:host(#{$prefix}-table-cell[size='xs']), +:host(#{$prefix}-table-cell[size='sm']) { + ::slotted(#{$prefix}-overflow-menu) { + height: calc(100% + 1px); + } +} + +:host(#{$prefix}-table-cell[size='md']) { + ::slotted(#{$prefix}-overflow-menu) { + height: $spacing-08; + } } diff --git a/web-components/packages/carbon-web-components/src/components/data-table/_table-skeleton.scss b/web-components/packages/carbon-web-components/src/components/data-table/_table-skeleton.scss deleted file mode 100644 index 78304e5537f2..000000000000 --- a/web-components/packages/carbon-web-components/src/components/data-table/_table-skeleton.scss +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright IBM Corp. 2019, 2023 -// -// This source code is licensed under the Apache-2.0 license found in the -// LICENSE file in the root directory of this source tree. -// - -@use '@carbon/styles/scss/config' as *; -@use '@carbon/styles/scss/utilities' as *; -@use '@carbon/styles/scss/theme' as *; - -:host(#{$prefix}-table-header-cell-skeleton), -:host(#{$prefix}-table-cell-skeleton) { - span { - @include skeleton; - - width: 75%; - height: 1rem; - display: block; - } -} - -:host(#{$prefix}-table-header-cell-skeleton) { - // TODO: audit - border-bottom: 1px solid $background-brand; - vertical-align: middle; -} diff --git a/web-components/packages/carbon-web-components/src/components/data-table/_table-sort.scss b/web-components/packages/carbon-web-components/src/components/data-table/_table-sort.scss index 8baeb6cbf517..fb927d3d2f10 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/_table-sort.scss +++ b/web-components/packages/carbon-web-components/src/components/data-table/_table-sort.scss @@ -9,21 +9,6 @@ @use '@carbon/styles/scss/spacing' as *; @use '@carbon/styles/scss/components/data-table/sort'; -:host(#{$prefix}-table-header-row[size='compact']) - ::slotted(#{$prefix}-table-header-cell[sort-direction]) { - height: rem(24px); -} - -:host(#{$prefix}-table-header-row[size='short']) - ::slotted(#{$prefix}-table-header-cell[sort-direction]) { - height: rem(32px); -} - -:host(#{$prefix}-table-header-row[size='tall']) - ::slotted(#{$prefix}-table-header-cell[sort-direction]) { - height: rem(64px); -} - // Padding of header cell for sorting // // Let sort button rather than the cell define the padding @@ -36,17 +21,16 @@ padding-right: 0; } -:host(#{$prefix}-table-header-cell[sort-direction]) .#{$prefix}--table-sort { - height: 100%; - padding-left: $spacing-04; - padding-right: $spacing-04; -} - :host(#{$prefix}-table-header-cell[sort-direction]:first-of-type) .#{$prefix}--table-sort { padding-left: $spacing-05; } +:host(#{$prefix}-table-header-cell[sort-direction][expandable][selection-name]) + .#{$prefix}--table-sort { + padding-left: 0; +} + // // Sort icon style // diff --git a/web-components/packages/carbon-web-components/src/components/data-table/data-table-story.mdx b/web-components/packages/carbon-web-components/src/components/data-table/data-table-story.mdx deleted file mode 100644 index ae34dd328847..000000000000 --- a/web-components/packages/carbon-web-components/src/components/data-table/data-table-story.mdx +++ /dev/null @@ -1,336 +0,0 @@ -import { Props, Description } from '@storybook/addon-docs/blocks'; -import { cdnJs, cdnCss } from '../../globals/internal/storybook-cdn'; - -# Data table - -> 💡 Check our -> [CodeSandbox](https://codesandbox.io/s/github/carbon-design-system/carbon-for-ibm-dotcom/tree/main/packages/carbon-web-components/examples/codesandbox/basic/components/data-table) -> example implementation. - -[![Edit carbon-web-components](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/carbon-design-system/carbon-for-ibm-dotcom/tree/main/packages/carbon-web-components/examples/codesandbox/basic/components/data-table) - -## Getting started - -Here's a quick example to get you started. - -Data table in `carbon-web-components` focuses on primitives that constructs -table UI, consisting of the following: - -| Tag | Description | HTML tag counterpart | -| ------------------------ | --------------- | -------------------- | -| `` | The container | N/A | -| `` | The table | `` | -| `` | The header | `` | -| `` | The header row | `` in `` | -| `` | The header cell | `` | -| `` | The header row | `` in `` | -| `` | The header cell | `
` | -| `` | The header body | `
` | - -### JS (via import) - -```javascript -import '@carbon/web-components/es/components/data-table/index.js'; -``` - - - - -### HTML - -```html - - - - - Foo - Bar - Baz - - - - - Foo1 - Bar1 - Baz1 - - - Foo2 - Bar2 - Baz2 - - - Foo3 - Bar3 - Baz3 - - - - -``` - -## Row selection - -Adding `selection-name` attribute to ``/`` -shows the selection UI in the table row. - -The logic of row selection needs to be handled on by the application code. When -user attempts to change row selection, the following events fire: - -| Element | Event | Description | -| ----------------------- | ------------------------------- | ------------------------------------------------------------- | -| `` | `cds-table-row-change-selection` | Indicates user-initiated change in selection of a single row. | -| `` | `cds-table-change-selection-all` | Indicates user-initiated change in selection of all rows. | - -Both of above events have the following properties in the event details: - -| Property | Description | -| ---------- | ------------------------ | -| `selected` | The new selection state. | - -Both of above events bubble and are cancelable. Canceling those events mean -canceling change in selection. - -Application code should listen to those events update its internal state of -table rows. To do so, it's convenient to add a `data-` attribte to each table -rows so the delegated event handler can see which row the events are from. - -Here's an example of [`lit-html`](https://lit-html.polymer-project.org). Note -that those events are native DOM events, and thus something like -`document.addEventListener('cds-table-row-change-selection', event => { ... })` -works, too: - -```javascript -/** - * Handles an event to change in selection of rows, fired from ``, - * to update application's row selection states. - * @param {CustomEvent} event The event. - */ -const handleChangeSelection = event => { - if (!event.defaultPrevented) { - rows = this.rows.map(row => { - // The row ID you set to `` the event is fired on - const targetRowId = event.target.dataset.rowId; - return targetRowId !== row.id - ? row - : { - ...row, - selected: event.detail.selected, - }; - }); - } -}; - -/** - * Handles an event to change in selection of all rows, fired from ``, - * to update application's row selection states. - * @param {CustomEvent} event The event. - */ -const handleChangeSelectionAll = event => { - if (!event.defaultPrevented) { - rows = this.rows.map(row => ({ - ...row, - selected: event.detail.selected, - })); - } -}; - -... - -html` - - - - row.selected)} selection-name="my-row-selection-all"> - Foo - Bar - Baz - - - - ${rows.map( - row => html` - - ${row.foo} - ${row.bar} - ${row.baz} - - ` - )} - - - -`; -``` - -## Sorting - -Adding `sort-direction` attribute to `` shows the sorting -UI in the table header cell. - -The logic of sorting needs to be handled on by the application code. When user -attempts to change sorting, `cds-table-header-cell-sort` event is fired, with the -following properties in the event details: - -| Property | Description | -| ------------------ | ----------------------- | -| `oldSortDirection` | The old sort direction. | -| `sortDirection` | The new sort direction. | - -`cds-table-header-cell-sort` event bubbles and is cancelable. Canceling -`cds-table-header-cell-sort` event means canceling change in sort direction. - -Application code should listen to those events update its internal state of -sorting. An example of the logic needed is available in our codesandbox example -above. To do so, it's convenient to add a `data-` attribte to each table header -cells so the delegated event handler can see which table column the events are -from. - -Here's an example of [`lit-html`](https://lit-html.polymer-project.org). Note -that those events are native DOM events, and thus something like -`document.addEventListener('cds-table-header-cell-sort', event => { ... })` -works, too: - -```javascript -/** - * Handles an event to sort rows, fired from ``, to update application's sorting state. - * @param {CustomEvent} event The event. - */ -const handleChangeSort = event => { - if (!defaultPrevented) { - // Changes the sorting state upon user gesture - sortInfo = { - columnId: event.target.dataset.columnId, - direction: event.detail.direction, - }; - } -}; - -... - -const collator = new Intl.Collator('en'); // Use the locale user is on - -/** - * Rows sorted by application's sorting state. - */ -const sortedRows = rows.slice().sort((lhs, rhs) => { - const lhsValue = lhs[sortInfo.columnId]; - const rhsValue = rhs[sortInfo.columnId]; - return (sortInfo.direction === 'ascending' ? 1 : -1) * collator.compare(lhsValue, rhsValue); -}); - -html` - - - - - Foo - Bar - Baz - - - - ${sortedRows.map( - row => html` - - ${row.foo} - ${row.bar} - ${row.baz} - - ` - )} - - - -`; -``` - -## Skeleton - -For the skeleton variation, utilize ``. - -```html - - - - Name - Protocol - Port - Rule - Attached Groups - Status - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -## Attributes, properties and events - -Note: For `boolean` attributes, `true` means simply setting the attribute (e.g. -``) and `false` means not setting the attribute (e.g. -`` without `sort` attribute). - -### `` - - - -### `` - - - -### `` - - - -### `` - - - -### `` - - - -### `` - - - -### `` - - - -### `` - - diff --git a/web-components/packages/carbon-web-components/src/components/data-table/data-table-story.scss b/web-components/packages/carbon-web-components/src/components/data-table/data-table-story.scss deleted file mode 100644 index e978c85f941f..000000000000 --- a/web-components/packages/carbon-web-components/src/components/data-table/data-table-story.scss +++ /dev/null @@ -1,14 +0,0 @@ -// -// Copyright IBM Corp. 2020, 2023 -// -// This source code is licensed under the Apache-2.0 license found in the -// LICENSE file in the root directory of this source tree. -// - -@use '@carbon/styles/scss/config' as *; - -/* stylelint-disable selector-type-no-unknown */ -#{$prefix}-ce-demo-data-table, -.#{$prefix}-ce-demo--data-table { - width: 100%; -} diff --git a/web-components/packages/carbon-web-components/src/components/data-table/data-table-story.ts b/web-components/packages/carbon-web-components/src/components/data-table/data-table-story.ts deleted file mode 100644 index 5c305448a442..000000000000 --- a/web-components/packages/carbon-web-components/src/components/data-table/data-table-story.ts +++ /dev/null @@ -1,916 +0,0 @@ -/** - * @license - * - * Copyright IBM Corp. 2019, 2023 - * - * This source code is licensed under the Apache-2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - -import debounce from 'lodash-es/debounce'; -import { LitElement, html } from 'lit'; -import { property } from 'lit/decorators.js'; -import { repeat } from 'lit/directives/repeat.js'; -import { ifDefined } from 'lit/directives/if-defined.js'; -import { action } from '@storybook/addon-actions'; -import { boolean, select } from '@storybook/addon-knobs'; -// Below path will be there when an application installs `@carbon/web-components` package. -// In our dev env, we auto-generate the file and re-map below path to to point to the generated file. -// @ts-ignore -import TrashCan from '@carbon/web-components/es/icons/trash-can/16'; -// @ts-ignore -import Download16 from '@carbon/web-components/es/icons/download/16'; -// @ts-ignore -import Settings16 from '@carbon/web-components/es/icons/settings/16'; -import { prefix } from '../../globals/settings'; -import BXBtn from '../button/button'; -import '../overflow-menu/overflow-menu'; -import '../overflow-menu/overflow-menu-body'; -import '../overflow-menu/overflow-menu-item'; -import '../pagination/pagination'; -import { TABLE_COLOR_SCHEME, TABLE_SIZE } from './table'; -import './table-head'; -import './table-header-row'; -import { TABLE_SORT_DIRECTION } from './table-header-cell'; -import './table-body'; -import './table-row'; -import './table-cell'; -import './table-header-expand-row'; -import './table-expand-row'; -import './table-expanded-row'; -import './table-toolbar'; -import './table-toolbar-content'; -import './table-toolbar-search'; -import './table-batch-actions'; -import './table-header-cell-skeleton'; -import './table-cell-skeleton'; -import { - rows as demoRows, - rowsMany as demoRowsMany, - columns as demoColumns, - sortInfo as demoSortInfo, -} from './stories/data'; -import { - TDemoTableColumn, - TDemoTableRow, - TDemoSortInfo, -} from './stories/types'; -import styles from './data-table-story.scss'; -import storyDocs from './data-table-story.mdx'; - -/** - * @param row A table row. - * @param searchString A search string. - * @returns `true` if the given table row matches the given search string. - */ -const doesRowMatchSearchString = (row: TDemoTableRow, searchString: string) => - Object.keys(row).some( - (key) => key !== 'id' && String(row[key] ?? '').indexOf(searchString) >= 0 - ); - -/** - * A class to manage table states, like selection and sorting. - * DEMONSTRATION-PURPOSE ONLY. - * Data/state handling in data table tends to involve lots of application-specific logics - * and thus abstracting everything in a library won't be a good return on investment - * vs. letting users copy code here and implement features that fit their needs. - */ -// @ts-ignore `BXCEDemoDataTable` is used (only) for type reference -// eslint-disable-next-line @typescript-eslint/no-unused-vars -class BXCEDemoDataTable extends LitElement { - /** - * The debounced handler for user-initiated change in search string. - */ - private _handleChangeSearchString: - | ((() => void) & { cancel(): void }) - | void = undefined; - - /** - * The table sorting info reflecting user-initiated changes. - */ - private _sortInfo?: TDemoSortInfo; - - /** - * The table rows reflecting selection. - */ - private _rows?: TDemoTableRow[]; - - /** - * The table rows reflecting selection and filtering. - */ - private _filteredRows?: TDemoTableRow[]; - - /** - * The search string. - */ - private _searchString = ''; - - /** - * Unique ID used for form elements. - */ - protected _uniqueId = Math.random().toString(36).slice(2); - - /** - * @param lhs A value. - * @param rhs Another value. - * @returns - * `0` if the given two values are equal - * A negative value to sort `lhs` to an index lower than `rhs` - * A positive value to sort `rhs` to an index lower than `lhs` - */ - private _compare(lhs, rhs) { - if (typeof lhs === 'number' && typeof rhs === 'number') { - return lhs - rhs; - } - return this.collator.compare(lhs, rhs); - } - - /** - * Handles Cancel button in batch action bar. - */ - private _handleCancelSelection() { - const { _rows: oldRows, _searchString: searchString } = this; - this._rows = this._rows!.map((row) => - searchString && !doesRowMatchSearchString(row, searchString) - ? row - : { ...row, selected: false } - ); - this.requestUpdate('_rows', oldRows); - } - - /** - * Handles user-initiated change in search string. - */ - private _handleChangeSearchStringImpl({ detail }: CustomEvent) { - const { _searchString: oldSearchString } = this; - this._searchString = detail.value; - this.requestUpdate('_searchString', oldSearchString); - } - - /** - * Handles an event to change in selection of rows, fired from ``. - * - * @param event The event. - */ - private _handleChangeSelection({ - defaultPrevented, - detail, - target, - }: CustomEvent) { - if (!defaultPrevented) { - const { rowId: changedRowId } = (target as HTMLElement).dataset; - const { selected } = detail; - const { _rows: oldRows } = this; - this._rows = oldRows!.map((row) => - Number(changedRowId) !== row.id ? row : { ...row, selected } - ); - this.requestUpdate('_rows', oldRows); - } - } - - /** - * Handles an event to change in selection of all rows, fired from ``. - * - * @param event The event. - */ - private _handleChangeSelectionAll({ defaultPrevented, detail }: CustomEvent) { - if (!defaultPrevented) { - const { selected } = detail; - const { _rows: oldRows, _searchString: searchString } = this; - this._rows = this._rows!.map((row) => - searchString && !doesRowMatchSearchString(row, searchString) - ? row - : { ...row, selected } - ); - this.requestUpdate('_rows', oldRows); - } - } - - /** - * Handles an event to sort rows, fired from ``. - * - * @param event The event. - */ - private _handleChangeSort({ defaultPrevented, detail, target }: CustomEvent) { - if (!defaultPrevented) { - const { columnId } = (target as HTMLElement).dataset; - const { sortDirection: direction } = detail; - const { _sortInfo: oldSortInfo } = this; - if (direction === TABLE_SORT_DIRECTION.NONE && columnId !== 'name') { - // Resets the sorting, given non-primary sorting column has got in non-sorting state - this._sortInfo = this.sortInfo; - } else { - // Sets the sorting as user desires - this._sortInfo = { - columnId: columnId!, - direction, - }; - } - this.requestUpdate('_sortInfo', oldSortInfo); - } - } - - /** - * Handles `cds-pagination-changed-current` event on the pagination UI. - * - * @param event The event. - */ - private _handleChangeStart({ detail }: CustomEvent) { - this.start = detail.start; - } - - /** - * Handles `cds-pages-select-changed` event on the pagination UI. - * - * @param event The event. - */ - private _handleChangePageSize({ detail }: CustomEvent) { - this.pageSize = detail.value; - } - - /** - * Handles Delete batch action button. - */ - private _handleDeleteRows() { - const { _rows: oldRows, _searchString: searchString } = this; - this._rows = oldRows!.filter( - (row) => !row.selected || !doesRowMatchSearchString(row, searchString) - ); - this.requestUpdate('_rows', oldRows); - } - - /** - * Handles Download batch action button. - * - * @param event The event triggering this action. - */ - private _handleDownloadRows({ target }: MouseEvent) { - const blob = new Blob( - [JSON.stringify(this._filteredRows!.filter((row) => row.selected))], - { type: 'application/json' } - ); - (target as BXBtn).href = URL.createObjectURL(blob); - this._handleCancelSelection(); - } - - /** - * @returns The content of the pagination UI. - */ - private _renderPagination() { - const { - pageSize, - start, - _filteredRows: filteredRows, - _handleChangeStart: handleChangeStart, - _handleChangePageSize: handleChangePageSize, - } = this; - if (typeof pageSize === 'undefined') { - return undefined; - } - return html` - - - - - - - - - `; - } - - /** - * The g11n collator to use. - */ - @property({ attribute: false }) - collator = new Intl.Collator(); - - /** - * Data table columns. - */ - @property({ attribute: false }) - columns?: TDemoTableColumn[]; - - /** - * Data table rows. - */ - @property({ attribute: false }) - rows?: TDemoTableRow[]; - - /** - * Table sorting info. - */ - @property({ attribute: false }) - sortInfo?: TDemoSortInfo; - - /** - * `true` if the the table should support selection UI. - */ - @property({ type: Boolean, reflect: true, attribute: 'has-selection' }) - hasSelection = false; - - /** - * Number of items per page. - */ - @property({ type: Number, attribute: 'page-size' }) - pageSize!: number; - - /** - * The table size. - */ - @property({ reflect: true }) - size = TABLE_SIZE.MD; - - /** - * The table color scheme. - */ - @property({ reflect: true, attribute: 'color-scheme' }) - colorScheme = TABLE_COLOR_SCHEME.REGULAR; - - /** - * The row number where current page start with, index that starts with zero. - */ - @property({ type: Number }) - start = 0; - - connectedCallback() { - super.connectedCallback(); - if (this._handleChangeSearchString) { - this._handleChangeSearchString.cancel(); - } - this._handleChangeSearchString = debounce( - this._handleChangeSearchStringImpl as () => void, - 500 - ); - } - - disconnectedCallback() { - if (this._handleChangeSearchString) { - this._handleChangeSearchString.cancel(); - this._handleChangeSearchString = undefined; - } - super.disconnectedCallback(); - } - - shouldUpdate(changedProperties) { - if (changedProperties.has('sortInfo')) { - this._sortInfo = this.sortInfo; - } - if (changedProperties.has('rows')) { - this._rows = this.rows; - } - if ( - changedProperties.has('rows') || - changedProperties.has('_rows') || - changedProperties.has('_searchString') - ) { - const { - pageSize, - start, - _rows: rows, - _searchString: searchString, - } = this; - this._filteredRows = !searchString - ? rows! - : rows!.filter((row) => doesRowMatchSearchString(row, searchString)); - const count = this._filteredRows.length; - if (count > 0 && start >= count) { - this.start = Math.max( - start - Math.ceil((start - count) / pageSize) * pageSize, - 0 - ); - } - } - return true; - } - - render() { - const { - id: elementId, - colorScheme, - hasSelection, - pageSize = Infinity, - start = 0, - size, - columns, - _filteredRows: filteredRows, - _handleCancelSelection: handleCancelSelection, - _handleDeleteRows: handleDeleteRows, - _handleDownloadRows: handleDownloadRows, - } = this; - const selectionAllName = !hasSelection - ? undefined - : `__${prefix}-ce-demo-data-table_select-all_${ - elementId || this._uniqueId - }`; - const selectedRowsCountInFiltered = filteredRows!.filter( - ({ selected }) => selected! - ).length; - const selectedAllInFiltered = - selectedRowsCountInFiltered > 0 && - filteredRows!.length === selectedRowsCountInFiltered; - const hasBatchActions = hasSelection && selectedRowsCountInFiltered > 0; - const { columnId: sortColumnId, direction: sortDirection } = - this._sortInfo!; - const sortedRows = - sortDirection === TABLE_SORT_DIRECTION.NONE - ? filteredRows! - : filteredRows! - .slice() - .sort( - (lhs, rhs) => - (this.constructor as typeof BXCEDemoDataTable).collationFactors[ - sortDirection - ] * this._compare(lhs[sortColumnId!], rhs[sortColumnId!]) - ); - return html` - - - Delete ${TrashCan({ slot: 'icon' })} - - Download ${Download16({ slot: 'icon' })} - - - - - - ${Settings16({ slot: 'icon' })} - - Action 1 - Action 2 - Action 3 - - - Primary Button - - - - - - ${repeat( - columns!, - ({ id: columnId }) => columnId, - ({ id: columnId, sortCycle, title }) => { - const sortDirectionForThisCell = - sortCycle && - (columnId === sortColumnId - ? sortDirection - : TABLE_SORT_DIRECTION.NONE); - return html` - - ${title} - - `; - } - )} - - - - ${repeat( - sortedRows.slice(start, start + pageSize), - ({ id: rowId }) => rowId, - (row) => { - const { id: rowId, selected } = row; - const selectionName = !hasSelection - ? undefined - : `__${prefix}-ce-demo-data-table_${ - elementId || this._uniqueId - }_${rowId}`; - const selectionValue = !hasSelection ? undefined : 'selected'; - return html` - - ${repeat( - columns!, - ({ id: columnId }) => columnId, - ({ id: columnId }) => - html` ${row[columnId]} ` - )} - - `; - } - )} - - - ${this._renderPagination()} - `; - } - - /** - * The map of how sorting direction affects sorting order. - */ - static collationFactors = { - [TABLE_SORT_DIRECTION.ASCENDING]: 1, - [TABLE_SORT_DIRECTION.DESCENDING]: -1, - }; -} - -const colorSchemes = { - 'Regular color scheme': null, - [`Zebra (${TABLE_COLOR_SCHEME.ZEBRA})`]: TABLE_COLOR_SCHEME.ZEBRA, -}; - -const sizes = { - [`xs (${TABLE_SIZE.XS})`]: TABLE_SIZE.XS, - [`sm (${TABLE_SIZE.SM})`]: TABLE_SIZE.SM, - [`md (${TABLE_SIZE.MD})`]: TABLE_SIZE.MD, - [`lg (${TABLE_SIZE.LG} - default)`]: TABLE_SIZE.LG, - [`xl (${TABLE_SIZE.XL})`]: TABLE_SIZE.XL, -}; - -const defineDemoDataTable = (() => { - let hasDemoDataTableDefined; - return () => { - if (!hasDemoDataTableDefined) { - hasDemoDataTableDefined = true; - const ce = customElements; - // Prevents `web-component-analyzer` from harvesting `` - ce.define(`${prefix}-ce-demo-data-table`, BXCEDemoDataTable); - } - }; -})(); - -export const Default = (args) => { - const { size } = args?.[`${prefix}-table`] ?? {}; - const { colorScheme } = args?.[`${prefix}-table-body`] ?? {}; - return html` - - - - Name - Protocol - Port - Rule - Attached Groups - Status - - - - - Load Balancer 1 - HTTP - 80 - Round Robin - Maureen's VM Groups - Active - - - Load Balancer 2 - HTTP - 80 - Round Robin - Maureen's VM Groups - Active - - - Load Balancer 3 - HTTP - 80 - Round Robin - Maureen's VM Groups - Active - - - - `; -}; - -Default.storyName = 'Default'; - -Default.parameters = { - knobs: { - [`${prefix}-table`]: () => ({ - size: select('Table size (size)', sizes, null), - }), - [`${prefix}-table-body`]: () => ({ - colorScheme: select( - 'Color scheme (color-scheme in ``)', - colorSchemes, - null - ), - }), - }, -}; - -export const expandable = (args) => { - const { size } = args?.[`${prefix}-table`] ?? {}; - const { zebra } = args?.[`${prefix}-table-body`] ?? {}; - const handleExpandRowAll = (event) => { - const { currentTarget, detail } = event; - const rows = currentTarget.querySelectorAll(`${prefix}-table-expand-row`); - Array.prototype.forEach.call(rows, (row) => { - row.expanded = detail.expanded; - }); - }; - const handleExpandRow = (event) => { - const { currentTarget } = event; - const headerRow = currentTarget.querySelector( - `${prefix}-table-header-expand-row` - ); - const rows = currentTarget.querySelectorAll(`${prefix}-table-expand-row`); - headerRow.expanded = Array.prototype.every.call( - rows, - (row) => row.expanded - ); - }; - return html` - - - - Name - Protocol - Port - Rule - Attached Groups - Status - - - - - Load Balancer 1 - HTTP - 80 - Round Robin - Maureen's VM Groups - Active - - -
Expandable row content
-
Description here
-
- - Load Balancer 2 - HTTP - 80 - Round Robin - Maureen's VM Groups - Active - - -
Expandable row content
-
Description here
-
- - Load Balancer 3 - HTTP - 80 - Round Robin - Maureen's VM Groups - Active - - -
Expandable row content
-
Description here
-
-
-
- `; -}; - -expandable.parameters = { - knobs: { - ...Default.parameters.knobs, - [`${prefix}-table-body`]: () => ({}), - }, -}; - -export const sortable = (args) => { - const { size } = args?.[`${prefix}-table`] ?? {}; - const { onBeforeChangeSelection: onBeforeChangeSelectionAll } = - args?.[`${prefix}-table-header-row`] ?? {}; - const { colorScheme } = args?.[`${prefix}-table-body`] ?? {}; - const { hasSelection, disableChangeSelection, onBeforeChangeSelection } = - args?.[`${prefix}-table-row`] ?? {}; - const { disableChangeSort, onBeforeSort } = - args?.[`${prefix}-table-header-cell`] ?? {}; - const beforeChangeSelectionHandler = { - handleEvent(event: CustomEvent) { - if (event.type === `${prefix}-table-change-selection-all`) { - onBeforeChangeSelectionAll(event); - } else { - onBeforeChangeSelection(event); - } - if (disableChangeSelection) { - event.preventDefault(); - } - }, - capture: true, // To prevent the default behavior before `` handles the event - }; - const beforeChangeSortHandler = { - handleEvent(event: CustomEvent) { - onBeforeSort(event); - if (disableChangeSort) { - event.preventDefault(); - } - }, - capture: true, // To prevent the default behavior before `` handles the event - }; - defineDemoDataTable(); - return html` - - - - - `; -}; - -sortable.parameters = { - knobs: { - ...Default.parameters.knobs, - [`${prefix}-table-header-row`]: () => ({ - onBeforeChangeSelection: action(`${prefix}-table-change-selection-all`), - }), - [`${prefix}-table-row`]: () => { - const hasSelection = boolean( - 'Supports selection feature (has-selection)', - false - ); - return { - hasSelection, - disableChangeSelection: - hasSelection && - boolean( - `Disable user-initiated change in selection '(Call event.preventDefault() in ${prefix}-table-row-change-selection/${prefix}-table-change-selection-all events)`, - false - ), - onBeforeChangeSelection: action(`${prefix}-table-row-change-selection`), - }; - }, - [`${prefix}-table-header-cell`]: () => ({ - disableChangeSort: boolean( - `Disable user-initiated change in sorting (Call event.preventDefault() in ${prefix}-table-header-cell-sort event)`, - false - ), - onBeforeSort: action(`${prefix}-table-header-cell-sort`), - }), - }, -}; - -export const sortableWithPagination = (args) => { - const { size } = args?.[`${prefix}-table`] ?? {}; - const { onBeforeChangeSelection: onBeforeChangeSelectionAll } = - args?.[`${prefix}-table-header-row`] ?? {}; - const { colorScheme } = args?.[`${prefix}-table-body`] ?? {}; - const { hasSelection, disableChangeSelection, onBeforeChangeSelection } = - args?.[`${prefix}-table-row`] ?? {}; - const { disableChangeSort, onBeforeSort } = - args?.[`${prefix}-table-header-cell`] ?? {}; - const beforeChangeSelectionHandler = { - handleEvent(event: CustomEvent) { - if (event.type === `${prefix}-table-change-selection-all`) { - onBeforeChangeSelectionAll(event); - } else { - onBeforeChangeSelection(event); - } - if (disableChangeSelection) { - event.preventDefault(); - } - }, - capture: true, // To prevent the default behavior before `` handles the event - }; - const beforeChangeSortHandler = { - handleEvent(event: CustomEvent) { - onBeforeSort(event); - if (disableChangeSort) { - event.preventDefault(); - } - }, - capture: true, // To prevent the default behavior before `` handles the event - }; - defineDemoDataTable(); - return html` - - - - - `; -}; - -sortableWithPagination.storyName = 'Sortable with pagination'; - -sortableWithPagination.parameters = { - knobs: sortable.parameters.knobs, -}; - -export const skeleton = (args) => { - const { size } = args?.[`${prefix}-table`]; - const { colorScheme } = args?.[`${prefix}-table-body`]; - return html` - - - - Name - Protocol - Port - Rule - Attached Groups - Status - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - `; -}; - -skeleton.parameters = { - percy: { - skip: true, - }, - knobs: { - ...Default.parameters.knobs, - }, -}; - -export default { - title: 'Components/Data table', - parameters: { - ...storyDocs.parameters, - }, -}; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/data-table.scss b/web-components/packages/carbon-web-components/src/components/data-table/data-table.scss index 61b4f0709330..28fcbb8145c3 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/data-table.scss +++ b/web-components/packages/carbon-web-components/src/components/data-table/data-table.scss @@ -7,18 +7,20 @@ $css--plex: true !default; +@use '@carbon/styles/scss/breakpoint' as *; @use '@carbon/styles/scss/config' as *; @use '@carbon/styles/scss/type' as *; @use '@carbon/styles/scss/theme' as *; @use '@carbon/styles/scss/spacing' as *; +@use '@carbon/styles/scss/layer/layer-tokens'; @use '@carbon/styles/scss/motion' as *; @use '@carbon/styles/scss/utilities/convert' as *; @use '@carbon/styles/scss/components/checkbox'; @use '@carbon/styles/scss/components/data-table' as *; +@use '@carbon/styles/scss/components/link' as *; + @use '@carbon/styles/scss/components/data-table/expandable'; -// TODO: deprecate -// @use '@carbon/styles/scss/components/data-table/data-table-inline-edit'; @use '@carbon/styles/scss/components/data-table/skeleton'; // TODO: deprecate @import @@ -28,4 +30,3 @@ $css--plex: true !default; @import 'table-sizes'; @import 'table-selection'; @import 'table-sort'; -@import 'table-skeleton'; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/defs.ts b/web-components/packages/carbon-web-components/src/components/data-table/defs.ts index 876d5257c6d3..eafe16dfec7f 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/defs.ts +++ b/web-components/packages/carbon-web-components/src/components/data-table/defs.ts @@ -7,21 +7,6 @@ * LICENSE file in the root directory of this source tree. */ -/** - * Table color scheme. - */ -export enum TABLE_COLOR_SCHEME { - /** - * Regular color scheme. - */ - REGULAR = '', - - /** - * Color scheme with zebra stripe. - */ - ZEBRA = 'zebra', -} - /** * Table size. */ diff --git a/web-components/packages/carbon-web-components/src/components/data-table/index.ts b/web-components/packages/carbon-web-components/src/components/data-table/index.ts index c363e4f18c85..0bd93ba2eed6 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/index.ts +++ b/web-components/packages/carbon-web-components/src/components/data-table/index.ts @@ -1,25 +1,26 @@ /** * @license * - * Copyright IBM Corp. 2021 + * Copyright IBM Corp. 2021, 2023 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ import './table'; +import './table-skeleton'; +import './table-header-title'; +import './table-header-description'; import './table-batch-actions'; import './table-body'; import './table-cell'; -import './table-cell-skeleton'; -import './table-expand-row'; +import './table-cell-content'; import './table-expanded-row'; import './table-head'; import './table-header-cell'; -import './table-header-cell-skeleton'; -import './table-header-expand-row'; import './table-header-row'; import './table-row'; +import './table-skeleton'; import './table-toolbar'; import './table-toolbar-content'; import './table-toolbar-search'; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-basic-story.ts b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-basic-story.ts new file mode 100644 index 000000000000..419539e9453d --- /dev/null +++ b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-basic-story.ts @@ -0,0 +1,269 @@ +/** + * @license + * + * Copyright IBM Corp. 2019, 2023 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { html } from 'lit'; +import { boolean, select, text } from '@storybook/addon-knobs'; +import { prefix } from '../../../globals/settings'; +import { TABLE_SIZE } from '../table'; +import '../index'; +import storyDocs from './data-table-story.mdx'; + +const sizes = { + [`xs (${TABLE_SIZE.XS})`]: TABLE_SIZE.XS, + [`sm (${TABLE_SIZE.SM})`]: TABLE_SIZE.SM, + [`md (${TABLE_SIZE.MD})`]: TABLE_SIZE.MD, + [`lg (${TABLE_SIZE.LG} - default)`]: TABLE_SIZE.LG, + [`xl (${TABLE_SIZE.XL})`]: TABLE_SIZE.XL, +}; + +export const Default = () => { + return html` + + + + Name + Rule + Status + Other + Example + + + + + Load Balancer 1 + Round robin + Starting + Test + 22 + + + Load Balancer 2 + DNS delegation + Active + Test + 22 + + + Load Balancer 3 + Round robin + Disabled + Test + 22 + + + Load Balancer 4 + Round robin + Disabled + Test + 22 + + + Load Balancer 5 + Round robin + Disabled + Test + 22 + + + Load Balancer 6 + Round robin + Disabled + Test + 22 + + + Load Balancer 7 + Round robin + Disabled + Test + 22 + + + + `; +}; + +export const XLWithTwoLines = () => { + return html` + + + + Name + Rule + Status + Other + Example + + + + + + Load Balancer 1 + Austin, Tx + + Round robin + Starting + Test + 22 + + + Load Balancer 2 + Austin, Tx + + DNS delegation + Active + Test + 22 + + + Load Balancer 3 + Austin, Tx + + Round robin + Disabled + Test + 22 + + + Load Balancer 4 + Austin, Tx + + Round robin + Disabled + Test + 22 + + + Load Balancer 5 + Austin, Tx + + Round robin + Disabled + Test + 22 + + + Load Balancer 6 + Austin, Tx + + Round robin + Disabled + Test + 22 + + + Load Balancer 7 + Austin, Tx + + Round robin + Disabled + Test + 22 + + + + `; +}; + +export const Playground = (args) => { + const { locale, size, useStaticWidth, useZebraStyles } = + args?.[`${prefix}-table`] ?? {}; + return html` + + + + Name + Rule + Status + Other + Example + + + + + Load Balancer 1 + Round robin + Starting + Test + 22 + + + Load Balancer 2 + DNS delegation + Active + Test + 22 + + + Load Balancer 3 + Round robin + Disabled + Test + 22 + + + Load Balancer 4 + Round robin + Disabled + Test + 22 + + + Load Balancer 5 + Round robin + Disabled + Test + 22 + + + Load Balancer 6 + Round robin + Disabled + Test + 22 + + + Load Balancer 7 + Round robin + Disabled + Test + 22 + + + + `; +}; + +Playground.parameters = { + knobs: { + [`${prefix}-table`]: () => ({ + locale: text('Locale', 'en'), + size: select('Size', sizes, TABLE_SIZE.LG), + useStaticWidth: boolean('Use static width', false), + useZebraStyles: boolean('Use zebra styles', false), + }), + }, +}; + +export default { + title: 'Components/DataTable/Basic', + parameters: { + ...storyDocs.parameters, + }, +}; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-batch-actions-story.ts b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-batch-actions-story.ts new file mode 100644 index 000000000000..38323f4e0ccc --- /dev/null +++ b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-batch-actions-story.ts @@ -0,0 +1,278 @@ +/** + * @license + * + * Copyright IBM Corp. 2019, 2023 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { html } from 'lit'; +import { boolean, select, text } from '@storybook/addon-knobs'; +import { prefix } from '../../../globals/settings'; +import { TABLE_SIZE } from '../table'; +import Add from '@carbon/web-components/es/icons/add/16'; +import Save from '@carbon/web-components/es/icons/save/16'; +import TrashCan from '@carbon/web-components/es/icons/trash-can/16'; +// @ts-ignore +import Download16 from '@carbon/web-components/es/icons/download/16'; +// @ts-ignore +import Settings16 from '@carbon/web-components/es/icons/settings/16'; +import '../index'; +import storyDocs from './data-table-story.mdx'; + +const sizes = { + [`xs (${TABLE_SIZE.XS})`]: TABLE_SIZE.XS, + [`sm (${TABLE_SIZE.SM})`]: TABLE_SIZE.SM, + [`md (${TABLE_SIZE.MD})`]: TABLE_SIZE.MD, + [`lg (${TABLE_SIZE.LG} - default)`]: TABLE_SIZE.LG, + [`xl (${TABLE_SIZE.XL})`]: TABLE_SIZE.XL, +}; + +export const Default = () => { + return html` + + DataTable + With batch actions. Lorem ipsum dolor sit amet, consectetur adipiscing + elit. Maecenas accumsan mauris sed congue egestas. Integer varius mauris + vel arcu pulvinar bibendum non sit amet ligula. Nullam ut nisi eu tellus + aliquet vestibulum vel sit amet odio. + + + + Delete ${TrashCan({ slot: 'icon' })} + ${Add({ slot: 'icon' })} + ${Save({ slot: 'icon' })} + + Download ${Download16({ slot: 'icon' })} + + + + + + ${Settings16({ slot: 'icon' })} + + alert('Alert 1')}> + Action 1 + + alert('Alert 2')}> + Action 2 + + alert('Alert 3')}> + Action 3 + + + + Add new + + + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + + Starting + + + + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + + + `; +}; + +export const Playground = (args) => { + const { isSortable, locale, radio, size, useStaticWidth, useZebraStyles } = + args?.[`${prefix}-table`] ?? {}; + return html` + + DataTable + With batch actions. + + + + Delete ${TrashCan({ slot: 'icon' })} + Save ${Save({ slot: 'icon' })} + + Download ${Download16({ slot: 'icon' })} + + + + + + ${Settings16({ slot: 'icon' })} + + alert('Alert 1')}> + Action 1 + + alert('Alert 2')}> + Action 2 + + alert('Alert 3')}> + Action 3 + + + + Add new + + + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + Starting + + + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + + + `; +}; + +Playground.parameters = { + knobs: { + [`${prefix}-table`]: () => ({ + isSortable: boolean('Is sortable', false), + locale: text('Locale', 'en'), + radio: boolean('Radio', false), + size: select('Size', sizes, TABLE_SIZE.LG), + useStaticWidth: boolean('Use static width', false), + useZebraStyles: boolean('Use zebra styles', false), + }), + }, +}; + +export default { + title: 'Components/DataTable/Batch Actions', + parameters: { + ...storyDocs.parameters, + }, +}; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-dynamic-story.ts b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-dynamic-story.ts new file mode 100644 index 000000000000..fae8e733315e --- /dev/null +++ b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-dynamic-story.ts @@ -0,0 +1,354 @@ +/** + * @license + * + * Copyright IBM Corp. 2019, 2023 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { html } from 'lit'; +import { boolean, select, text } from '@storybook/addon-knobs'; +import { prefix } from '../../../globals/settings'; +import { TABLE_SIZE } from '../table'; +import Add from '@carbon/web-components/es/icons/add/16'; +import Save from '@carbon/web-components/es/icons/save/16'; +import TrashCan from '@carbon/web-components/es/icons/trash-can/16'; +// @ts-ignore +import Download16 from '@carbon/web-components/es/icons/download/16'; +// @ts-ignore +import Settings16 from '@carbon/web-components/es/icons/settings/16'; +import '../index'; +import storyDocs from './data-table-story.mdx'; + +const sizes = { + [`xs (${TABLE_SIZE.XS})`]: TABLE_SIZE.XS, + [`sm (${TABLE_SIZE.SM})`]: TABLE_SIZE.SM, + [`md (${TABLE_SIZE.MD})`]: TABLE_SIZE.MD, + [`lg (${TABLE_SIZE.LG} - default)`]: TABLE_SIZE.LG, + [`xl (${TABLE_SIZE.XL})`]: TABLE_SIZE.XL, +}; + +let headerCount = 6; +let rowCount = 1; + +const insertInRandomPosition = (array, element) => { + const index = Math.floor(Math.random() * (array.length + 1)); + return [...array.slice(0, index), element, ...array.slice(index)]; +}; + +const addRow = () => { + const newRow = document.createElement('cds-table-row'); + + const templateRow = { + name: `New Row ${rowCount}`, + protocol: 'HTTP', + port: rowCount * 100, + rule: rowCount % 2 === 0 ? 'Round robin' : 'DNS delegation', + attached_groups: `Row ${rowCount}'s VM Groups`, + status: 'Starting', + }; + + for (let key in templateRow) { + if (Object.prototype.hasOwnProperty.call(templateRow, key)) { + const cell = document.createElement('cds-table-cell'); + cell.textContent = templateRow[key]; + newRow.appendChild(cell); + } + } + + const rows = document.querySelectorAll('cds-table-row'); + const diff = headerCount - Object.keys(templateRow).length; + + [...Array(diff)].forEach(() => { + const newCell = document.createElement('cds-table-cell'); + newCell.textContent = `Header ${headerCount - 1}`; + newRow.appendChild(newCell); + }); + + newRow.setAttribute('selection-name', `${rows.length}`); + + const updatedRows = insertInRandomPosition([...rows], newRow); + updatedRows.forEach((e) => { + document.querySelector('cds-table-body')!.insertBefore(e, null); + }); + + rowCount++; +}; + +const addHeader = () => { + const headerRow = document.querySelector('cds-table-header-row'); + const newHeader = document.createElement('cds-table-header-cell'); + newHeader.textContent = `Header ${headerCount}`; + headerRow?.appendChild(newHeader); + + const rows = document.querySelectorAll('cds-table-row'); + rows.forEach((e) => { + const newCell = document.createElement('cds-table-cell'); + newCell.textContent = `Header ${headerCount}`; + e.appendChild(newCell); + }); + headerCount++; +}; + +export const Default = () => { + return html` + + DataTable + Use the toolbar menu to add rows and + headers + + + + ${Add({ slot: 'icon' })} + ${Save({ slot: 'icon' })} + ${Save({ slot: 'icon' })} + + Download ${Download16({ slot: 'icon' })} + + + + + + ${Settings16({ slot: 'icon' })} + + Add row + Add header + + + + + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + Starting + + + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + + + `; +}; + +export const Playground = (args) => { + const { isSortable, locale, radio, size, useStaticWidth, useZebraStyles } = + args?.[`${prefix}-table`] ?? {}; + return html` + + DataTable + Use the toolbar menu to add rows and + headers + + + + Delete ${TrashCan({ slot: 'icon' })} + Save ${Save({ slot: 'icon' })} + + Download ${Download16({ slot: 'icon' })} + + + + + + ${Settings16({ slot: 'icon' })} + + Add row + Add header + + + + + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + +
Expandable row content
+
Description here
+
+ + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + Starting + + +
Expandable row content
+
Description here
+
+ + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + +
Expandable row content
+
Description here
+
+ + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + +
Expandable row content
+
Description here
+
+ + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + +
Expandable row content
+
Description here
+
+ + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + +
Expandable row content
+
Description here
+
+
+
+ `; +}; + +Playground.parameters = { + knobs: { + [`${prefix}-table`]: () => ({ + isSortable: boolean('Is sortable', false), + locale: text('Locale', 'en'), + radio: boolean('Radio', false), + size: select('Size', sizes, TABLE_SIZE.LG), + useStaticWidth: boolean('Use static width', false), + useZebraStyles: boolean('Use zebra styles', false), + }), + }, +}; + +export default { + title: 'Components/DataTable/Dynamic', + parameters: { + ...storyDocs.parameters, + }, +}; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-expansion-story.ts b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-expansion-story.ts new file mode 100644 index 000000000000..98058eff0bfe --- /dev/null +++ b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-expansion-story.ts @@ -0,0 +1,354 @@ +/** + * @license + * + * Copyright IBM Corp. 2019, 2023 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { html } from 'lit'; +import { boolean, select, text } from '@storybook/addon-knobs'; +import { prefix } from '../../../globals/settings'; +import { TABLE_SIZE } from '../table'; +// @ts-ignore +import Download16 from '@carbon/web-components/es/icons/download/16'; +// @ts-ignore +import Settings16 from '@carbon/web-components/es/icons/settings/16'; +import '../index'; +import storyDocs from './data-table-story.mdx'; + +const sizes = { + [`xs (${TABLE_SIZE.XS})`]: TABLE_SIZE.XS, + [`sm (${TABLE_SIZE.SM})`]: TABLE_SIZE.SM, + [`md (${TABLE_SIZE.MD})`]: TABLE_SIZE.MD, + [`lg (${TABLE_SIZE.LG} - default)`]: TABLE_SIZE.LG, + [`xl (${TABLE_SIZE.XL})`]: TABLE_SIZE.XL, +}; + +export const Default = () => { + return html` + + DataTable + With expansion + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + +
Expandable row content
+
Description here
+
+ + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + Starting + + +
Expandable row content
+
Description here
+
+ + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + +
Expandable row content
+
Description here
+
+ + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + +
Expandable row content
+
Description here
+
+ + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + +
Expandable row content
+
Description here
+
+ + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + +
Expandable row content
+
Description here
+
+
+
+ `; +}; + +export const BatchExpansion = () => { + return html` + + DataTable + With expansion + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + +
Expandable row content
+
Description here
+
+ + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + Starting + + +
Expandable row content
+
Description here
+
+ + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + +
Expandable row content
+
Description here
+
+ + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + +
Expandable row content
+
Description here
+
+ + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + +
Expandable row content
+
Description here
+
+ + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + +
Expandable row content
+
Description here
+
+
+
+ `; +}; + +export const Playground = (args) => { + const { isSortable, locale, size, useStaticWidth, useZebraStyles } = + args?.[`${prefix}-table`] ?? {}; + return html` + + DataTable + With expansion + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + +
Expandable row content
+
Description here
+
+ + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + Starting + + +
Expandable row content
+
Description here
+
+ + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + +
Expandable row content
+
Description here
+
+ + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + +
Expandable row content
+
Description here
+
+ + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + +
Expandable row content
+
Description here
+
+ + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + +
Expandable row content
+
Description here
+
+
+
+ `; +}; + +Playground.parameters = { + knobs: { + [`${prefix}-table`]: () => ({ + isSortable: boolean('Is sortable', false), + locale: text('Locale', 'en'), + size: select('Size', sizes, TABLE_SIZE.LG), + useStaticWidth: boolean('Use static width', false), + useZebraStyles: boolean('Use zebra styles', false), + }), + }, +}; + +export default { + title: 'Components/DataTable/Expansion', + parameters: { + ...storyDocs.parameters, + }, +}; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-filtering-story.ts b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-filtering-story.ts new file mode 100644 index 000000000000..c60a001c8589 --- /dev/null +++ b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-filtering-story.ts @@ -0,0 +1,236 @@ +/** + * @license + * + * Copyright IBM Corp. 2019, 2023 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { html } from 'lit'; +import { boolean, select, text } from '@storybook/addon-knobs'; +import { prefix } from '../../../globals/settings'; +import { TABLE_SIZE } from '../table'; +// @ts-ignore +import Settings16 from '@carbon/web-components/es/icons/settings/16'; +import '../index'; +import storyDocs from './data-table-story.mdx'; + +const sizes = { + [`xs (${TABLE_SIZE.XS})`]: TABLE_SIZE.XS, + [`sm (${TABLE_SIZE.SM})`]: TABLE_SIZE.SM, + [`md (${TABLE_SIZE.MD})`]: TABLE_SIZE.MD, + [`lg (${TABLE_SIZE.LG} - default)`]: TABLE_SIZE.LG, + [`xl (${TABLE_SIZE.XL})`]: TABLE_SIZE.XL, +}; + +export const Default = () => { + return html` + + DataTable + With filtering + + + + + + ${Settings16({ slot: 'icon' })} + + Action 1 + Action 2 + Action 3 + + + Primary Button + + + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + Starting + + + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + + + `; +}; + +export const Playground = (args) => { + const { isSortable, locale, radio, size, useStaticWidth, useZebraStyles } = + args?.[`${prefix}-table`] ?? {}; + return html` + + DataTable + With filtering + + + + + + ${Settings16({ slot: 'icon' })} + + Action 1 + Action 2 + Action 3 + + + Primary Button + + + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + Starting + + + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + + + `; +}; + +Playground.parameters = { + knobs: { + [`${prefix}-table`]: () => ({ + isSortable: boolean('Is sortable', false), + locale: text('Locale', 'en'), + size: select('Size', sizes, TABLE_SIZE.LG), + useStaticWidth: boolean('Use static width', false), + useZebraStyles: boolean('Use zebra styles', false), + }), + }, +}; + +export default { + title: 'Components/DataTable/Filtering', + parameters: { + ...storyDocs.parameters, + }, +}; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-selection-story.ts b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-selection-story.ts new file mode 100644 index 000000000000..b604a01ed4f9 --- /dev/null +++ b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-selection-story.ts @@ -0,0 +1,359 @@ +/** + * @license + * + * Copyright IBM Corp. 2019, 2023 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { html } from 'lit'; +import { boolean, select, text } from '@storybook/addon-knobs'; +import { prefix } from '../../../globals/settings'; +import { TABLE_SIZE } from '../table'; +// @ts-ignore +import Download16 from '@carbon/web-components/es/icons/download/16'; +// @ts-ignore +import Settings16 from '@carbon/web-components/es/icons/settings/16'; +import '../index'; +import storyDocs from './data-table-story.mdx'; + +const sizes = { + [`xs (${TABLE_SIZE.XS})`]: TABLE_SIZE.XS, + [`sm (${TABLE_SIZE.SM})`]: TABLE_SIZE.SM, + [`md (${TABLE_SIZE.MD})`]: TABLE_SIZE.MD, + [`lg (${TABLE_SIZE.LG} - default)`]: TABLE_SIZE.LG, + [`xl (${TABLE_SIZE.XL})`]: TABLE_SIZE.XL, +}; + +export const Default = () => { + return html` + + DataTable + With selection + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + Starting + + + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + + + `; +}; + +export const WithRadioSelection = () => { + return html` + + DataTable + With selection + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + Starting + + + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + + + `; +}; + +export const WithSelectionAndSorting = () => { + return html` + + DataTable + With selection + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + Starting + + + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + + + `; +}; + +export const Playground = (args) => { + const { isSortable, locale, radio, size, useStaticWidth, useZebraStyles } = + args?.[`${prefix}-table`] ?? {}; + return html` + + DataTable + With selection + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + Starting + + + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + + + `; +}; + +Playground.parameters = { + knobs: { + [`${prefix}-table`]: () => ({ + isSortable: boolean('Is sortable', false), + locale: text('Locale', 'en'), + radio: boolean('Radio', false), + size: select('Size', sizes, TABLE_SIZE.LG), + useStaticWidth: boolean('Use static width', false), + useZebraStyles: boolean('Use zebra styles', false), + }), + }, +}; + +export default { + title: 'Components/DataTable/Selection', + parameters: { + ...storyDocs.parameters, + }, +}; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-skeleton-story.ts b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-skeleton-story.ts new file mode 100644 index 000000000000..883e3770f66e --- /dev/null +++ b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-skeleton-story.ts @@ -0,0 +1,63 @@ +/** + * @license + * + * Copyright IBM Corp. 2019, 2023 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { html } from 'lit'; +import { boolean, number } from '@storybook/addon-knobs'; +import { prefix } from '../../../globals/settings'; +import storyDocs from './data-table-story.mdx'; +import '../index'; + +const headers = [ + 'Name', + 'Protocol', + 'Port', + 'Rule', + 'Attached groups', + 'Status', +]; + +export const Default = () => { + return html` `; +}; + +export const Playground = (args) => { + const { columnCount, rowCount, showHeader, showToolbar, zebra } = + args?.[`${prefix}-table`] ?? {}; + + return html` + + + `; +}; + +Playground.parameters = { + knobs: { + [`${prefix}-table`]: () => ({ + compact: boolean('Compact', false), + columnCount: number('Column count', 5), + rowCount: number('Row count', 5), + showHeader: boolean('Show header', true), + showToolbar: boolean('Show toolbar', true), + zebra: boolean('Use zebra styles', false), + }), + }, +}; + +export default { + title: 'Components/DataTable/Skeleton', + parameters: { + ...storyDocs.parameters, + }, +}; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-sorting-story.ts b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-sorting-story.ts new file mode 100644 index 000000000000..b85cefcd155c --- /dev/null +++ b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-sorting-story.ts @@ -0,0 +1,203 @@ +/** + * @license + * + * Copyright IBM Corp. 2019, 2023 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { html } from 'lit'; +import { boolean, select, text } from '@storybook/addon-knobs'; +import { prefix } from '../../../globals/settings'; +import { TABLE_SIZE } from '../table'; +// @ts-ignore +import Settings16 from '@carbon/web-components/es/icons/settings/16'; +import '../index'; +import storyDocs from './data-table-story.mdx'; + +const sizes = { + [`xs (${TABLE_SIZE.XS})`]: TABLE_SIZE.XS, + [`sm (${TABLE_SIZE.SM})`]: TABLE_SIZE.SM, + [`md (${TABLE_SIZE.MD})`]: TABLE_SIZE.MD, + [`lg (${TABLE_SIZE.LG} - default)`]: TABLE_SIZE.LG, + [`xl (${TABLE_SIZE.XL})`]: TABLE_SIZE.XL, +}; + +export const Default = () => { + return html` + + DataTable + With filtering + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + Starting + + + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + + + `; +}; + +export const Playground = (args) => { + const { isSortable, locale, size, useStaticWidth, useZebraStyles } = + args?.[`${prefix}-table`] ?? {}; + return html` + + DataTable + With filtering + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + Starting + + + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + + + `; +}; + +Playground.parameters = { + knobs: { + [`${prefix}-table`]: () => ({ + isSortable: boolean('Is sortable', true), + locale: text('Locale', 'en'), + size: select('Size', sizes, TABLE_SIZE.LG), + useStaticWidth: boolean('Use static width', false), + useZebraStyles: boolean('Use zebra styles', false), + }), + }, +}; + +export default { + title: 'Components/DataTable/Sorting', + parameters: { + ...storyDocs.parameters, + }, +}; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-story.mdx b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-story.mdx new file mode 100644 index 000000000000..a094dce23f71 --- /dev/null +++ b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-story.mdx @@ -0,0 +1,521 @@ +import { Props, Description } from '@storybook/addon-docs/blocks'; +import { cdnJs, cdnCss } from '../../../globals/internal/storybook-cdn'; + +# Data table + +> 💡 Check our +> [CodeSandbox](https://codesandbox.io/s/github/carbon-design-system/carbon-for-ibm-dotcom/tree/main/packages/carbon-web-components/examples/codesandbox/basic/components/data-table) +> example implementation. + +[![Edit carbon-web-components](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/carbon-design-system/carbon-for-ibm-dotcom/tree/main/packages/carbon-web-components/examples/codesandbox/basic/components/data-table) + +Data table in `@carbon/web-components` focuses on primitives that constructs +table UI, consisting of the following: + +| Tag | Description | HTML tag counterpart | +| ------------------------ | --------------- | -------------------- | +| `` | The container | N/A | +| `` | The table | `` | +| `` | The header | `` | +| `` | The header row | `` in `` | +| `` | The header cell | `` | +| `` | The header row | `` in `` | +| `` | The header cell | `
` | +| `` | The header body | `
` | + +## Table of Contents +- [Getting started](#getting-started) +- [Sorting](#sorting) + - [Programmatic sorting](#programmatic-sorting) + - [Custom sorting](#custom-sorting) +- [Expansion](#expansion) + - [Programmatic expansion](#programmatic-expansion) +- [Selection](#selection) + - [Programmatic selection](#programmatic-selection) +- [Filtering](#filtering) +- [Batch actions](#batch-actions) +- [Toolbar](#toolbar) +- [Skeleton](#skeleton) +- [Attributes, properties, and events](#attributes-properties-and-events) + +- [Feedback](#feedback) + +## Getting started + +Here's a quick example to get you started. + +### JS (via import) + +```javascript +import '@carbon/web-components/es/components/data-table/index.js'; +``` + + + + +### HTML + +Here is an example of a basic data table with a header row with two columns, and three regular rows: + +```html + + + + Name + Status + + + + + Load Balancer 1 + Disabled + + + Load Balancer 2 + Starting + + + Load Balancer 3 + Active + + + +``` + +After rendering a `DataTable` component using the code snippet above, you can +optionally add any number of features including sorting, row expansion, +filtering, row selection, batch actions. + +For more information, see each usage section below for the functionality that +you're interested in. If you want to see a full list of the arguments passed +into your render prop, visit the [render props](#attributes-properties-and-events) section. + +### Table header + +You can add a header to the table that can contain a title and a description. + +```html + + DataTable + Example of a DataTable description. + + + Name + Status + + + + + Load Balancer 1 + Disabled + + + Load Balancer 2 + Starting + + + Load Balancer 3 + Active + + + +``` + +### XL With Two Lines + +In case there is extra data that needs to be added within a `` +that would require a second line, you can use the `` +component as such: + +```html + + + ... + + + Load Balancer 1 + Austin, Tx + + Round robin + Starting + Test + 22 + + ... + +``` + +This requires the table to be of `size="xl"` to accomodate the content properly. + + +## Sorting + +In order to sort the rows in your data table, you will need to pass in the `is-sortable` prop to the `` component. + +Optionally, you can pass in `is-sortable` to each `` that you want to be sorted, or not sorted. +This is useful if you only want to enable sorting on some table column headers, but not all. + +```html + +... + +``` + +### Programmatic sorting + +You can also change the sort status of the table by querying the +specific `` you would like to sort. + +```js + // sorting the first column + document.querySelectorAll('cds-table-header-cell')[0].shadowRoot.querySelector('button').click() +``` + +### Custom sorting + +If the default sorting logic doesn't match your use-case, you can provide a +custom sort method as a `customSortRow` prop to ``. + +`sortRow` is a method that takes in the values of two cells, in addition to some +info, and should return -1, 0, or 1 as a result (mirroring the native sort +behavior in JavaScript). + +The component uses a default `Intl.Collator` object created with the current set +locale to sort the rows. The default sorting row function uses this collator, +and as such it is needed in the custom sorting function. + +As a result, `customSortRow` function would take on the following shape: + +```js + customSortRow(lhs, rhs, collator) { + if (typeof lhs === 'number' && typeof rhs === 'number') { + return lhs - rhs; + } + return collator.compare(lhs, rhs); + } + + document.querySelector('cds-table').customSortRow = customSortRow; +``` + +If you would like to use a different locale for collator comparisons, +you can change the `locale` property and it will be updated accordingly. +In case a different collator is needed, you can create a new object and +pass it in as such: + +```js + document.querySelector('cds-table').collator = // new collator here; +``` + +## Expansion + +The `` component supports row-level expansion when using the `expandable` prop. +Setting the prop will apply the `expandable` prop to every row. This is useful when every +row can be expandable. In order to make a row be expandable, it is necessary for the row to +have a `` component directly following it. + +```html + + + + Name + Status + + + + + Load Balancer 1 + Disabled + + +
Expandable row content
+
Description here
+
+ + Load Balancer 2 + Starting + + +
Expandable row content
+
Description here
+
+ + Load Balancer 3 + Active + + +
Expandable row content
+
Description here
+
+
+
+``` + +In case you'd like a button to expand all the rows at once, you can +use the `batch-expansion` prop in ``. + +In case you only want to set specific rows to be expandable, be sure to set the +`expandable` prop only on that specific `` component, and have its +adjacent sibling `` directly following it. + +### Programmatic expansion + +You can programmatically trigger a row expansion by querying the desired row as such: + +```js + document.querySelectorAll('cds-table-row')[0].shadowRoot.querySelector('.cds--table-expand__button').click(); +``` + +## Selection + +The `` component supports row selection when using the `is-selectable` prop. +Setting the prop will automatically set a numerical `selection-name` attribute to every row, +though this can also be customized with your own custom name. + +```html + + +``` + +By default, the `` component will render with its own checkbox, used to +select all the rows at once. If you would like to disable this behavior, set the `hide-checkbox` +prop within the component. + +```html + + ... + + ... + + ... + +``` + +### Programmatic selection + +You can programmatically trigger a row selection by querying the desired row as such: + +```js +document.querySelectorAll('cds-table-row')[0].shadowRoot.querySelector('cds-checkbox').shadowRoot.querySelector('input').click() +``` + +## Filtering + +Filtering in a `` is provided through usage of the `` and +the `` component. Any input entered through +`` will be used in the built-in filtering function. + +```html + + + + + + + + ... header + + ... body and rows + +``` + +Our built-in filtering function scans the rows' strings and attempts to +match to the current search value input. If there are no matches in a given row, +the current row will be filtered out. + +In other words, a custom function will be doing its own string comparison per row, +and thus it would have the following structure: + +```js +const newFilterRows = (rowText, searchString) => { + return // provide custom string comparison return boolean; +} + + document.querySelector('cds-table').filterRows = newFilterRows; +``` + +## Batch actions + +You can combine batch actions with the `` component to allow the user to +perform a single action on multiple selected rows. + +This toolbar is highly customizable, as any action can be adhered to a `` +and inserted into the toolbar. + +In the example below, we have custom ``s that we can customize to our liking +by using the `onclick` property and passing it any custom functions. + +The component has a built-in download function, that is called whenever one of the +``s has a `download` attribute. The component will create a JSON object containing +the currently selected rows, and trigger its download within the browser. + +```js +const alertFunction = () => alert('Alert!'); +``` + +```html + + + + + Delete + + Save + + Download + + + + ... headers + + ... body and rows + +``` + +## Toolbar + +As mentioned earlier, you can add a `` component to the toolbar. +You can also add an overflow menu and a button within the toolbar as such: + +```html + + + + + + + + + Example 1 + Example 2 + + + Example button + + + ... header + + ... body and rows + + +``` + +### Overflow Menu + +An overflow menu can be added to the toolbar table row. To add an overflow menu, +simply add a `` containing the ``. + +```html + ... + + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + + + Options + + Stop app + Restart app + Rename + + + + + ... +``` + +## Skeleton + +The default Skeleton table will render with 5 rows and columns, the header, and the toolbar. + +```html + +``` + +Specify the number of rows and columns you need with the `row-count` and `column-count` attributes. + +```html + +``` + + +### Custom headers + +Optionally, you can specify to have header titles on the Skeleton table. + +```html + +``` + +```javascript +const customHeaders = ['Name', 'Protocol', 'Port', 'Rule', 'Attached groups', 'Status']; +document.querySelector('cds-table-skeleton').headers = customHeaders; +``` + +## Attributes, properties and events + +Note: For `boolean` attributes, `true` means simply setting the attribute (e.g. +``) and `false` means not setting the attribute (e.g. +`` without `is-sortable` attribute). + +### `` + + + +### `` + + + +### `` + + + +### `` + + + +### `` + + + +### `` + + + +### ` + +### `` + + + +### `` + + + +### `` + + + +### `` + + + +### `cds-table-skeleton` + + \ No newline at end of file diff --git a/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-toolbar-story.ts b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-toolbar-story.ts new file mode 100644 index 000000000000..d505b81fd768 --- /dev/null +++ b/web-components/packages/carbon-web-components/src/components/data-table/stories/data-table-toolbar-story.ts @@ -0,0 +1,691 @@ +/** + * @license + * + * Copyright IBM Corp. 2019, 2023 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { html } from 'lit'; +import { boolean, select, text } from '@storybook/addon-knobs'; +import { prefix } from '../../../globals/settings'; +import { TABLE_SIZE } from '../table'; +// @ts-ignore +import Download16 from '@carbon/web-components/es/icons/download/16'; +// @ts-ignore +import Settings16 from '@carbon/web-components/es/icons/settings/16'; +import OverflowMenuVertical16 from '@carbon/icons/lib/overflow-menu--vertical/16'; +import '../index'; +import storyDocs from './data-table-story.mdx'; + +const sizes = { + [`xs (${TABLE_SIZE.XS})`]: TABLE_SIZE.XS, + [`sm (${TABLE_SIZE.SM})`]: TABLE_SIZE.SM, + [`md (${TABLE_SIZE.MD})`]: TABLE_SIZE.MD, + [`lg (${TABLE_SIZE.LG} - default)`]: TABLE_SIZE.LG, + [`xl (${TABLE_SIZE.XL})`]: TABLE_SIZE.XL, +}; + +export const Default = () => { + return html` + + DataTable + With toolbar + + + + + + ${Settings16({ slot: 'icon' })} + + alert('Alert 1')}> + Action 1 + + alert('Alert 2')}> + Action 2 + + alert('Alert 3')}> + Action 3 + + + + Primary button + + + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + Starting + + + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + + + `; +}; + +export const PersistentToolbar = () => { + return html` + + DataTable + With toolbar + + + + + + ${Settings16({ slot: 'icon' })} + + alert('Alert 1')}> + Action 1 + + alert('Alert 2')}> + Action 2 + + alert('Alert 3')}> + Action 3 + + + + Primary button + + + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + Starting + + + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + + + `; +}; + +export const SmallPersistentToolbar = () => { + return html` + + DataTable + With toolbar + + + + + + ${Settings16({ slot: 'icon' })} + + Action 1 + Action 2 + Action 3 + + + Primary Button + + + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + Starting + + + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + + + `; +}; + +export const WithOverflowMenu = () => { + return html` + + DataTable + With toolbar + + + + + + ${Settings16({ slot: 'icon' })} + + alert('Alert 1')}> + Action 1 + + alert('Alert 2')}> + Action 2 + + alert('Alert 3')}> + Action 3 + + + + Primary button + + + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + + ${OverflowMenuVertical16({ slot: 'icon' })} + Options + + + Stop app + Restart app + Rename + + + + + + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + Starting + + + ${OverflowMenuVertical16({ slot: 'icon' })} + Options + + + Stop app + Restart app + Rename + + + + + + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + + ${OverflowMenuVertical16({ slot: 'icon' })} + Options + + + Stop app + Restart app + Rename + + + + + + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + + ${OverflowMenuVertical16({ slot: 'icon' })} + Options + + + Stop app + Restart app + Rename + + + + + + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + + ${OverflowMenuVertical16({ slot: 'icon' })} + Options + + + Stop app + Restart app + Rename + + + + + + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + + ${OverflowMenuVertical16({ slot: 'icon' })} + Options + + + Stop app + Restart app + Rename + + + + + + + `; +}; + +export const Playground = (args) => { + const { + isSortable, + locale, + radio, + overflowMenuOnHover, + size, + useStaticWidth, + useZebraStyles, + } = args?.[`${prefix}-table`] ?? {}; + return html` + + DataTable + With batch actions. + + + + + + ${Settings16({ slot: 'icon' })} + + Action 1 + Action 2 + Action 3 + + + Primary button + + + + + + Name + Protocol + Port + Rule + Attached groups + Status + + + + + + Load Balancer 3 + HTTP + 3000 + Round robin + Kevin's VM Groups + Disabled + + + ${OverflowMenuVertical16({ slot: 'icon' })} + Options + + + Stop app + Restart app + Rename + + + + + + Load Balancer 1 + HTTP + 443 + Round robin + Maureen's VM Groups + Starting + + + ${OverflowMenuVertical16({ slot: 'icon' })} + Options + + + Stop app + Restart app + Rename + + + + + + Load Balancer 2 + HTTP + 80 + DNS delegation + Andrew's VM Groups + Active + + + ${OverflowMenuVertical16({ slot: 'icon' })} + Options + + + Stop app + Restart app + Rename + + + + + + Load Balancer 6 + HTTP + 3000 + Round robin + Marc's VM Groups + Disabled + + + ${OverflowMenuVertical16({ slot: 'icon' })} + Options + + + Stop app + Restart app + Rename + + + + + + Load Balancer 4 + HTTP + 443 + Round robin + Mel's VM Groups + Starting + + + ${OverflowMenuVertical16({ slot: 'icon' })} + Options + + + Stop app + Restart app + Rename + + + + + + Load Balancer 5 + HTTP + 80 + DNS delegation + Ronja's VM Groups + Active + + + ${OverflowMenuVertical16({ slot: 'icon' })} + Options + + + Stop app + Restart app + Rename + + + + + + + `; +}; + +Playground.parameters = { + knobs: { + [`${prefix}-table`]: () => ({ + isSortable: boolean('Is sortable', false), + locale: text('Locale', 'en'), + overflowMenuOnHover: boolean('Overflow menu on hover', false), + radio: boolean('Radio', false), + size: select('Size', sizes, TABLE_SIZE.LG), + useStaticWidth: boolean('Use static width', false), + useZebraStyles: boolean('Use zebra styles', false), + }), + }, +}; + +export default { + title: 'Components/DataTable/Toolbar', + parameters: { + ...storyDocs.parameters, + }, +}; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/stories/data.ts b/web-components/packages/carbon-web-components/src/components/data-table/stories/data.ts deleted file mode 100644 index 64ac85ebe94c..000000000000 --- a/web-components/packages/carbon-web-components/src/components/data-table/stories/data.ts +++ /dev/null @@ -1,88 +0,0 @@ -/** - * @license - * - * Copyright IBM Corp. 2019 - * - * This source code is licensed under the Apache-2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - -import 'core-js/modules/es.array.flat.js'; -import 'core-js/modules/es.string.pad-start.js'; -import { TABLE_SORT_DIRECTION } from '../table-header-cell'; -import { TDemoTableColumn, TDemoTableRow, TDemoSortInfo } from './types'; - -export const columns: TDemoTableColumn[] = [ - { - id: 'name', - title: 'Name', - sortCycle: 'bi-states-from-ascending', - }, - { - id: 'protocol', - title: 'Protocol', - }, - { - id: 'port', - title: 'Port', - sortCycle: 'tri-states-from-ascending', - }, - { - id: 'rule', - title: 'Rule', - }, - { - id: 'attachedGroups', - title: 'Attached Groups', - }, - { - id: 'status', - title: 'Status', - }, -]; - -export const rows: TDemoTableRow[] = [ - { - id: 1, - name: 'Load Balancer 1', - protocol: 'HTTP', - port: 80, - rule: 'Round Robin', - attachedGroups: "Maureen's VM Groups", - status: 'Active', - }, - { - id: 2, - name: 'Load Balancer 2', - protocol: 'HTTPS', - port: 443, - rule: 'Round Robin', - attachedGroups: "Maureen's VM Groups", - status: 'Active', - }, - { - id: 3, - selected: true, - name: 'Load Balancer 3', - protocol: 'HTTP', - port: 80, - rule: 'Round Robin', - attachedGroups: "Maureen's VM Groups", - status: 'Active', - }, -]; - -export const rowsMany: TDemoTableRow[] = Array.from(new Array(50)) - .map((_item, i) => - rows.map((row, j) => ({ - ...row, - id: i * 3 + j, - name: `Load Balancer ${String(i * 3 + j + 1).padStart(3, '0')}`, - })) - ) - .flat(); - -export const sortInfo: TDemoSortInfo = { - columnId: 'name', - direction: TABLE_SORT_DIRECTION.ASCENDING, -}; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/stories/types.ts b/web-components/packages/carbon-web-components/src/components/data-table/stories/types.ts deleted file mode 100644 index 0e47e6fd9a50..000000000000 --- a/web-components/packages/carbon-web-components/src/components/data-table/stories/types.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @license - * - * Copyright IBM Corp. 2019 - * - * This source code is licensed under the Apache-2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - -import { TABLE_SORT_DIRECTION } from '../table-header-cell'; - -/** - * Example data structure of data table column. - */ -export type TDemoTableColumn = { - id: string; - title: string; - sortCycle?: string; -}; - -/** - * Example data structure of data table row. - */ -export type TDemoTableRow = { - id: number; - selected?: boolean; - [key: string]: any; -}; - -/** - * Example structure of table sorting info. - */ -export type TDemoSortInfo = { - columnId: string; - direction: TABLE_SORT_DIRECTION; -}; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/table-batch-actions.ts b/web-components/packages/carbon-web-components/src/components/data-table/table-batch-actions.ts index f62c8df79da9..98028f50163f 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/table-batch-actions.ts +++ b/web-components/packages/carbon-web-components/src/components/data-table/table-batch-actions.ts @@ -19,12 +19,13 @@ import styles from './data-table.scss'; * @fires cds-table-batch-actions-cancel-clicked - The custom event fired after the Cancel button is clicked. */ @customElement(`${prefix}-table-batch-actions`) -class BXTableBatchActions extends LitElement { +class CDSTableBatchActions extends LitElement { /** * Handles `click` event on the Cancel button. */ private _handleCancel() { - const { eventClickCancel } = this.constructor as typeof BXTableBatchActions; + const { eventClickCancel } = this + .constructor as typeof CDSTableBatchActions; this.dispatchEvent( new CustomEvent(eventClickCancel, { bubbles: true, composed: true }) ); @@ -50,6 +51,14 @@ class BXTableBatchActions extends LitElement { @property({ type: Number, attribute: 'selected-rows-count' }) selectedRowsCount = 0; + firstUpdated() { + this.querySelectorAll( + (this.constructor as typeof CDSTableBatchActions).selectorButtons + ).forEach((e) => { + e.setAttribute('batch-action', ''); + }); + } + updated(changedProperties) { if (changedProperties.has('active')) { this.setAttribute('tabindex', `${this.active ? '' : '-1'}`); @@ -79,6 +88,13 @@ class BXTableBatchActions extends LitElement { `; } + /** + * The CSS selector to find the action buttons. + */ + static get selectorButtons() { + return `${prefix}-button`; + } + /** * The name of the custom event fired after the Cancel button is clicked. */ @@ -89,4 +105,4 @@ class BXTableBatchActions extends LitElement { static styles = styles; } -export default BXTableBatchActions; +export default CDSTableBatchActions; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/table-body.ts b/web-components/packages/carbon-web-components/src/components/data-table/table-body.ts index dd5cf2d20de6..16fdda169eb3 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/table-body.ts +++ b/web-components/packages/carbon-web-components/src/components/data-table/table-body.ts @@ -10,8 +10,7 @@ import { LitElement, html } from 'lit'; import { property, customElement, query } from 'lit/decorators.js'; import { prefix } from '../../globals/settings'; -import { TABLE_COLOR_SCHEME } from './defs'; -import BXTableRow from './table-row'; +import CDSTableRow from './table-row'; import styles from './data-table.scss'; /** @@ -20,7 +19,7 @@ import styles from './data-table.scss'; * @element cds-table-body */ @customElement(`${prefix}-table-body`) -class BXTableBody extends LitElement { +class CDSTableBody extends LitElement { /** * The `` element in the shadow DOM. */ @@ -31,14 +30,12 @@ class BXTableBody extends LitElement { * Updates `even`/`odd` properties of the child ``s. */ private _updateZebra() { - const { colorScheme, _slotNode: slotNode } = this; + const { useZebraStyles, _slotNode: slotNode } = this; slotNode.assignedNodes().forEach((node) => { if (node.nodeType === Node.ELEMENT_NODE) { - const odd = (node as HTMLElement).matches('*:nth-of-type(odd)'); - (node as BXTableRow).even = - colorScheme === TABLE_COLOR_SCHEME.ZEBRA && !odd; - (node as BXTableRow).odd = - colorScheme === TABLE_COLOR_SCHEME.ZEBRA && odd; + const even = (node as HTMLElement).matches('*:nth-of-type(even)'); + (node as CDSTableRow).even = useZebraStyles && even; + (node as CDSTableRow).odd = useZebraStyles && !even; } }); } @@ -50,11 +47,19 @@ class BXTableBody extends LitElement { this._updateZebra(); }; + /** + * TODO: Uncomment when Carbon fully implements sticky header + * Specify whether the header should be sticky. + * Still experimental: may not work with every combination of table props + */ + // @property({ type: Boolean, reflect: true, attribute: 'sticky-header' }) + // stickyHeader = false; + /** * The color scheme. */ - @property({ reflect: true, attribute: 'color-scheme' }) - colorScheme = TABLE_COLOR_SCHEME.REGULAR; + @property({ type: Boolean, reflect: true, attribute: 'use-zebra-styles' }) + useZebraStyles = false; connectedCallback() { if (!this.hasAttribute('role')) { @@ -64,7 +69,7 @@ class BXTableBody extends LitElement { } updated(changedProperties) { - if (changedProperties.has('colorScheme')) { + if (changedProperties.has('useZebraStyles')) { this._updateZebra(); } } @@ -77,4 +82,4 @@ class BXTableBody extends LitElement { static styles = styles; } -export default BXTableBody; +export default CDSTableBody; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/table-cell-content.ts b/web-components/packages/carbon-web-components/src/components/data-table/table-cell-content.ts new file mode 100644 index 000000000000..8bed58086591 --- /dev/null +++ b/web-components/packages/carbon-web-components/src/components/data-table/table-cell-content.ts @@ -0,0 +1,29 @@ +/** + * @license + * + * Copyright IBM Corp. 2019, 2023 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { LitElement, html } from 'lit'; +import { customElement } from 'lit/decorators.js'; +import { prefix } from '../../globals/settings'; +import styles from './data-table.scss'; + +/** + * Data table cell content. + * + * @element cds-table-cell-content + */ +@customElement(`${prefix}-table-cell-content`) +class CDSTableCellContent extends LitElement { + render() { + return html` `; + } + + static styles = styles; +} + +export default CDSTableCellContent; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/table-cell.ts b/web-components/packages/carbon-web-components/src/components/data-table/table-cell.ts index 4d4a55de6963..1fad2574f944 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/table-cell.ts +++ b/web-components/packages/carbon-web-components/src/components/data-table/table-cell.ts @@ -8,7 +8,7 @@ */ import { LitElement, html } from 'lit'; -import { customElement } from 'lit/decorators.js'; +import { customElement, property } from 'lit/decorators.js'; import { prefix } from '../../globals/settings'; import styles from './data-table.scss'; @@ -18,7 +18,31 @@ import styles from './data-table.scss'; * @element cds-table-cell */ @customElement(`${prefix}-table-cell`) -class BXTableCell extends LitElement { +class CDSTableCell extends LitElement { + /** + * Specify whether the overflow menu (if it exists) should be shown always, or only on hover + */ + @property({ + type: Boolean, + reflect: true, + attribute: 'overflow-menu-on-hover', + }) + overflowMenuOnHover = false; + + /** + * The table size. + */ + @property({ reflect: true }) + size; + + /** + * TODO: Uncomment when Carbon fully implements sticky header + * Specify whether the header should be sticky. + * Still experimental: may not work with every combination of table props + */ + // @property({ type: Boolean, reflect: true, attribute: 'sticky-header' }) + // stickyHeader = false; + connectedCallback() { if (!this.hasAttribute('role')) { this.setAttribute('role', 'cell'); @@ -27,10 +51,10 @@ class BXTableCell extends LitElement { } render() { - return html` `; + return html``; } static styles = styles; } -export default BXTableCell; +export default CDSTableCell; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/table-expand-row.ts b/web-components/packages/carbon-web-components/src/components/data-table/table-expand-row.ts deleted file mode 100644 index 87fa483cd488..000000000000 --- a/web-components/packages/carbon-web-components/src/components/data-table/table-expand-row.ts +++ /dev/null @@ -1,141 +0,0 @@ -/** - * @license - * - * Copyright IBM Corp. 2020, 2023 - * - * This source code is licensed under the Apache-2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - -import { html } from 'lit'; -import { property, customElement } from 'lit/decorators.js'; -import ChevronRight16 from '@carbon/icons/lib/chevron--right/16'; -import { prefix } from '../../globals/settings'; -import HostListenerMixin from '../../globals/mixins/host-listener'; -import HostListener from '../../globals/decorators/host-listener'; -import BXTableRow from './table-row'; -import BXTableExpandedRow from './table-expanded-row'; -import styles from './data-table.scss'; - -/** - * The expando row in table row. - * - * @element cds-table-expand-row - */ -@customElement(`${prefix}-table-expand-row`) -class BXTableExpandRow extends HostListenerMixin(BXTableRow) { - /** - * Handles `click` event on the expando button. - */ - private _handleClickExpando() { - this._handleUserInitiatedToggleExpando(); - } - - /** - * Handles `mouseover`/`mouseout` event handler on this element. - * - * @param event The event. - */ - @HostListener('mouseover') - @HostListener('mouseout') - // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to - private _handleMouseOverOut(event: MouseEvent) { - const { selectorExpandedRow } = this.constructor as typeof BXTableExpandRow; - const { nextElementSibling } = this; - if (nextElementSibling?.matches(selectorExpandedRow)) { - (nextElementSibling as BXTableExpandedRow).highlighted = - event.type === 'mouseover'; - } - } - - /** - * Handles user-initiated toggle request of the expando button in this table row. - * - * @param expanded The new expanded state. - */ - private _handleUserInitiatedToggleExpando(expanded = !this.expanded) { - const init = { - bubbles: true, - cancelable: true, - composed: true, - detail: { - expanded, - }, - }; - if ( - this.dispatchEvent( - new CustomEvent( - ( - this.constructor as typeof BXTableExpandRow - ).eventBeforeExpandoToggle, - init - ) - ) - ) { - this.expanded = expanded; - this.dispatchEvent( - new CustomEvent( - (this.constructor as typeof BXTableExpandRow).eventExpandoToggle, - init - ) - ); - } - } - - protected _renderFirstCells() { - const { _handleClickExpando: handleClickExpando } = this; - return html` -
- -
- ${super._renderFirstCells()} - `; - } - - /** - * `true` if the table row should be expanded. - */ - @property({ type: Boolean, reflect: true }) - expanded = false; - - updated(changedProperties) { - if (changedProperties.has('expanded')) { - const { selectorExpandedRow } = this - .constructor as typeof BXTableExpandRow; - const { expanded, nextElementSibling } = this; - if (nextElementSibling?.matches(selectorExpandedRow)) { - (nextElementSibling as BXTableExpandedRow).expanded = expanded; - } - } - } - - /** - * A selector that will return the corresponding expanded row. - */ - static get selectorExpandedRow() { - return `${prefix}-table-expanded-row`; - } - - /** - * The name of the custom event fired before the expanded state this row is being toggled upon a user gesture. - * Cancellation of this event stops the user-initiated action of toggling the expanded state. - */ - static get eventBeforeExpandoToggle() { - return `${prefix}-table-row-expando-beingtoggled`; - } - - /** - * The name of the custom event fired after the expanded state this row is toggled upon a user gesture. - */ - static get eventExpandoToggle() { - return `${prefix}-table-row-expando-toggled`; - } - - static styles = styles; // `styles` here is a `CSSResult` generated by custom WebPack loader -} - -export default BXTableExpandRow; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/table-expanded-row.ts b/web-components/packages/carbon-web-components/src/components/data-table/table-expanded-row.ts index fe5e819f3059..35788dc76661 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/table-expanded-row.ts +++ b/web-components/packages/carbon-web-components/src/components/data-table/table-expanded-row.ts @@ -12,13 +12,33 @@ import { property, customElement } from 'lit/decorators.js'; import { prefix } from '../../globals/settings'; import styles from './data-table.scss'; +import HostListener from '../../globals/decorators/host-listener'; +import HostListenerMixin from '../../globals/mixins/host-listener'; +import CDSTableRow from './table-row'; /** * Table row of collapsible details. * * @element cds-table-expanded-row */ @customElement(`${prefix}-table-expanded-row`) -class BXTableExpandedRow extends LitElement { +class CDSTableExpandedRow extends HostListenerMixin(LitElement) { + /** + * Handles `mouseover`/`mouseout` event handler on this element. + * + * @param event The event. + */ + @HostListener('mouseover') + @HostListener('mouseout') + // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to + private _handleMouseOverOut(event: MouseEvent) { + const { selectorRow } = this.constructor as typeof CDSTableExpandedRow; + const { previousElementSibling } = this; + if (previousElementSibling?.matches(selectorRow)) { + (previousElementSibling as CDSTableRow).highlighted = + event.type === 'mouseover'; + } + } + /** * The colspan. */ @@ -31,12 +51,24 @@ class BXTableExpandedRow extends LitElement { @property({ type: Boolean, reflect: true }) expanded = false; + /** + * `true` if the table row should be filtered. + */ + @property({ type: Boolean, reflect: true }) + filtered = false; + /** * `true` if the table row should be highlighted. */ @property({ type: Boolean, reflect: true }) highlighted = false; + /** + * `true` if the previous table row has been selected + */ + @property({ type: Boolean, reflect: true }) + selected = false; + render() { const { colSpan } = this; return html` @@ -48,7 +80,14 @@ class BXTableExpandedRow extends LitElement { `; } + /** + * A selector that will return the previous sibling row. + */ + static get selectorRow() { + return `${prefix}-table-row`; + } + static styles = styles; // `styles` here is a `CSSResult` generated by custom WebPack loader } -export default BXTableExpandedRow; +export default CDSTableExpandedRow; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/table-head.ts b/web-components/packages/carbon-web-components/src/components/data-table/table-head.ts index 3a71f6515e85..5f3546ff0317 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/table-head.ts +++ b/web-components/packages/carbon-web-components/src/components/data-table/table-head.ts @@ -18,7 +18,15 @@ import styles from './data-table.scss'; * @element cds-table-head */ @customElement(`${prefix}-table-head`) -class BXTableHead extends LitElement { +class CDSTableHead extends LitElement { + /** + * TODO: Uncomment when Carbon fully implements sticky header + * Specify whether the header should be sticky. + * Still experimental: may not work with every combination of table props + */ + //@property({ type: Boolean, reflect: true, attribute: 'sticky-header' }) + // stickyHeader = false; + connectedCallback() { if (!this.hasAttribute('role')) { this.setAttribute('role', 'rowgroup'); @@ -33,4 +41,4 @@ class BXTableHead extends LitElement { static styles = styles; } -export default BXTableHead; +export default CDSTableHead; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/table-header-cell-skeleton.ts b/web-components/packages/carbon-web-components/src/components/data-table/table-header-cell-skeleton.ts deleted file mode 100644 index 86588b4b5408..000000000000 --- a/web-components/packages/carbon-web-components/src/components/data-table/table-header-cell-skeleton.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * @license - * - * Copyright IBM Corp. 2019, 2023 - * - * This source code is licensed under the Apache-2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - -import { customElement } from 'lit/decorators.js'; -import { prefix } from '../../globals/settings'; -import BXTableCell from './table-cell'; - -/** - * Data table header cell with skeleton content. - * - * @element cds-table-header-cell-skeleton - */ -@customElement(`${prefix}-table-header-cell-skeleton`) -class BXTableHeaderCellSkeleton extends BXTableCell {} - -export default BXTableHeaderCellSkeleton; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/table-header-cell.ts b/web-components/packages/carbon-web-components/src/components/data-table/table-header-cell.ts index 575c894e922f..939e3504b058 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/table-header-cell.ts +++ b/web-components/packages/carbon-web-components/src/components/data-table/table-header-cell.ts @@ -9,8 +9,8 @@ import { LitElement, html } from 'lit'; import { property, customElement } from 'lit/decorators.js'; -import ArrowsVeritcal16 from '@carbon/icons/lib/arrows--vertical/16'; -import ArrowDown16 from '@carbon/icons/lib/arrow--down/16'; +import ArrowsVertical32 from '@carbon/icons/lib/arrows--vertical/32'; +import ArrowDown32 from '@carbon/icons/lib/arrow--down/32'; import { prefix } from '../../globals/settings'; import FocusMixin from '../../globals/mixins/focus'; import { @@ -31,7 +31,7 @@ export { TABLE_SORT_CYCLE, TABLE_SORT_CYCLES, TABLE_SORT_DIRECTION }; * Cancellation of this event stops the user-initiated change in sort direction. */ @customElement(`${prefix}-table-header-cell`) -class BXTableHeaderCell extends FocusMixin(LitElement) { +class CDSTableHeaderCell extends FocusMixin(LitElement) { /** * Handles `click` event on the sort button. * @@ -47,7 +47,7 @@ class BXTableHeaderCell extends FocusMixin(LitElement) { sortDirection: nextSortDirection, }, }; - const constructor = this.constructor as typeof BXTableHeaderCell; + const constructor = this.constructor as typeof CDSTableHeaderCell; if ( this.dispatchEvent(new CustomEvent(constructor.eventBeforeSort, init)) ) { @@ -78,7 +78,7 @@ class BXTableHeaderCell extends FocusMixin(LitElement) { 'Likely that `_getNextSort()` is called with non-sorted table column, which should not happen in regular condition.' ); } - const directions = (this.constructor as typeof BXTableHeaderCell) + const directions = (this.constructor as typeof CDSTableHeaderCell) .TABLE_SORT_CYCLES[sortCycle]; const index = directions.indexOf(sortDirection as TABLE_SORT_DIRECTION); if (index < 0) { @@ -93,6 +93,23 @@ class BXTableHeaderCell extends FocusMixin(LitElement) { return directions[(index + 1) % directions.length]; } + /** + * `true` if the table has expandable rows + */ + @property({ type: Boolean, reflect: true, attribute: 'is-sortable' }) + isExpandable = false; + + /** + * `true` if this table has selectable rows + */ + @property({ type: Boolean, reflect: true, attribute: 'is-sortable' }) + isSelectable = false; + /** + * `true` if this table header column should be sortable + */ + @property({ type: Boolean, reflect: true, attribute: 'is-sortable' }) + isSortable = false; + /** * `true` if this table header cell is of a primary sorting column. */ @@ -112,6 +129,14 @@ class BXTableHeaderCell extends FocusMixin(LitElement) { @property({ reflect: true, attribute: 'sort-direction' }) sortDirection?: TABLE_SORT_DIRECTION; + /** + * TODO: Uncomment when Carbon fully implements sticky header + * Specify whether the header should be sticky. + * Still experimental: may not work with every combination of table props + */ + // @property({ type: Boolean, reflect: true, attribute: 'sticky-header' }) + // stickyHeader = false; + connectedCallback() { if (!this.hasAttribute('role')) { this.setAttribute('role', 'columnheader'); @@ -119,16 +144,22 @@ class BXTableHeaderCell extends FocusMixin(LitElement) { super.connectedCallback(); } + updated(changedProperties) { + if (this.isSortable && !changedProperties.has('sortDirection')) { + this.sortDirection = TABLE_SORT_DIRECTION.NONE; + } + } + render() { const { sortDirection } = this; if (sortDirection) { const sortIcon = sortDirection === TABLE_SORT_DIRECTION.NONE - ? ArrowsVeritcal16({ + ? ArrowsVertical32({ part: 'sort-icon', class: `${prefix}--table-sort__icon-unsorted`, }) - : ArrowDown16({ + : ArrowDown32({ part: 'sort-icon', class: `${prefix}--table-sort__icon`, }); @@ -168,4 +199,4 @@ class BXTableHeaderCell extends FocusMixin(LitElement) { static TABLE_SORT_CYCLES = TABLE_SORT_CYCLES; } -export default BXTableHeaderCell; +export default CDSTableHeaderCell; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/table-header-description.ts b/web-components/packages/carbon-web-components/src/components/data-table/table-header-description.ts new file mode 100644 index 000000000000..eca5de3845cb --- /dev/null +++ b/web-components/packages/carbon-web-components/src/components/data-table/table-header-description.ts @@ -0,0 +1,29 @@ +/** + * @license + * + * Copyright IBM Corp. 2019, 2023 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { LitElement, html } from 'lit'; +import { customElement } from 'lit/decorators.js'; +import { prefix } from '../../globals/settings'; +import styles from './data-table.scss'; + +/** + * Data table header description + * + * @element cds-table-header-description + */ +@customElement(`${prefix}-table-header-description`) +class CDSTableHeaderDescription extends LitElement { + render() { + return html` `; + } + + static styles = styles; +} + +export default CDSTableHeaderDescription; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/table-header-expand-row.ts b/web-components/packages/carbon-web-components/src/components/data-table/table-header-expand-row.ts deleted file mode 100644 index 04da24d4b9ac..000000000000 --- a/web-components/packages/carbon-web-components/src/components/data-table/table-header-expand-row.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @license - * - * Copyright IBM Corp. 2020, 2023 - * - * This source code is licensed under the Apache-2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - -import { customElement } from 'lit/decorators.js'; -import { prefix } from '../../globals/settings'; -import BXTableExpandRow from './table-expand-row'; - -/** - * Data table header row. - * - * @element cds-table-header-expand-row - */ -@customElement(`${prefix}-table-header-expand-row`) -class BXTableHeaderExpandRow extends BXTableExpandRow { - /** - * The name of the custom event fired before this row is selected/unselected upon a user gesture. - * Cancellation of this event stops the user-initiated change in selection. - */ - static get eventBeforeChangeSelection() { - return `${prefix}-table-change-selection-all`; - } - - /** - * The name of the custom event fired before the expanded state this row is being toggled upon a user gesture. - * Cancellation of this event stops the user-initiated action of toggling the expanded state. - */ - static get eventBeforeExpandoToggle() { - return `${prefix}-table-row-expando-beingtoggled-all`; - } - - /** - * The name of the custom event fired after the expanded state this row is toggled upon a user gesture. - */ - static get eventExpandoToggle() { - return `${prefix}-table-row-expando-toggled-all`; - } -} - -export default BXTableHeaderExpandRow; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/table-header-row.ts b/web-components/packages/carbon-web-components/src/components/data-table/table-header-row.ts index 0388c4161701..b42f3754020a 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/table-header-row.ts +++ b/web-components/packages/carbon-web-components/src/components/data-table/table-header-row.ts @@ -9,7 +9,7 @@ import { customElement } from 'lit/decorators.js'; import { prefix } from '../../globals/settings'; -import BXTableRow from './table-row'; +import CDSTableRow from './table-row'; /** * Data table header row. @@ -17,7 +17,7 @@ import BXTableRow from './table-row'; * @element cds-table-header-row */ @customElement(`${prefix}-table-header-row`) -class BXTableHeaderRow extends BXTableRow { +class CDSTableHeaderRow extends CDSTableRow { /** * The name of the custom event fired before this row is selected/unselected upon a user gesture. * Cancellation of this event stops the user-initiated change in selection. @@ -27,4 +27,4 @@ class BXTableHeaderRow extends BXTableRow { } } -export default BXTableHeaderRow; +export default CDSTableHeaderRow; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/table-cell-skeleton.ts b/web-components/packages/carbon-web-components/src/components/data-table/table-header-title.ts similarity index 50% rename from web-components/packages/carbon-web-components/src/components/data-table/table-cell-skeleton.ts rename to web-components/packages/carbon-web-components/src/components/data-table/table-header-title.ts index 88afdf05485f..40a2474f2454 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/table-cell-skeleton.ts +++ b/web-components/packages/carbon-web-components/src/components/data-table/table-header-title.ts @@ -7,21 +7,23 @@ * LICENSE file in the root directory of this source tree. */ -import { html } from 'lit'; +import { LitElement, html } from 'lit'; import { customElement } from 'lit/decorators.js'; import { prefix } from '../../globals/settings'; -import BXTableCell from './table-cell'; +import styles from './data-table.scss'; /** - * Data table cell with skeleton content. + * Data table header title * - * @element cds-table-cell-skeleton + * @element cds-table-header-title */ -@customElement(`${prefix}-table-cell-skeleton`) -class BXTableCellSkeleton extends BXTableCell { +@customElement(`${prefix}-table-header-title`) +class CDSTableHeader extends LitElement { render() { - return html` `; + return html` `; } + + static styles = styles; } -export default BXTableCellSkeleton; +export default CDSTableHeader; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/table-row.ts b/web-components/packages/carbon-web-components/src/components/data-table/table-row.ts index 6bd2441e57e3..c4649a6f7ccd 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/table-row.ts +++ b/web-components/packages/carbon-web-components/src/components/data-table/table-row.ts @@ -10,9 +10,15 @@ import { LitElement, html } from 'lit'; import { property, customElement } from 'lit/decorators.js'; import { prefix } from '../../globals/settings'; +import ChevronRight16 from '@carbon/web-components/es/icons/chevron--right/16'; import FocusMixin from '../../globals/mixins/focus'; import styles from './data-table.scss'; +import HostListener from '../../globals/decorators/host-listener'; +import HostListenerMixin from '../../globals/mixins/host-listener'; +import CDSTableExpandedRow from './table-expanded-row'; +import CDSTableCell from './table-cell'; + /** * Data table row. * @@ -24,14 +30,50 @@ import styles from './data-table.scss'; * Cancellation of this event stops the user-initiated change in selection. */ @customElement(`${prefix}-table-row`) -class BXTableRow extends FocusMixin(LitElement) { +class CDSTableRow extends HostListenerMixin(FocusMixin(LitElement)) { + /** + * Handles `click` event on the radio button. + * + * @param event The event. + */ + @HostListener('eventRadioChange') + // @ts-ignore + private _handleClickSelectionRadio(event: CustomEvent) { + const { detail } = event; + const selected = detail.checked; + const init = { + bubbles: true, + cancelable: true, + composed: true, + detail: { + selected, + }, + }; + const constructor = this.constructor as typeof CDSTableRow; + if ( + this.dispatchEvent( + new CustomEvent(constructor.eventBeforeChangeSelection, init) + ) + ) { + this.selected = selected; + const { selectorExpandedRow } = this.constructor as typeof CDSTableRow; + + if (this.nextElementSibling?.matches(selectorExpandedRow)) { + (this.nextElementSibling as CDSTableExpandedRow).selected = selected; + } + } + } + /** * Handles `click` event on the check box. * * @param event The event. */ - private _handleClickSelectionCheckbox(event: Event) { - const selected = (event.target as HTMLInputElement).checked; + @HostListener('eventCheckboxChange') + // @ts-ignore + private _handleClickSelectionCheckbox(event: CustomEvent) { + const { detail } = event; + const selected = detail.checked; const init = { bubbles: true, cancelable: true, @@ -40,54 +82,139 @@ class BXTableRow extends FocusMixin(LitElement) { selected, }, }; - const constructor = this.constructor as typeof BXTableRow; + const constructor = this.constructor as typeof CDSTableRow; if ( this.dispatchEvent( new CustomEvent(constructor.eventBeforeChangeSelection, init) ) ) { this.selected = selected; + const { selectorExpandedRow } = this.constructor as typeof CDSTableRow; + + if (this.nextElementSibling?.matches(selectorExpandedRow)) { + (this.nextElementSibling as CDSTableExpandedRow).selected = selected; + } } } + /** + * Handles `click` event on the expando button. + */ + private _handleClickExpando() { + this._handleUserInitiatedToggleExpando(); + } + + /** + * Handles `mouseover`/`mouseout` event handler on this element. + * + * @param event The event. + */ + @HostListener('mouseover') + @HostListener('mouseout') + // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to + private _handleMouseOverOut(event: MouseEvent) { + const { selectorExpandedRow, selectorTableCellOverflowMenu } = this + .constructor as typeof CDSTableRow; + const { nextElementSibling } = this; + if (nextElementSibling?.matches(selectorExpandedRow)) { + (nextElementSibling as CDSTableExpandedRow).highlighted = + event.type === 'mouseover'; + } + if (this.overflowMenuOnHover) { + const overflowMenu = this.querySelector(selectorTableCellOverflowMenu); + const parentCell = overflowMenu?.parentElement; + + if (event.type === 'mouseout') { + (parentCell as CDSTableCell).overflowMenuOnHover = true; + } else { + (parentCell as CDSTableCell).overflowMenuOnHover = false; + } + } + } + + /** + * Handles user-initiated toggle request of the expando button in this table row. + * + * @param expanded The new expanded state. + */ + _handleUserInitiatedToggleExpando(expanded = !this.expanded) { + const init = { + bubbles: true, + cancelable: true, + composed: true, + detail: { + expanded, + }, + }; + if ( + this.dispatchEvent( + new CustomEvent( + (this.constructor as typeof CDSTableRow).eventBeforeExpandoToggle, + init + ) + ) + ) { + this.expanded = expanded; + this.dispatchEvent( + new CustomEvent( + (this.constructor as typeof CDSTableRow).eventExpandoToggle, + init + ) + ); + } + } + + protected _renderExpandButton() { + const { _handleClickExpando: handleClickExpando } = this; + return html` +
+ +
+ `; + } + /** * @returns The first set of table cells. */ protected _renderFirstCells() { const { disabled, + hideCheckbox, + radio, selected, selectionLabel, selectionName, selectionValue, } = this; - // Using `@click` instead of `@change` to support `.preventDefault()` return !selectionName ? undefined : html` -
- ${html` - - - `} +
+ ${radio + ? html`` + : html` `}
`; } + /** + * `true` if this table should support batch expansion + */ + @property({ type: Boolean, reflect: true, attribute: 'batch-expansion' }) + batchExpansion = false; + /** * `true` if this table row should be disabled. */ @@ -103,6 +230,41 @@ class BXTableRow extends FocusMixin(LitElement) { @property({ type: Boolean, reflect: true }) even = false; + /** + * `true` if this table row can be expanded to show content underneath + * + * @private + */ + @property({ type: Boolean, reflect: true }) + expandable = false; + + /** + * `true` when the table row expanded is showing + * + * @private + */ + @property({ type: Boolean, reflect: true }) + expanded = false; + + /** + * `true` if this table row should be filtered out. + */ + @property({ type: Boolean, reflect: true }) + filtered = false; + + /** + * Specify whether the checkbox should be present in the DOM, + * but invisible and uninteractable. + */ + @property({ type: Boolean, reflect: true, attribute: 'hide-checkbox' }) + hideCheckbox = false; + + /** + * `true` if the table row should be highlighted. + */ + @property({ type: Boolean, reflect: true }) + highlighted = false; + /** * `true` if this table row is placed at an odd position in parent ``. * `` sets this property, _only_ in zebra stripe mode. @@ -112,6 +274,24 @@ class BXTableRow extends FocusMixin(LitElement) { @property({ type: Boolean, reflect: true }) odd = false; + /** + * Specify whether the overflow menu (if it exists) should be shown always, or only on hover + */ + @property({ + type: Boolean, + reflect: true, + attribute: 'overflow-menu-on-hover', + }) + overflowMenuOnHover = false; + + /** + * Specify whether the control should be a radio button or inline checkbox + * + * @private + */ + @property({ type: Boolean, reflect: true }) + radio = false; + /** * `true` if this table row should be selected. */ @@ -137,6 +317,14 @@ class BXTableRow extends FocusMixin(LitElement) { @property({ attribute: 'selection-value' }) selectionValue = ''; + /** + * TODO: Uncomment when Carbon fully implements sticky header + * Specify whether the header should be sticky. + * Still experimental: may not work with every combination of table props + */ + // @property({ type: Boolean, reflect: true, attribute: 'sticky-header' }) + // stickyHeader = false; + connectedCallback() { if (!this.hasAttribute('role')) { this.setAttribute('role', 'row'); @@ -144,8 +332,49 @@ class BXTableRow extends FocusMixin(LitElement) { super.connectedCallback(); } + updated(changedProperties) { + if (changedProperties.has('expanded')) { + const { selectorExpandedRow } = this.constructor as typeof CDSTableRow; + const { expanded, nextElementSibling } = this; + if (nextElementSibling?.matches(selectorExpandedRow)) { + (nextElementSibling as CDSTableExpandedRow).expanded = expanded; + } + } + + if (changedProperties.has('highlighted')) { + const { selectorExpandedRow } = this.constructor as typeof CDSTableRow; + const { highlighted, nextElementSibling } = this; + if (nextElementSibling?.matches(selectorExpandedRow)) { + (nextElementSibling as CDSTableExpandedRow).highlighted = highlighted; + } + } + } + render() { - return html` ${this._renderFirstCells()} `; + if (this.selectionName) { + this.closest( + (this.constructor as typeof CDSTableRow).selectorTable + )?.setAttribute('is-selectable', ''); + } + return html` + ${this.expandable ? this._renderExpandButton() : ''} + ${this._renderFirstCells()} + + `; + } + + /** + * The name of the custom event fired after this radio button changes its checked state. + */ + static get eventRadioChange() { + return `${prefix}-radio-button-changed`; + } + + /** + * The name of the custom event fired after this radio button changes its checked state. + */ + static get eventCheckboxChange() { + return `${prefix}-checkbox-changed`; } /** @@ -157,13 +386,42 @@ class BXTableRow extends FocusMixin(LitElement) { } /** - * The CSS selector to find the table. + * A selector that will return the parent table */ static get selectorTable() { return `${prefix}-table`; } + /** + * The CSS selector to find the overflow menu on the table cell + */ + static get selectorTableCellOverflowMenu() { + return `${prefix}-table-cell ${prefix}-overflow-menu`; + } + + /** + * A selector that will return the corresponding expanded row. + */ + static get selectorExpandedRow() { + return `${prefix}-table-expanded-row`; + } + + /** + * The name of the custom event fired before the expanded state this row is being toggled upon a user gesture. + * Cancellation of this event stops the user-initiated action of toggling the expanded state. + */ + static get eventBeforeExpandoToggle() { + return `${prefix}-table-row-expando-beingtoggled`; + } + + /** + * The name of the custom event fired after the expanded state this row is toggled upon a user gesture. + */ + static get eventExpandoToggle() { + return `${prefix}-table-row-expando-toggled`; + } + static styles = styles; } -export default BXTableRow; +export default CDSTableRow; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/table-skeleton.ts b/web-components/packages/carbon-web-components/src/components/data-table/table-skeleton.ts new file mode 100644 index 000000000000..e58b99320ff5 --- /dev/null +++ b/web-components/packages/carbon-web-components/src/components/data-table/table-skeleton.ts @@ -0,0 +1,164 @@ +/** + * @license + * + * Copyright IBM Corp. 2019, 2023 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { classMap } from 'lit/directives/class-map.js'; +import { LitElement, html } from 'lit'; +import { property, customElement } from 'lit/decorators.js'; +import { prefix } from '../../globals/settings'; +import styles from './data-table.scss'; + +/** + * Data table skeleton + * + * @element cds-table-skeleton + */ +@customElement(`${prefix}-table-skeleton`) +class CDSTableSkeleton extends LitElement { + /** + * Optionally specify the displayed headers + */ + @property() + private headers: string[] = []; + + /** + * Optionally specify whether you want the Skeleton to be rendered as a compact DataTable + */ + @property({ type: Boolean, reflect: true }) + compact = false; + + /** + * Specify the number of columns that you want to render in the skeleton state + */ + @property({ type: Number, reflect: true, attribute: 'column-count' }) + columnCount = 5; + + /** + * Specify the number of rows that you want to render in the skeleton state + */ + @property({ type: Number, reflect: true, attribute: 'row-count' }) + rowCount = 5; + + /** + * Specify if the table header should be rendered as part of the skeleton. + */ + @property({ type: Boolean, reflect: true, attribute: 'show-header' }) + showHeader = true; + + /** + * Specify if the table toolbar should be rendered as part of the skeleton. + */ + @property({ type: Boolean, reflect: true, attribute: 'show-toolbar' }) + showToolbar = true; + + /** + * true to add useZebraStyles striping. + */ + @property({ type: Boolean, reflect: true }) + zebra = false; + + /** + * @returns The header + */ + protected _renderHeader() { + const { showHeader } = this; + return !showHeader + ? undefined + : html` +
+
+
+
+
+
+ `; + } + + /** + * @returns The header + */ + protected _renderToolbar() { + const { showToolbar } = this; + return !showToolbar + ? undefined + : html` +
+
+ +
+
+ `; + } + + connectedCallback() { + if (!this.hasAttribute('role')) { + this.setAttribute('role', 'table'); + } + super.connectedCallback(); + } + + updated() { + if (this.headers.length) { + this.columnCount = this.headers.length; + } else { + this.headers = Array(this.columnCount).fill(''); + } + } + + render() { + const { compact, columnCount, headers, rowCount, zebra } = this; + const classes = classMap({ + [`${prefix}--skeleton`]: true, + [`${prefix}--data-table`]: true, + [`${prefix}--data-table--compact`]: compact, + [`${prefix}--data-table--zebra`]: zebra, + }); + return html` + ${this._renderHeader()} ${this._renderToolbar()} + + + + + ${Array.from(new Array(columnCount)).map( + (_, index) => + html` + + ` + )} + + + + ${Array.from(new Array(rowCount)).map( + (_) => + html` + + ${Array.from(new Array(columnCount)).map( + (_) => + html` + + ` + )} + + ` + )} + +
+
+ ${headers[index]} +
+
+ +
+ `; + } + + static styles = styles; +} + +export default CDSTableSkeleton; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/table-toolbar-content.ts b/web-components/packages/carbon-web-components/src/components/data-table/table-toolbar-content.ts index 90509a2768e4..2e88b83038c7 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/table-toolbar-content.ts +++ b/web-components/packages/carbon-web-components/src/components/data-table/table-toolbar-content.ts @@ -18,17 +18,30 @@ import styles from './data-table.scss'; * @element cds-table-toolbar-content */ @customElement(`${prefix}-table-toolbar-content`) -class BXTableToolbarContent extends LitElement { +class CDSTableToolbarContent extends LitElement { /** * `true` if this batch actions bar is active. */ @property({ type: Boolean, reflect: true, attribute: 'has-batch-actions' }) hasBatchActions = false; + /** + * Table toolbar contents size + */ + @property({ reflect: true }) + size; + updated(changedProperties) { if (changedProperties.has('hasBatchActions')) { this.setAttribute('tabindex', `${this.hasBatchActions ? '-1' : ''}`); } + + if (changedProperties.has('size')) { + [...this.children].forEach((e) => { + const size = this.size === 'xs' ? 'sm' : this.size; + e.setAttribute('size', size); + }); + } } render() { @@ -38,4 +51,4 @@ class BXTableToolbarContent extends LitElement { static styles = styles; } -export default BXTableToolbarContent; +export default CDSTableToolbarContent; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/table-toolbar-search.ts b/web-components/packages/carbon-web-components/src/components/data-table/table-toolbar-search.ts index 02a9b6dc5b67..2ba843321689 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/table-toolbar-search.ts +++ b/web-components/packages/carbon-web-components/src/components/data-table/table-toolbar-search.ts @@ -24,7 +24,7 @@ import styles from './data-table.scss'; * @fires cds-search-input - The custom event fired after the search content is changed upon a user gesture. */ @customElement(`${prefix}-table-toolbar-search`) -class BXTableToolbarSearch extends HostListenerMixin(CDSSearch) { +class CDSTableToolbarSearch extends HostListenerMixin(CDSSearch) { @query('input') private _inputNode!: HTMLInputElement; @@ -55,7 +55,11 @@ class BXTableToolbarSearch extends HostListenerMixin(CDSSearch) { @HostListener('focusout') // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to private _handleFocusOut(event: FocusEvent) { - if (!this.contains(event.relatedTarget as Node) && !this.value) { + if ( + !this.contains(event.relatedTarget as Node) && + !this.value && + !this.persistent + ) { this.expanded = false; } } @@ -73,11 +77,17 @@ class BXTableToolbarSearch extends HostListenerMixin(CDSSearch) { @property({ type: Boolean, reflect: true }) expanded = false; + /** + * `true` if the search box should be always be open. + */ + @property({ type: Boolean, reflect: true }) + persistent = false; + /** * The search box size. */ @property({ reflect: true }) - size = INPUT_SIZE.SMALL; + size = INPUT_SIZE.LARGE; connectedCallback() { if (!this.hasAttribute('role')) { @@ -88,11 +98,19 @@ class BXTableToolbarSearch extends HostListenerMixin(CDSSearch) { render() { const result = super.render(); - const { expanded, size, _handleSearchClick: handleSearchClick } = this; + const { + persistent, + expanded, + size, + _handleSearchClick: handleSearchClick, + } = this; const classes = classMap({ [`${prefix}--search`]: true, [`${prefix}--search--${size}`]: size, }); + if (persistent) { + this.expanded = true; + } return html`
`; } + /** + * The CSS selector to find the overflow menu body + */ + static get selectorOverflowMenuBody() { + return `${prefix}-overflow-menu-body`; + } + + /** + * The CSS selector to find the toolbar contents + */ + static get selectorToolbarContent() { + return `${prefix}-table-toolbar-content`; + } + static styles = styles; } -export default BXTableToolbar; +export default CDSTableToolbar; diff --git a/web-components/packages/carbon-web-components/src/components/data-table/table.ts b/web-components/packages/carbon-web-components/src/components/data-table/table.ts index 5d9c9059bf81..175d77b50233 100644 --- a/web-components/packages/carbon-web-components/src/components/data-table/table.ts +++ b/web-components/packages/carbon-web-components/src/components/data-table/table.ts @@ -8,13 +8,26 @@ */ import { LitElement, html } from 'lit'; -import { property, customElement } from 'lit/decorators.js'; +import { property, customElement, state } from 'lit/decorators.js'; import { prefix } from '../../globals/settings'; import { forEach } from '../../globals/internal/collection-helpers'; -import { TABLE_COLOR_SCHEME, TABLE_SIZE } from './defs'; +import { TABLE_SIZE, TABLE_SORT_DIRECTION } from './defs'; import styles from './data-table.scss'; -export { TABLE_COLOR_SCHEME, TABLE_SIZE }; +import HostListener from '../../globals/decorators/host-listener'; +import HostListenerMixin from '../../globals/mixins/host-listener'; +import { + CDSRadioButton, + CDSTableBatchActions, + CDSTableCell, + CDSTableHeaderCell, + CDSTableHeaderRow, + CDSTableRow, + CDSTableToolbarSearch, +} from '../..'; +import CDSTableExpandedRow from './table-expanded-row'; + +export { TABLE_SIZE }; /** * Data table. @@ -22,18 +35,537 @@ export { TABLE_COLOR_SCHEME, TABLE_SIZE }; * @element cds-table */ @customElement(`${prefix}-table`) -class BXTable extends LitElement { +class CDSTable extends HostListenerMixin(LitElement) { + /** + * The map of how sorting direction affects sorting order. + */ + private collationFactors = { + [TABLE_SORT_DIRECTION.ASCENDING]: 1, + [TABLE_SORT_DIRECTION.DESCENDING]: -1, + }; + + /** + * Reference to download button + */ + @state() + private _downloadButton; + + /** + * Current search value for filtering + */ + @state() + private _searchValue = ''; + + /** + * Table header row within component + */ + @state() + private _tableHeaderRow; + + /** + * Table body + */ + @state() + private _tableBody; + + /** + * Table expanded row within component + */ + @state() + private _tableExpandedRows; + + /** + * Table rows within component + */ + @state() + private _tableRows; + + /** + * Reference to the component containing batch actions + */ + @state() + private _tableBatchActions; + + /** + * Reference to the table toolbar + */ + @state() + private _tableToolbar; + + /** + * Reference to the table toolbar content + */ + @state() + private _tableToolbarContent; + + @state() + private _selectedRows: CDSTableRow[] = []; + + /** + * `true` if this table should support batch expansion + */ + @property({ type: Boolean, reflect: true, attribute: 'batch-expansion' }) + batchExpansion = false; + + /** + * The g11n collator to use. + */ + @property({ attribute: false }) + collator; + + /** + * @param lhs A value. + * @param rhs Another value. + * @param collator A custom collator. + * @returns + * `0` if the given two values are equal + * A negative value to sort `lhs` to an index lower than `rhs` + * A positive value to sort `rhs` to an index lower than `lhs` + */ + // eslint-disable-next-line class-methods-use-this + customSortRow(lhs, rhs, collator) { + if (typeof lhs === 'number' && typeof rhs === 'number') { + return lhs - rhs; + } + return collator.compare(lhs, rhs); + } + + /** + * Specify whether the rows should be able to be expandable + */ + @property({ type: Boolean, reflect: true }) + expandable = false; + + /** + * The method used when filtering the table with the search bar. + * Can be replaced with custom method. + * + * @param row A table row. + * @param searchString A search string. + * @returns `false` if the given table row matches the given search string. + */ + @property() + filterRows = (rowText: string, searchString: string) => + rowText.toLowerCase().indexOf(searchString) < 0; + + /** + * The total headers + */ + @property() + headerCount = 0; + + /** + * `true` if this table contains selectable rows + */ + @property({ type: Boolean, reflect: true, attribute: 'is-selectable' }) + isSelectable = false; + + /** + * `true` if this table should support sorting. + */ + @property({ type: Boolean, reflect: true, attribute: 'is-sortable' }) + isSortable = false; + /** * The table size. */ @property({ reflect: true }) - size = TABLE_SIZE.LG; + locale = 'en'; /** - * `true` if this table should support sorting. + * Specify whether the overflow menu (if it exists) should be shown always, or only on hover + */ + @property({ + type: Boolean, + reflect: true, + attribute: 'overflow-menu-on-hover', + }) + overflowMenuOnHover = false; + + /** + * Specify whether the control should be a radio button or inline checkbox */ @property({ type: Boolean, reflect: true }) - sort = false; + radio = false; + + /** + * The table size. + */ + @property({ reflect: true }) + size = TABLE_SIZE.LG; + + /** + * TODO: Uncomment when Carbon fully implements sticky header + * Specify whether the header should be sticky. + * Still experimental: may not work with every combination of table props + */ + // @property({ type: Boolean, attribute: 'sticky-header', reflect: true }) + // stickyHeader = false; + + /** + * If true, will use a width of 'auto' instead of 100% + */ + @property({ type: Boolean, attribute: 'use-static-width', reflect: true }) + useStaticWidth = false; + + /** + * true to add useZebraStyles striping. + */ + @property({ type: Boolean, attribute: 'use-zebra-styles', reflect: true }) + useZebraStyles = false; + + @property({ type: Boolean, attribute: 'with-header', reflect: true }) + withHeader; + + private _handleSlotChange({ target }: Event) { + const hasContent = (target as HTMLSlotElement) + .assignedNodes() + .some( + (node) => node.nodeType !== Node.TEXT_NODE || node!.textContent!.trim() + ); + this.withHeader = hasContent; + } + + private _handleFilterRows() { + const unfilteredRows = [] as any; + forEach(this._tableRows, (elem) => { + const rowText = elem.textContent?.trim(); + const filtered = this.filterRows(rowText as string, this._searchValue); + (elem as any).filtered = filtered; + + if (!filtered) { + unfilteredRows.push(elem); + } + + if (this.expandable) { + (elem as any).nextElementSibling.filtered = filtered; + } + }); + + const init = { + bubbles: true, + cancelable: true, + composed: true, + detail: { + unfilteredRows, + }, + }; + this.dispatchEvent( + new CustomEvent( + (this.constructor as typeof CDSTable).eventTableFiltered, + init + ) + ); + } + + /** + * Download manager for selected rows. + */ + private _handleDownload({ target }) { + const data = [] as any; + + const elementsToArray = (elements) => + Array.from(elements, (element) => (element as any).textContent); + + const headerCells = this.querySelectorAll( + (this.constructor as typeof CDSTable).selectorHeaderCell + ); + const rows = this._selectedRows; + const headerTitleArray = elementsToArray(headerCells); + + rows.forEach((row) => { + const rowData = {}; + const cells = elementsToArray( + row.querySelectorAll( + (this.constructor as typeof CDSTable).selectorTableRowCells + ) + ); + + cells.forEach((cellText, index) => { + const headerTitle = headerTitleArray[index]; + rowData[headerTitle] = cellText; + }); + + data.push(rowData); + }); + + const blob = new Blob([JSON.stringify(data)], { type: 'application/json' }); + + target.href = URL.createObjectURL(blob); + } + + /** + * Handles batch expansion + */ + @HostListener('eventExpandoToggle') + // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to + private _handleBatchExpansion = async (event: CustomEvent) => { + const { detail, target } = event; + const { expanded } = detail; + + if ((target as CDSTableHeaderRow) === this._tableHeaderRow) { + this._tableRows.forEach((e) => ((e as CDSTableRow).expanded = expanded)); + } + }; + + /** + * Handles sorting the table depending on the column selected + */ + @HostListener('eventBeforeSort') + // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to + private _handleSort = async (event: CustomEvent) => { + const { detail, target } = event; + const { sortDirection } = detail; + + if (!this.contains(target as any)) { + return; + } + + const rows = [...this._tableRows]; + const columns = [...this._tableHeaderRow.children]; + const columnIndex = columns.indexOf(target); + + columns.forEach( + (e) => e !== target && e.setAttribute('sort-direction', 'none') + ); + + // regular row sorting + rows.sort((a, b) => { + const cellA = a.querySelectorAll( + (this.constructor as typeof CDSTable).selectorTableRowCells + )[columnIndex].textContent; + const cellB = b.querySelectorAll( + (this.constructor as typeof CDSTable).selectorTableRowCells + )[columnIndex].textContent; + return ( + this.collationFactors[sortDirection] * + this.customSortRow(cellA, cellB, this.collator) + ); + }); + + // take into account the expanded rows, mapping each expandable row to its original for proper reinsertion + if (this.expandable) { + const originalRows = [...this._tableRows]; + const expandedRows = [...this._tableExpandedRows]; + + const mapping = originalRows.reduce((acc, element, index) => { + const sortId = element.getAttribute('sort-id'); + acc[sortId] = expandedRows[index]; + return acc; + }, {}); + + const sortedWithExpanded = [] as any; + + rows.forEach((e) => { + const sortId = e.getAttribute('sort-id'); + sortedWithExpanded.push(e); + sortedWithExpanded.push(mapping[sortId]); + }); + + sortedWithExpanded.forEach((e) => { + this._tableBody.insertBefore(e, null); + }); + } else { + rows.forEach((e) => { + this._tableBody.insertBefore(e, null); + }); + } + + const init = { + bubbles: true, + cancelable: true, + composed: true, + detail: { + sortedHeader: columns[columnIndex], + }, + }; + this.dispatchEvent( + new CustomEvent( + (this.constructor as typeof CDSTable).eventTableSorted, + init + ) + ); + + this._handleFilterRows(); + }; + + /** + * Handles search input within the toolbar actions + */ + @HostListener('eventSearchInput') + // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to + private _handleSearchInput = async (event: CustomEvent) => { + const { detail, target } = event; + + if (this.contains(target as CDSTableToolbarSearch)) { + const { value } = detail; + this._searchValue = value; + this._handleFilterRows(); + } + }; + + /** + * Handles row selection + */ + @HostListener('eventBeforeChangeSelection') + // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to + private _handleRowSelect = async (event: CustomEvent) => { + const { detail, target } = event; + const { selected } = detail; + const { + _tableBatchActions: tableBatchActions, + _tableToolbarContent: tableToolbarContent, + _tableHeaderRow: tableHeaderRow, + _selectedRows: selectedRows, + } = this; + + if (!this.contains(target as CDSTableRow)) { + return; + } + + if (this.radio) { + this._tableRows.forEach((e) => { + if (e !== target) { + e.removeAttribute('selected'); + e.shadowRoot!.querySelector(`${prefix}-radio-button`).checked = false; + } + }); + this._selectedRows.push(...[target as CDSTableRow]); + } else { + if (selectedRows.includes(target as CDSTableRow)) { + this._selectedRows = selectedRows.filter((e) => e !== target); + } else { + selectedRows.push(target as CDSTableRow); + } + + if (tableBatchActions) { + tableBatchActions.active = this._selectedRows?.length; + tableBatchActions.selectedRowsCount += selected ? 1 : -1; + } + + if (tableToolbarContent) { + tableToolbarContent.hasBatchActions = this._selectedRows.length; + } + } + + const totalRows = [...this._tableRows].filter( + (elem) => !elem.hasAttribute('filtered') + ).length; + + // selected header checkbox upon all rows being selected + const headerCheckbox = tableHeaderRow.shadowRoot + ?.querySelector(`${prefix}-checkbox`) + .shadowRoot.querySelector(`.${prefix}--checkbox`); + const allRowsSelected = this._selectedRows.length === totalRows; + headerCheckbox.checked = !this._selectedRows.length ? false : true; + headerCheckbox.indeterminate = + !allRowsSelected && this._selectedRows.length > 0; + + const init = { + bubbles: true, + cancelable: true, + composed: true, + detail: { + selectedRow: target, + selectedRows: selectedRows, + }, + }; + this.dispatchEvent( + new CustomEvent( + (this.constructor as typeof CDSTable).eventTableRowSelect, + init + ) + ); + }; + + /** + * Handles header row selection, selecting/unselecting all rows + */ + @HostListener('eventBeforeChangeSelectionAll') + // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to + private _handleAllRowsSelect = async (event: CustomEvent) => { + const { detail, target } = event; + const { selected } = detail; + const { + _tableBatchActions: tableBatchActions, + _tableToolbarContent: tableToolbarContent, + _tableRows: tableRows, + } = this; + + if (!this.contains(target as CDSTableRow)) { + return; + } + + let totalRows = 0; + forEach(tableRows, (elem) => { + if (!(elem as CDSTableRow).filtered) { + (elem as CDSTableRow).selected = selected; + this.radio + ? (( + (elem as CDSTableRow).shadowRoot!.querySelector( + `${prefix}-radio-button` + ) as CDSRadioButton + ).checked = selected) + : null; + this._selectedRows.push(elem as CDSTableRow); + totalRows++; + + const { selectorTableExpandedRows } = this + .constructor as typeof CDSTable; + const { nextElementSibling } = elem; + + // selecting the expanded row as well + if (nextElementSibling?.matches(selectorTableExpandedRows)) { + (elem.nextElementSibling as CDSTableExpandedRow).selected = selected; + } + } + }); + + if (!selected) { + this._selectedRows = []; + } + + if (tableBatchActions) { + tableBatchActions.selectedRowsCount = selected ? totalRows : 0; + tableBatchActions.active = selected; + } + + if (tableToolbarContent) { + tableToolbarContent.hasBatchActions = selected; + } + + const init = { + bubbles: true, + cancelable: true, + composed: true, + detail: { + selectedRows: this._selectedRows, + }, + }; + this.dispatchEvent( + new CustomEvent( + (this.constructor as typeof CDSTable).eventTableRowSelectAll, + init + ) + ); + }; + + /** + * Handles cancel button within the toolbar actions + */ + @HostListener('eventClickCancel') + // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to + private _handleCancelSelection = async (event: CustomEvent) => { + const { target } = event; + const { _tableHeaderRow: tableHeaderRow } = this; + + if (this.contains(target as CDSTableBatchActions)) { + tableHeaderRow.shadowRoot + ?.querySelector(`${prefix}-checkbox`) + .shadowRoot.querySelector(`.${prefix}--checkbox`) + .click(); + } + }; connectedCallback() { if (!this.hasAttribute('role')) { @@ -42,22 +574,337 @@ class BXTable extends LitElement { super.connectedCallback(); } + firstUpdated() { + this._tableBatchActions = this.querySelector( + (this.constructor as typeof CDSTable).selectorTableBatchActions + ); + this._tableToolbar = this.querySelector( + (this.constructor as typeof CDSTable).selectorTableToolbar + ); + this._tableToolbarContent = this.querySelector( + (this.constructor as typeof CDSTable).selectorTableToolbarContent + ); + this._tableBody = this.querySelector( + (this.constructor as typeof CDSTable).selectorTableBody + ); + this._tableHeaderRow = this.querySelector( + (this.constructor as typeof CDSTable).selectorRowsWithHeader + ); + this._tableExpandedRows = this.querySelectorAll( + (this.constructor as typeof CDSTable).selectorTableExpandedRows + ); + + this._tableRows = this.querySelectorAll( + (this.constructor as typeof CDSTable).selectorTableRow + ); + + this._downloadButton = this.querySelector( + (this.constructor as typeof CDSTable).selectorToolbarDownload + ); + if (this._downloadButton) { + this._downloadButton.onclick = this._handleDownload.bind(this); + } + this.headerCount = this._tableHeaderRow.children.length; + } + updated(changedProperties) { + if (changedProperties.has('expandable')) { + this._tableRows.forEach((e, index) => { + e.expandable = this.expandable; + e.setAttribute('sort-id', index); + }); + this._tableHeaderRow.expandable = this.expandable; + this._tableHeaderRow.batchExpansion = this.batchExpansion; + this.headerCount += this.expandable ? 1 : -1; + } + + if (changedProperties.has('headerCount')) { + this._tableExpandedRows.forEach((e) => { + e.setAttribute('colspan', this.headerCount); + }); + } + + if (changedProperties.has('isSelectable')) { + this.headerCount++; + } + + if (changedProperties.has('isSortable')) { + const headerCells = this.querySelectorAll( + (this.constructor as typeof CDSTable).selectorHeaderCell + ); + headerCells.forEach((e) => { + (e as CDSTableHeaderCell).isSortable = this.isSortable; + (e as CDSTableHeaderCell).removeAttribute('sort-direction'); + (e as CDSTableHeaderCell).isSelectable = this.isSelectable; + (e as CDSTableHeaderCell).isExpandable = this.expandable; + }); + } + + if (changedProperties.has('locale')) { + this.collator = new Intl.Collator(this.locale); + } + + if ( + changedProperties.has('overflowMenuOnHover') || + changedProperties.has('size') + ) { + forEach( + this.querySelectorAll( + (this.constructor as typeof CDSTable).selectorTableCellOverflowMenu + ), + (elem) => { + const cell = elem.parentNode as CDSTableCell; + const row = cell.parentNode as CDSTableRow; + cell.overflowMenuOnHover = this.overflowMenuOnHover; + row.overflowMenuOnHover = this.overflowMenuOnHover; + cell.setAttribute('size', this.size); + elem.setAttribute('size', this.size); + elem.setAttribute('data-table', ''); + } + ); + } + + if (changedProperties.has('radio')) { + // Propagate `size` attribute to descendants until `:host-context()` gets supported in all major browsers + forEach( + this.querySelectorAll( + (this.constructor as typeof CDSTable).selectorTableRow + ), + (elem) => { + (elem as CDSTableRow).radio = this.radio; + } + ); + } + if (changedProperties.has('size')) { // Propagate `size` attribute to descendants until `:host-context()` gets supported in all major browsers forEach( this.querySelectorAll( - (this.constructor as typeof BXTable).selectorRowsWithHeader + (this.constructor as typeof CDSTable).selectorAllRows ), (elem) => { elem.setAttribute('size', this.size); } ); + this._tableToolbar?.setAttribute('size', this.size); + } + + // TODO: Uncomment when Carbon fully implements Sticky header feature + // if (changedProperties.has('stickyHeader')) { + // const tableBody = this.querySelector( + // (this.constructor as typeof CDSTable).selectorTableBody + // ); + // const tableHead = this.querySelector( + // (this.constructor as typeof CDSTable).selectorTableHead + // ); + // (tableBody as any).stickyHeader = this.stickyHeader; + // (tableHead as any).stickyHeader = this.stickyHeader; + // forEach( + // this.querySelectorAll( + // (this.constructor as typeof CDSTable).selectorRowsWithHeader + // ), + // (elem) => { + // (elem as any).stickyHeader = this.stickyHeader; + // } + // ); + // forEach( + // this.querySelectorAll( + // (this.constructor as typeof CDSTable).selectorTableCells + // ), + // (elem) => { + // (elem as any).stickyHeader = this.stickyHeader; + // } + // ); + // } + + if (changedProperties.has('useZebraStyles')) { + const tableBody = this.querySelector( + (this.constructor as typeof CDSTable).selectorTableBody + ); + (tableBody as any).useZebraStyles = this.useZebraStyles; } } + /* eslint-disable no-constant-condition */ render() { - return html` `; + return html` +
+ + +
+ + + ${false // TODO: replace with this.stickyHeader when feature is fully implemented + ? html`
+
+ +
+
` + : html``} + `; + } + /* eslint-enable no-constant-condition */ + + /** + * The name of the custom event fired before a new sort direction is set upon a user gesture. + * Cancellation of this event stops the user-initiated change in sort direction. + */ + static get eventBeforeSort() { + return `${prefix}-table-header-cell-sort`; + } + + /** + * The name of the custom event fired during search bar input + */ + static get eventSearchInput() { + return `${prefix}-search-input`; + } + + /** + * The name of the custom event fired before header row is selected/unselected upon a user gesture. + */ + static get eventBeforeChangeSelectionAll() { + return `${prefix}-table-change-selection-all`; + } + + /** + * The name of the custom event fired before a row is selected/unselected upon a user gesture. + */ + static get eventBeforeChangeSelection() { + return `${prefix}-table-row-change-selection`; + } + + /** + * The name of the custom event fired after the Cancel button is clicked. + */ + static get eventClickCancel() { + return `${prefix}-table-batch-actions-cancel-clicked`; + } + + /** + * The name of the custom event fired after the expanded state a row is toggled upon a user gesture. + */ + static get eventExpandoToggle() { + return `${prefix}-table-row-expando-toggled`; + } + + /** + * The name of the custom event fired after a row has been selected + */ + static get eventTableRowSelect() { + return `${prefix}-table-row-selected`; + } + + /** + * The name of the custom event fired after all rows have been selected + */ + static get eventTableRowSelectAll() { + return `${prefix}-table-row-all-selected`; + } + + /** + * The name of the custom event fired after the table has been sorted + */ + static get eventTableSorted() { + return `${prefix}-table-sorted`; + } + + /** + * The name of the custom event fired after the table has been filtered containing remaining rows. + */ + static get eventTableFiltered() { + return `${prefix}-table-filtered`; + } + + /** + * The CSS selector to find the overflow menu on the table cell + */ + static get selectorTableCellOverflowMenu() { + return `${prefix}-table-cell ${prefix}-overflow-menu`; + } + + /** + * The CSS selector to find the download button + */ + static get selectorToolbarDownload() { + return `${prefix}-button[download]`; + } + + /** + * The CSS selector to find the table batch actions + */ + static get selectorTableBatchActions() { + return `${prefix}-table-batch-actions`; + } + + /** + * The CSS selector to find the table toolbar + */ + static get selectorTableToolbar() { + return `${prefix}-table-toolbar`; + } + + /** + * The CSS selector to find the table toolbar content + */ + static get selectorTableToolbarContent() { + return `${prefix}-table-toolbar-content`; + } + + /** + * The CSS selector to find the table toolbar search + */ + static get selectorTableToolbarSearch() { + return `${prefix}-table-toolbar-search`; + } + + /** + * The CSS selector to find the table head + */ + static get selectorTableHead() { + return `${prefix}-table-head`; + } + + /** + * The CSS selector to find the table body + */ + static get selectorTableBody() { + return `${prefix}-table-body`; + } + + /** + * The CSS selector to find the table expanded rows + */ + static get selectorTableExpandedRows() { + return `${prefix}-table-expanded-row`; + } + + /** + * The CSS selector to find the table rows + */ + static get selectorTableRow() { + return `${prefix}-table-row`; + } + + /** + * The CSS selector to find the rows cells. + */ + static get selectorTableRowCells() { + return `${prefix}-table-cell`; + } + + /** + * The CSS selector to find the rows cells, including header cells. + */ + static get selectorTableCells() { + return `${prefix}-table-cell, ${prefix}-table-header-cell`; + } + + /** + * The CSS selector to find the header cell + */ + static get selectorHeaderCell() { + return `${prefix}-table-header-cell`; } /** @@ -67,7 +914,14 @@ class BXTable extends LitElement { return `${prefix}-table-header-row,${prefix}-table-row`; } + /** + * The CSS selector to find all rows + */ + static get selectorAllRows() { + return `${prefix}-table-header-row,${prefix}-table-row,${prefix}-table-expanded-row`; + } + static styles = styles; } -export default BXTable; +export default CDSTable; diff --git a/web-components/packages/carbon-web-components/src/components/overflow-menu/overflow-menu.ts b/web-components/packages/carbon-web-components/src/components/overflow-menu/overflow-menu.ts index 3cda0a0f1652..af1253e4e282 100644 --- a/web-components/packages/carbon-web-components/src/components/overflow-menu/overflow-menu.ts +++ b/web-components/packages/carbon-web-components/src/components/overflow-menu/overflow-menu.ts @@ -75,12 +75,24 @@ class CDSOverflowMenu } }; + /** + * `true` if this tooltip is in a data table row + */ + @property({ type: Boolean, reflect: true, attribute: 'data-table' }) + dataTable = false; + /** * `true` if this overflow menu should be disabled. */ @property({ type: Boolean, reflect: true }) disabled = false; + /** + * `true` if this overflow menu body should be flipped. + */ + @property({ type: Boolean, reflect: true }) + flipped = false; + /** * `true` if the dropdown should be open. */ @@ -99,6 +111,12 @@ class CDSOverflowMenu @property({ reflect: true }) size = OVERFLOW_MENU_SIZE.MEDIUM; + /** + * `true` if this menu is a toolbar action + */ + @property({ type: Boolean, attribute: 'toolbar-action', reflect: true }) + toolbarAction = false; + /** * @returns The position of the trigger button in the viewport. */ @@ -143,6 +161,20 @@ class CDSOverflowMenu this.setAttribute('aria-expanded', String(Boolean(open))); } } + + if (changedProperties.has('dataTable')) { + const tooltip = this.shadowRoot?.querySelector(`${prefix}-tooltip`); + tooltip?.setAttribute('data-table', ''); + } + + if (changedProperties.has('flipped')) { + ( + this.querySelector( + `${prefix}-overflow-menu-body` + ) as CDSOverflowMenuBody + ).flipped = true; + } + if (changedProperties.has('size')) { const { size } = this; const { _menuBody: menuBody } = this; @@ -156,6 +188,15 @@ class CDSOverflowMenu } }); button?.classList.add(`${prefix}--overflow-menu--${this.size}`); + + const tooltip = this.shadowRoot?.querySelector(`${prefix}-tooltip`); + tooltip?.setAttribute('size', this.size); + } + + if (changedProperties.has('toolbarAction') && this.toolbarAction) { + this.shadowRoot + ?.querySelector(`${prefix}-tooltip`) + ?.setAttribute('toolbar-action', ''); } super.updated(changedProperties); diff --git a/web-components/packages/carbon-web-components/src/components/radio-button/radio-button.scss b/web-components/packages/carbon-web-components/src/components/radio-button/radio-button.scss index 8288b603b751..31ad1a8e5a08 100644 --- a/web-components/packages/carbon-web-components/src/components/radio-button/radio-button.scss +++ b/web-components/packages/carbon-web-components/src/components/radio-button/radio-button.scss @@ -10,7 +10,7 @@ $css--plex: true !default; @use '@carbon/styles/scss/config' as *; @use '@carbon/styles/scss/theme' as *; @use '@carbon/styles/scss/spacing' as *; -@use '@carbon/styles/scss/utilities'; +@use '@carbon/styles/scss/utilities' as *; @use '@carbon/styles/scss/components/form'; @use '@carbon/styles/scss/components/radio-button/radio-button' as *; @@ -39,6 +39,16 @@ $css--plex: true !default; } } +:host(#{$prefix}-radio-button[data-table]) { + .#{$prefix}--radio-button__label { + width: rem(28px); + } + + .#{$prefix}--radio-button__appearance { + margin-right: -#{$spacing-01}; + } +} + :host(#{$prefix}-radio-button[readOnly]) { @extend .#{$prefix}--radio-button-group--readonly; } diff --git a/web-components/packages/carbon-web-components/src/components/radio-button/radio-button.ts b/web-components/packages/carbon-web-components/src/components/radio-button/radio-button.ts index d3506a6438e0..390dc784c66a 100644 --- a/web-components/packages/carbon-web-components/src/components/radio-button/radio-button.ts +++ b/web-components/packages/carbon-web-components/src/components/radio-button/radio-button.ts @@ -134,6 +134,18 @@ class CDSRadioButton extends HostListenerMixin(FocusMixin(LitElement)) { if (this._manager) { this._manager.select(radioButtonDelegate, this.readOnly); } + this.dispatchEvent( + new CustomEvent( + (this.constructor as typeof CDSRadioButton).eventChange, + { + bubbles: true, + composed: true, + detail: { + checked: this.checked, + }, + } + ) + ); } }; @@ -169,6 +181,12 @@ class CDSRadioButton extends HostListenerMixin(FocusMixin(LitElement)) { @property({ type: Boolean, reflect: true }) checked = false; + /** + * `true` if the radio button is used in a data table + */ + @property({ type: Boolean, reflect: true, attribute: 'data-table' }) + dataTable = false; + /** * `true` if the radio button item should be disabled. */ diff --git a/web-components/packages/carbon-web-components/src/components/tooltip/tooltip.scss b/web-components/packages/carbon-web-components/src/components/tooltip/tooltip.scss index 29b855c1d967..6bf2dd10a940 100644 --- a/web-components/packages/carbon-web-components/src/components/tooltip/tooltip.scss +++ b/web-components/packages/carbon-web-components/src/components/tooltip/tooltip.scss @@ -10,6 +10,32 @@ @use '@carbon/styles/scss/config' as *; @use '../popover/popover'; @use '@carbon/styles/scss/components/tooltip'; +@use '@carbon/styles/scss/theme' as *; + +// For some reason `#{prefix}-tooltip` doesn't work here, will need to investigate + +:host(cds-tooltip[data-table]) { + display: contents; + + &:hover { + ::slotted(button) { + background-color: $layer-selected-hover !important; + } + } +} + +:host(cds-tooltip[data-table][size='xs']), +:host(cds-tooltip[data-table][size='sm']) { + ::slotted(button) { + height: calc(100% + 1px) !important; + } +} + +:host(cds-tooltip[toolbar-action]) { + ::slotted(button) { + outline: none !important; + } +} :host(#{prefix}-tooltip-content) { ::slotted(.#{$prefix}-tooltip-content) { diff --git a/web-components/packages/carbon-web-components/src/components/tooltip/tooltip.ts b/web-components/packages/carbon-web-components/src/components/tooltip/tooltip.ts index d9949c5904ce..a79469eef809 100644 --- a/web-components/packages/carbon-web-components/src/components/tooltip/tooltip.ts +++ b/web-components/packages/carbon-web-components/src/components/tooltip/tooltip.ts @@ -30,6 +30,12 @@ class CDSTooltip extends HostListenerMixin(CDSPopover) { @property({ reflect: true, type: String }) align = 'top'; + /** + * `true` if this tooltip is in a data table row + */ + @property({ type: Boolean, reflect: true, attribute: 'data-table' }) + dataTable = false; + /** * Specify whether the tooltip should be closed when clicked */ @@ -54,6 +60,18 @@ class CDSTooltip extends HostListenerMixin(CDSPopover) { @property({ attribute: 'leave-delay-ms', type: Number }) leaveDelayMs = 300; + /** + * Specify the size of the tooltip + */ + @property({ reflect: true }) + size = false; + + /** + * Specify whether the tooltip should be open when it first renders + */ + @property({ reflect: true, attribute: 'toolbar-action', type: Boolean }) + toolbarAction = false; + /** * Handles `mouseover` event on this element. */ @@ -96,7 +114,6 @@ class CDSTooltip extends HostListenerMixin(CDSPopover) { /** * Handles `keydown` event on this element. * Space & enter will toggle state, Escape will only close. - */ @HostListener('click') // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to @@ -115,6 +132,9 @@ class CDSTooltip extends HostListenerMixin(CDSPopover) { .filter( (node) => node.nodeType !== Node.TEXT_NODE || node!.textContent!.trim() ); + if (!component[0]) { + return; + } (component[0] as HTMLElement).addEventListener('focus', this._handleHover); (component[0] as HTMLElement).addEventListener( 'focusout', diff --git a/web-components/packages/carbon-web-components/src/index.ts b/web-components/packages/carbon-web-components/src/index.ts index 9c234f0c1155..eb2fc8852a1c 100644 --- a/web-components/packages/carbon-web-components/src/index.ts +++ b/web-components/packages/carbon-web-components/src/index.ts @@ -31,10 +31,14 @@ export { default as CDSTable } from './components/data-table/table'; export { default as CDSTableBatchActions } from './components/data-table/table-batch-actions'; export { default as CDSTableBody } from './components/data-table/table-body'; export { default as CDSTableCell } from './components/data-table/table-cell'; +export { default as CDSTableCellContent } from './components/data-table/table-cell-content'; export { default as CDSTableHead } from './components/data-table/table-head'; +export { default as CDSTableHeaderTitle } from './components/data-table/table-header-title'; +export { default as CDSTableHeadDescription } from './components/data-table/table-header-description'; export { default as CDSTableHeaderCell } from './components/data-table/table-header-cell'; export { default as CDSTableHeaderRow } from './components/data-table/table-header-row'; export { default as CDSTableRow } from './components/data-table/table-row'; +export { default as CDSTableSkeleton } from './components/data-table/table-skeleton'; export { default as CDSTableToolbarContent } from './components/data-table/table-toolbar-content'; export { default as CDSTableToolbarSearch } from './components/data-table/table-toolbar-search'; export { default as CDSDatePicker } from './components/date-picker/date-picker'; diff --git a/web-components/packages/carbon-web-components/tests/spec/data-table_spec.ts b/web-components/packages/carbon-web-components/tests/spec/data-table_spec.ts index bb63c526a811..9821f144e569 100644 --- a/web-components/packages/carbon-web-components/tests/spec/data-table_spec.ts +++ b/web-components/packages/carbon-web-components/tests/spec/data-table_spec.ts @@ -11,21 +11,18 @@ import { html, render } from 'lit'; import EventManager from '../utils/event-manager'; import { ifDefined } from 'lit/directives/if-defined.js'; import { INPUT_SIZE } from '../../src/components/text-input/text-input'; -import { TABLE_COLOR_SCHEME } from '../../src/components/data-table/table'; -import BXTableHeaderCell, { +import CDSTableHeaderCell, { TABLE_SORT_CYCLE, TABLE_SORT_DIRECTION, } from '../../src/components/data-table/table-header-cell'; -import BXTableRow from '../../src/components/data-table/table-row'; -import BXTableExpandRow from '../../src/components/data-table/table-expand-row'; -import BXTableExpandedRow from '../../src/components/data-table/table-expanded-row'; -import BXTableToolbarSearch from '../../src/components/data-table/table-toolbar-search'; -import { Default } from '../../src/components/data-table/data-table-story'; - -const template = ({ colorScheme = TABLE_COLOR_SCHEME.REGULAR, ...rest } = {}) => - Default({ +import CDSTableRow from '../../src/components/data-table/table-row'; +import CDSTableExpandedRow from '../../src/components/data-table/table-expanded-row'; +import CDSTableToolbarSearch from '../../src/components/data-table/table-toolbar-search'; +import { Playground } from '../../src/components/data-table/stories/data-table-basic-story'; + +const template = ({ ...rest } = {}) => + Playground({ 'cds-table': { ...rest }, - 'cds-table-body': { colorScheme }, }); const headerCellTemplate = (props?) => { @@ -63,13 +60,13 @@ const expandRowTemplate = (props?) => { selectionValue, } = props ?? {}; return html` - + selection-value="${ifDefined(selectionValue)}"> `; }; @@ -158,16 +155,13 @@ describe('data-table', function () { describe('cds-table-body', function () { it('should support setting zebra stripe to rows', async function () { - render( - template({ colorScheme: TABLE_COLOR_SCHEME.ZEBRA }), - document.body - ); + render(template(), document.body); await Promise.resolve(); const result = Array.prototype.every.call( document.body.querySelectorAll('cds-table-row'), (item, i) => - (item as BXTableRow).even === ((i + 1) % 2 === 0) && - (item as BXTableRow).odd === ((i + 1) % 2 !== 0) + (item as CDSTableRow).even === ((i + 1) % 2 === 0) && + (item as CDSTableRow).odd === ((i + 1) % 2 !== 0) ); expect(result).toBe(true); }); @@ -178,8 +172,8 @@ describe('data-table', function () { const result = Array.prototype.every.call( document.body.querySelectorAll('cds-table-row'), (item) => - (item as BXTableRow).even === false && - (item as BXTableRow).odd === false + (item as CDSTableRow).even === false && + (item as CDSTableRow).odd === false ); expect(result).toBe(true); }); @@ -224,7 +218,7 @@ describe('data-table', function () { await Promise.resolve(); const elem = document.body.querySelector( 'cds-table-header-cell' - ) as BXTableHeaderCell; + ) as CDSTableHeaderCell; const button = elem.shadowRoot!.querySelector( '.cds--table-sort' ) as HTMLButtonElement; @@ -251,7 +245,7 @@ describe('data-table', function () { await Promise.resolve(); const elem = document.body.querySelector( 'cds-table-header-cell' - ) as BXTableHeaderCell; + ) as CDSTableHeaderCell; const button = elem.shadowRoot!.querySelector( '.cds--table-sort' ) as HTMLButtonElement; @@ -278,7 +272,7 @@ describe('data-table', function () { await Promise.resolve(); const elem = document.body.querySelector( 'cds-table-header-cell' - ) as BXTableHeaderCell; + ) as CDSTableHeaderCell; const button = elem.shadowRoot!.querySelector( '.cds--table-sort' ) as HTMLButtonElement; @@ -305,7 +299,7 @@ describe('data-table', function () { await Promise.resolve(); const elem = document.body.querySelector( 'cds-table-header-cell' - ) as BXTableHeaderCell; + ) as CDSTableHeaderCell; const button = elem.shadowRoot!.querySelector( '.cds--table-sort' ) as HTMLButtonElement; @@ -332,7 +326,7 @@ describe('data-table', function () { await Promise.resolve(); const elem = document.body.querySelector( 'cds-table-header-cell' - ) as BXTableHeaderCell; + ) as CDSTableHeaderCell; events.on(elem, 'cds-table-header-cell-sort', (event) => { event.preventDefault(); }); @@ -394,7 +388,7 @@ describe('data-table', function () { expect(spyBeforeChange).toHaveBeenCalled(); expect(spyBeforeChange.calls.argsFor(0)[0].detail.selected).toBe(true); await Promise.resolve(); - expect((row as BXTableRow).selected).toBe(true); + expect((row as CDSTableRow).selected).toBe(true); }); it('should fire cds-table-row-change-selection event upon unselecting', async function () { @@ -413,7 +407,7 @@ describe('data-table', function () { expect(spyBeforeChange).toHaveBeenCalled(); expect(spyBeforeChange.calls.argsFor(0)[0].detail.selected).toBe(false); await Promise.resolve(); - expect((row as BXTableRow).selected).toBe(false); + expect((row as CDSTableRow).selected).toBe(false); }); it('should support preventing table row selection from being toggled upon user gesture', async function () { @@ -430,18 +424,18 @@ describe('data-table', function () { }); row!.shadowRoot!.querySelector('input')!.click(); await Promise.resolve(); - expect((row as BXTableRow).selected).toBe(false); + expect((row as CDSTableRow).selected).toBe(false); }); }); }); - describe('cds-table-expand-row', function () { + describe('cds-table-row', function () { describe('Misc attributes', function () { it('should render with minimum attributes', async function () { render(expandRowTemplate(), document.body); await Promise.resolve(); expect( - document.body.querySelector('cds-table-expand-row' as any) + document.body.querySelector('cds-table-row' as any) ).toMatchSnapshot({ mode: 'shadow' }); }); @@ -459,7 +453,7 @@ describe('data-table', function () { ); await Promise.resolve(); expect( - document.body.querySelector('cds-table-expand-row' as any) + document.body.querySelector('cds-table-row' as any) ).toMatchSnapshot({ mode: 'shadow' }); }); }); @@ -469,22 +463,22 @@ describe('data-table', function () { render(expandRowTemplate(), document.body); await Promise.resolve(); - const expandRow = document.body.querySelector('cds-table-expand-row'); + const expandRow = document.body.querySelector('cds-table-row'); const expandedRow = document.body.querySelector( 'cds-table-expanded-row' ); expandRow!.shadowRoot!.querySelector('button')!.click(); await Promise.resolve(); - expect((expandRow as BXTableExpandRow).expanded).toBe(true); + expect((expandRow as CDSTableRow).expanded).toBe(true); await Promise.resolve(); - expect((expandedRow as BXTableExpandedRow).expanded).toBe(true); + expect((expandedRow as CDSTableExpandedRow).expanded).toBe(true); expandRow!.shadowRoot!.querySelector('button')!.click(); await Promise.resolve(); - expect((expandRow as BXTableExpandRow).expanded).toBe(false); + expect((expandRow as CDSTableRow).expanded).toBe(false); await Promise.resolve(); - expect((expandedRow as BXTableExpandedRow).expanded).toBe(false); + expect((expandedRow as CDSTableExpandedRow).expanded).toBe(false); }); it('should fire cds-table-row-expando-beingtoggled/cds-table-row-expando-toggled events upon expanding', async function () { @@ -492,7 +486,7 @@ describe('data-table', function () { const spyAfterToggle = jasmine.createSpy('after toggle'); render(expandRowTemplate(), document.body); await Promise.resolve(); - const expandRow = document.body.querySelector('cds-table-expand-row'); + const expandRow = document.body.querySelector('cds-table-row'); events.on( expandRow!, 'cds-table-row-expando-beingtoggled', @@ -512,7 +506,7 @@ describe('data-table', function () { const spyAfterToggle = jasmine.createSpy('after toggle'); render(expandRowTemplate({ expanded: true }), document.body); await Promise.resolve(); - const expandRow = document.body.querySelector('cds-table-expand-row'); + const expandRow = document.body.querySelector('cds-table-row'); events.on( expandRow!, 'cds-table-row-expando-beingtoggled', @@ -531,7 +525,7 @@ describe('data-table', function () { const spyAfterToggle = jasmine.createSpy('after toggle'); render(expandRowTemplate(), document.body); await Promise.resolve(); - const expandRow = document.body.querySelector('cds-table-expand-row'); + const expandRow = document.body.querySelector('cds-table-row'); events.on(expandRow!, 'cds-table-row-expando-beingtoggled', (event) => { event.preventDefault(); }); @@ -547,7 +541,7 @@ describe('data-table', function () { render(expandRowTemplate(), document.body); await Promise.resolve(); - const expandRow = document.body.querySelector('cds-table-expand-row'); + const expandRow = document.body.querySelector('cds-table-row'); const expandedRow = document.body.querySelector( 'cds-table-expanded-row' ); @@ -555,12 +549,12 @@ describe('data-table', function () { expandRow!.dispatchEvent( new CustomEvent('mouseover', { bubbles: true }) ); - expect((expandedRow as BXTableExpandedRow).highlighted).toBe(true); + expect((expandedRow as CDSTableExpandedRow).highlighted).toBe(true); expandRow!.dispatchEvent( new CustomEvent('mouseout', { bubbles: true }) ); - expect((expandedRow as BXTableExpandedRow).highlighted).toBe(false); + expect((expandedRow as CDSTableExpandedRow).highlighted).toBe(false); }); }); }); @@ -602,7 +596,7 @@ describe('data-table', function () { toolbarSearch!.dispatchEvent( new CustomEvent('focusin', { bubbles: true }) ); - expect((toolbarSearch as BXTableToolbarSearch).expanded).toBe(true); + expect((toolbarSearch as CDSTableToolbarSearch).expanded).toBe(true); await Promise.resolve(); await Promise.resolve(); expect(input!.focus).toHaveBeenCalled(); @@ -617,7 +611,7 @@ describe('data-table', function () { toolbarSearch!.dispatchEvent( new CustomEvent('focusout', { bubbles: true }) ); - expect((toolbarSearch as BXTableToolbarSearch).expanded).toBe(false); + expect((toolbarSearch as CDSTableToolbarSearch).expanded).toBe(false); }); }); });