diff --git a/src/components/table/bl-table.stories.mdx b/src/components/table/bl-table.stories.mdx index ed1b78ee..9cec7b42 100644 --- a/src/components/table/bl-table.stories.mdx +++ b/src/components/table/bl-table.stories.mdx @@ -1,6 +1,6 @@ import {html} from 'lit'; import {Meta, Canvas, ArgsTable, Story} from '@storybook/addon-docs'; -import {userEvent} from '@storybook/testing-library'; +import { UPDATE_STORY_ARGS } from "@storybook/core-events"; -export const TableTemplate = (args) => html` -
- - - - ${args.headers.map((header) => - html` - ${header.text} - ` - )} - - - - ${args.data.map((row) => - html` - - ${args.headers.map((header, col_idx) => - html` - - ${row[header.key]} - - ` - )} - - ` - )} - - -
-`; +export const TableTemplate = (args, {id}) => { + const updateArgs = (_args)=>{ + window.__STORYBOOK_ADDONS_CHANNEL__.emit(UPDATE_STORY_ARGS, { + storyId: id, + updatedArgs: _args, + }); + } + let {data, headers, selectValue} = args; + const onSort = (event) => { + const [sortKey, sortDirection] = event.detail; + console.log("onSort", event.detail); + updateArgs({ + ...args, + data: data.sort((a, b) => { + return a[sortKey].toString().localeCompare(b[sortKey], 'tr', {sensitivity: 'base'}) * (sortDirection === 'asc' ? -1 : 1); + }), + }); -export const focusSecondOption = async ({}) => { - await userEvent.tab(); - await userEvent.keyboard('{ArrowRight}'); + console.log(data) + } + const onRowSelect = (event) => { + console.log("onRowSelect", event.detail); + updateArgs({ + ...args, + selectValue: event.detail, + }); + } + + return html` +
+ + + + ${args.headers.map((header) => + html` + + ${header.text} + ` + )} + + + + ${data.map((row) => + html` + + ${headers.map((header, col_idx) => + html` + + ${row[header.key]} + + ` + )} + + ` + )} + + +
+ `; } # Table Component @@ -81,16 +107,16 @@ If you set a list of `values`, options with those values will be selected. {key: "gender", text: "Gender", width: '85px'}, {key: "ip_address", text: "IP Address", width: '90px', sticky: 'right'}, ], - data: Array(1) - .fill([ - { - id: 1, - first_name: "Antonella", - last_name: "Bellefonte", - email: "abellefonte0@nba.com", - gender: "Female", - ip_address: "193.108.174.118" - }, + selectValue: ["row-1","row-4"], + data: [ + { + id: 1, + first_name: "Antonella", + last_name: "Bellefonte", + email: "abellefonte0@nba.com", + gender: "Female", + ip_address: "193.108.174.118" + }, {id:2,first_name:"Wash",last_name:"Carnson",email:"wcarnson1@jalbum.net",gender:"Male",ip_address:"255.169.128.60"}, {id:3,first_name:"Betteanne",last_name:"Cowgill",email:"bcowgill2@timesonline.co.uk",gender:"Female",ip_address:"235.237.3.233", disabled: true}, {id:4,first_name:"Lilith",last_name:"Astbury",email:"lastbury3@shinystat.com",gender:"Female",ip_address:"46.41.60.65"}, @@ -110,8 +136,7 @@ If you set a list of `values`, options with those values will be selected. {id:18,first_name:"Germana",last_name:"Downing",email:"gdowningh@dion.ne.jp",gender:"Female",ip_address:"55.83.134.58"}, {id:19,first_name:"Kizzie",last_name:"Pyzer",email:"kpyzeri@google.com.br",gender:"Female",ip_address:"84.92.93.248"}, {id:20,first_name:"Almire",last_name:"Slinn",email:"aslinnj@wiley.com",gender:"Female",ip_address:"195.56.102.224"}, - ]) - .reduce((a, b)=>a.concat(b), []), + ], stickyHeader:true }}> {TableTemplate.bind({})} diff --git a/src/components/table/bl-table.test.ts b/src/components/table/bl-table.test.ts index d115dc6e..7b135352 100644 --- a/src/components/table/bl-table.test.ts +++ b/src/components/table/bl-table.test.ts @@ -346,7 +346,7 @@ describe("bl-table", () => { //when const el = await fixture( html` - + @@ -370,7 +370,7 @@ describe("bl-table", () => { - + 1 @@ -881,7 +881,7 @@ describe("bl-table", () => { - + 1 @@ -901,7 +901,7 @@ describe("bl-table", () => { 193.108.174.118 - + 2 @@ -939,14 +939,17 @@ describe("bl-table", () => { const ev = await oneEvent(el, "bl-table-row-select"); expect(ev).to.exist; - expect(ev.detail).to.be.deep.equal([0,1]); + expect(ev.detail).to.be.deep.equal([ + "row-1", + "row-2", + ]); }); it("should fire bl-table-row-select event when user unchecked on checkbox in header row", async () => { const el = await fixture( html` @@ -973,7 +976,7 @@ describe("bl-table", () => { - + 1 @@ -993,7 +996,7 @@ describe("bl-table", () => { 193.108.174.118 - + 2 @@ -1064,7 +1067,7 @@ describe("bl-table", () => { - + 1 @@ -1084,7 +1087,7 @@ describe("bl-table", () => { 193.108.174.118 - + 2 @@ -1122,14 +1125,14 @@ describe("bl-table", () => { const ev = await oneEvent(el, "bl-table-row-select"); expect(ev).to.exist; - expect(ev.detail).to.be.deep.equal([0]); + expect(ev.detail).to.be.deep.equal(["row-1"]); }); it("should fire bl-table-row-select event when user unchecked on checkbox in first table row", async () => { const el = await fixture( html` @@ -1156,7 +1159,7 @@ describe("bl-table", () => { - + 1 @@ -1176,7 +1179,7 @@ describe("bl-table", () => { 193.108.174.118 - + 2 @@ -1214,7 +1217,7 @@ describe("bl-table", () => { const ev = await oneEvent(el, "bl-table-row-select"); expect(ev).to.exist; - expect(ev.detail).to.be.deep.equal([1]); + expect(ev.detail).to.be.deep.equal(["row-2"]); }); }); }); diff --git a/src/components/table/bl-table.ts b/src/components/table/bl-table.ts index 1d3aeaae..31992983 100644 --- a/src/components/table/bl-table.ts +++ b/src/components/table/bl-table.ts @@ -24,14 +24,21 @@ export default class BlTable extends LitElement { } /** - * Selected table row indexes + * Selected table row selection key list */ @property({ type: Array, reflect: true, attribute: "select-value" }) - get selectValue(): number[] { + get selectValue(): string[] { return this._selectValue; } - set selectValue(value: number[]) { + set selectValue(value: string[]) { this._selectValue = value; + this.updateComplete.then(() => { + Array.from(this.querySelectorAll("bl-table-header-cell,bl-table-cell,bl-table-row")).map( + com => { + (com as BlTableHeaderCell | BlTableCell | BlTableRow).requestUpdate(); + } + ); + }); } /** @@ -92,9 +99,9 @@ export default class BlTable extends LitElement { /** * Fires when selected table rows changed */ - @event("bl-table-row-select") private onRowSelect: EventDispatcher; + @event("bl-table-row-select") private onRowSelect: EventDispatcher; - @state() private _selectValue: number[] = []; + @state() private _selectValue: string[] = []; @state() private _sortKey: string = ""; @@ -124,14 +131,14 @@ export default class BlTable extends LitElement { return isHeaderCell ? this.multiple && this.selectable : this.selectable; } - isRowSelected(index: number) { - return this.selectValue.includes(index); + isRowSelected(selectionKey: string) { + return this.selectValue.includes(selectionKey); } isAllSelected() { return Array.from(this.tableRows) .filter(tr => !(tr as BlTableRow).disabled) - .every(tr => this.selectValue.includes((tr as BlTableRow).index)); + .every(tr => this.selectValue.includes((tr as BlTableRow).selectionKey)); } isAnySelected() { @@ -139,35 +146,28 @@ export default class BlTable extends LitElement { !this.isAllSelected() && Array.from(this.tableRows) .filter(tr => !(tr as BlTableRow).disabled) - .some(tr => this.selectValue.includes((tr as BlTableRow).index)) + .some(tr => this.selectValue.includes((tr as BlTableRow).selectionKey)) ); } - onSelectionChange(index: number, selected: boolean, isHeader = false) { + onSelectionChange(isHeader = false, selected: boolean, selectionKey: string) { if (isHeader) { if (selected) { this.selectValue = Array.from(this.tableRows) .filter(tr => !(tr as BlTableRow).disabled) - .map(tr => (tr as BlTableRow).index); + .map(tr => (tr as BlTableRow).selectionKey); } else { this.selectValue = []; } } else { - if (this.selectValue.includes(index) && !selected) { - this.selectValue = this.selectValue.filter(v => v !== index); - } else if (!this.selectValue.includes(index) && selected) { - this.selectValue = [...this.selectValue, index]; + if (this.selectValue.includes(selectionKey) && !selected) { + this.selectValue = this.selectValue.filter(v => v !== selectionKey); + } else if (!this.selectValue.includes(selectionKey) && selected) { + this.selectValue = [...this.selectValue, selectionKey]; } } this.onRowSelect(this.selectValue); - this.updateComplete.then(() => { - Array.from(this.querySelectorAll("bl-table-header-cell,bl-table-cell,bl-table-row")).map( - com => { - (com as BlTableHeaderCell | BlTableCell | BlTableRow).requestUpdate(); - } - ); - }); } onSortChange(sortKey: string, sortDirection: string) { this._sortKey = sortKey; diff --git a/src/components/table/table-cell/bl-table-cell.css b/src/components/table/table-cell/bl-table-cell.css index 545a560d..080cd136 100644 --- a/src/components/table/table-cell/bl-table-cell.css +++ b/src/components/table/table-cell/bl-table-cell.css @@ -28,6 +28,18 @@ box-shadow: 8px 0 16px 0 rgb(39 49 66 / 10%);; } +.table-cell.shadow-left::before{ + content: ''; + position: absolute; + left: -1px; + top: 0; + width: 16px; + height: 100%; + z-index: -1; + border-left: 1px solid var(--bl-color-neutral-lighter); + box-shadow: -8px 0 16px 0 rgb(39 49 66 / 10%); +} + bl-checkbox { margin-right: var(--bl-size-m); } diff --git a/src/components/table/table-cell/bl-table-cell.test.ts b/src/components/table/table-cell/bl-table-cell.test.ts index 3463e49c..ef10f44e 100644 --- a/src/components/table/table-cell/bl-table-cell.test.ts +++ b/src/components/table/table-cell/bl-table-cell.test.ts @@ -9,7 +9,7 @@ describe("bl-table-cell", () => { //then expect(el).instanceOf(BlTableCell); expect(el.index).to.equal(-1); - expect(el.rowIndex).to.equal(-1); + expect(el.selectionKey).to.equal(""); }); it("should be rendered with default values", async () => { diff --git a/src/components/table/table-cell/bl-table-cell.ts b/src/components/table/table-cell/bl-table-cell.ts index 7626b5a7..93f4903c 100644 --- a/src/components/table/table-cell/bl-table-cell.ts +++ b/src/components/table/table-cell/bl-table-cell.ts @@ -33,7 +33,7 @@ export default class BlTableCell extends LitElement { return this.disableSelection; } get selectable() { - return this.index === 0 && !!this._table?.isSelectable(false); + return this.index === 0 && !!this._table?.isSelectable(false) && this.selectionKey; } get index() { const parent = this.parentNode; @@ -43,8 +43,8 @@ export default class BlTableCell extends LitElement { } return [...parent.children].indexOf(this); } - get rowIndex(): number { - return this._tableRow ? this._tableRow.index : -1; + get selectionKey(): string { + return this._tableRow ? this._tableRow.selectionKey : ""; } get checked() { return !!this._tableRow?.checked; @@ -68,7 +68,7 @@ export default class BlTableCell extends LitElement { } onChange(event: CustomEvent) { - this._table?.onSelectionChange(this.rowIndex, event.detail); + this._table?.onSelectionChange(false, event.detail, this.selectionKey); } private _renderCheckbox() { @@ -85,7 +85,6 @@ export default class BlTableCell extends LitElement { render(): TemplateResult { const className = this.shadowRight ? "shadow-right" : this.shadowLeft ? "shadow-left" : ""; - // disable text color —bl-color-neutral-light return html`
${this._renderCheckbox()} diff --git a/src/components/table/table-header-cell/bl-table-header-cell.css b/src/components/table/table-header-cell/bl-table-header-cell.css index 702e7243..574c5769 100644 --- a/src/components/table/table-header-cell/bl-table-header-cell.css +++ b/src/components/table/table-header-cell/bl-table-header-cell.css @@ -4,7 +4,7 @@ display: table-cell; border: 1px solid var(--bl-color-neutral-lighter); - background-color: var(--bl-color-tertiary-background); + background-color: var(--bl-color-neutral-lightest); padding: var(--bl-size-m); font: var(--bl-font-title-3-medium); color: var(--bl-color-neutral-darker); @@ -32,6 +32,18 @@ box-shadow: 8px 0 16px 0 rgb(39 49 66 / 10%);; } +.table-header-cell.shadow-left::before{ + content: ''; + position: absolute; + left: -1px; + top: 0; + width: 16px; + height: 100%; + z-index: -1; + border-left: 1px solid var(--bl-color-neutral-lighter); + box-shadow: -8px 0 16px 0 rgb(39 49 66 / 10%); +} + bl-checkbox { margin-right: var(--bl-size-m); } diff --git a/src/components/table/table-header-cell/bl-table-header-cell.ts b/src/components/table/table-header-cell/bl-table-header-cell.ts index 2aa37949..e36890cd 100644 --- a/src/components/table/table-header-cell/bl-table-header-cell.ts +++ b/src/components/table/table-header-cell/bl-table-header-cell.ts @@ -90,7 +90,7 @@ export default class BlTableHeaderCell extends LitElement { } onChange(event: CustomEvent) { - this._table?.onSelectionChange(this.index, event.detail, true); + this._table?.onSelectionChange(true, event.detail, ""); } onSort() { diff --git a/src/components/table/table-row/bl-table-row.css b/src/components/table/table-row/bl-table-row.css index fb7b411f..e8ce60d1 100644 --- a/src/components/table/table-row/bl-table-row.css +++ b/src/components/table/table-row/bl-table-row.css @@ -10,6 +10,7 @@ :host([disabled]), :host([disabled]) ::slotted(bl-table-cell) { background-color: var(--bl-color-neutral-lightest); + color: var(--bl-color-neutral-light); } :host(:not([checked], [disabled]):hover), diff --git a/src/components/table/table-row/bl-table-row.test.ts b/src/components/table/table-row/bl-table-row.test.ts index 13e07160..c80869ae 100644 --- a/src/components/table/table-row/bl-table-row.test.ts +++ b/src/components/table/table-row/bl-table-row.test.ts @@ -8,7 +8,6 @@ describe("bl-table-row", () => { //then expect(el).instanceOf(BlTableRow); - expect(el.index).to.equal(-1); }); it("should be rendered with default values", async () => { diff --git a/src/components/table/table-row/bl-table-row.ts b/src/components/table/table-row/bl-table-row.ts index de5e2fd2..e59af3dc 100644 --- a/src/components/table/table-row/bl-table-row.ts +++ b/src/components/table/table-row/bl-table-row.ts @@ -1,10 +1,12 @@ -import { html, LitElement, TemplateResult } from "lit"; -import { customElement } from "lit/decorators.js"; +import { html, LitElement, PropertyValues, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators.js"; import { CSSResultGroup } from "lit/development"; import "element-internals-polyfill"; import "../../checkbox-group/checkbox/bl-checkbox"; import { blTableBodyTag } from "../table-body/bl-table-body"; import type BlTableBody from "../table-body/bl-table-body"; +import BlTableCell from "../table-cell/bl-table-cell"; +import BlTableHeaderCell from "../table-header-cell/bl-table-header-cell"; import { blTableHeaderTag } from "../table-header/bl-table-header"; import type BlTableHeader from "../table-header/bl-table-header"; import style from "../table-row/bl-table-row.css"; @@ -21,6 +23,12 @@ export default class BlTableRow extends LitElement { return [style]; } + /** + * selection key for table row + */ + @property({ type: String, reflect: true, attribute: "selection-key" }) + selectionKey: string = ""; + connectedCallback(): void { super.connectedCallback(); if ( @@ -38,7 +46,8 @@ export default class BlTableRow extends LitElement { super.disconnectedCallback(); } - updated() { + updated(_changedProperties: PropertyValues) { + super.updated(_changedProperties); this.removeAttribute("checked"); this.removeAttribute("disabled"); this.removeAttribute("sticky-first-column"); @@ -55,6 +64,13 @@ export default class BlTableRow extends LitElement { } else if (this.disabled) { this.setAttribute("disabled", "true"); } + if (_changedProperties.has("selectionKey")) { + this.updateComplete.then(() => { + Array.from(this.querySelectorAll("bl-table-header-cell,bl-table-cell")).map(com => { + (com as BlTableHeaderCell | BlTableCell).requestUpdate(); + }); + }); + } } private get _table() { @@ -67,17 +83,9 @@ export default class BlTableRow extends LitElement { get disabled() { return !!this._firstTableCell?.disabled; } - get index() { - const parent = this.parentNode; - - if (!parent) { - return -1; - } - return [...parent.children].indexOf(this); - } get checked() { - return !!this._table?.isRowSelected(this.index); + return !!this._table?.isRowSelected(this.selectionKey); } get stickyFirstColumn() {