chore: Clean up data grid component (#2604)

- Moves data grid styles to separate stylesheet.
- Adds `rowsSelectable` option, renames `rows-` properties to match.
- Adds WIP `rowsExpandable` option.
- Fixes showing tooltip on focus.
- Cleans up rows controller typing.

---------

Co-authored-by: Emma Segal-Grossman <hi@emma.cafe>
This commit is contained in:
sua yoo 2025-05-14 09:44:07 -07:00 committed by GitHub
parent c73512dbd4
commit 7c9627f4bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 336 additions and 116 deletions

View File

@ -41,6 +41,7 @@ export class DataGridFocusController implements ReactiveController {
return;
}
// Move focus from table cell to on first tabbable element
const el = opts.setFocusOnTabbable
? this.firstTabbable
: this.firstFocusable;
@ -55,6 +56,28 @@ export class DataGridFocusController implements ReactiveController {
el.focus();
}
}
// Show tooltip on tab focus. Tooltip on any focus should be
// disabled in `btrix-data-grid-row` to prevent tooltips being
// showing duplicate messages during form submission.
const tooltip = this.#host.closest("sl-tooltip");
if (tooltip && !tooltip.disabled) {
const hideTooltip = () => {
void tooltip.hide();
this.#host.removeEventListener("input", hideTooltip);
this.#host.removeEventListener("blur", hideTooltip);
};
this.#host.addEventListener("input", hideTooltip, {
once: true,
});
this.#host.addEventListener("blur", hideTooltip, {
once: true,
});
void tooltip.show();
}
},
{ passive: true, capture: true },
);

View File

@ -1,12 +1,19 @@
import type { ReactiveController, ReactiveControllerHost } from "lit";
import type {
ReactiveController,
ReactiveControllerHost,
TemplateResult,
} from "lit";
import { nanoid } from "nanoid";
import type { EmptyObject } from "type-fest";
import type { DataGrid } from "../data-grid";
import { renderRows } from "../renderRows";
import type { GridItem, GridRowId, GridRows } from "../types";
import { cached } from "@/utils/weakCache";
export const emptyItem: EmptyObject = {};
/**
* Enables removing and adding rows from a grid.
*
@ -15,19 +22,22 @@ import { cached } from "@/utils/weakCache";
* that are slotted into `<btrix-data-grid>`, it may be necessary to
* implement this controller on the container component.
*/
export class DataGridRowsController implements ReactiveController {
export class DataGridRowsController<Item extends GridItem = GridItem>
implements ReactiveController
{
readonly #host: ReactiveControllerHost &
EventTarget & {
items?: GridItem[];
rowKey?: DataGrid["rowKey"];
defaultItem?: DataGrid["defaultItem"];
removeRows?: DataGrid["removeRows"];
addRows?: DataGrid["addRows"];
};
items?: Item[];
} & Partial<
Pick<DataGrid, "rowKey" | "defaultItem" | "rowsRemovable" | "rowsAddible">
>;
#prevItems?: GridItem[];
#prevItems?: Item[];
public rows: GridRows<GridItem> = new Map<GridRowId, GridItem>();
public rows: GridRows<Item | EmptyObject> = new Map<
GridRowId,
Item | EmptyObject
>();
constructor(host: ReactiveControllerHost & EventTarget) {
this.#host = host;
@ -46,22 +56,19 @@ export class DataGridRowsController implements ReactiveController {
}
}
private setRowsFromItems<T extends GridItem = GridItem>(items: T[]) {
private setRowsFromItems(items: Item[]) {
const rowKey = this.#host.rowKey;
this.rows = new Map(
this.#host.rowKey
? items.map((item) => [
item[rowKey as unknown as string] as GridRowId,
item,
])
rowKey
? items.map((item) => [item[rowKey] as GridRowId, item])
: items.map(
cached((item) => [nanoid(), item], { cacheConstructor: Map }),
),
);
}
public setItems<T extends GridItem = GridItem>(items: T[]) {
public setItems(items: Item[]) {
if (!this.#prevItems || items !== this.#prevItems) {
this.setRowsFromItems(items);
@ -69,15 +76,12 @@ export class DataGridRowsController implements ReactiveController {
}
}
public updateItem<T extends GridItem = GridItem>(id: GridRowId, item: T) {
public updateItem(id: GridRowId, item: Item) {
this.rows.set(id, item);
this.#host.requestUpdate();
}
public addRows<T extends GridItem = GridItem>(
defaultItem: T | EmptyObject = {},
count = 1,
) {
public addRows(defaultItem: Item | EmptyObject = emptyItem, count = 1) {
for (let i = 0; i < count; i++) {
const id = nanoid();
@ -96,4 +100,17 @@ export class DataGridRowsController implements ReactiveController {
this.#host.requestUpdate();
}
public isEmpty(item: Item | EmptyObject): item is EmptyObject {
return item === emptyItem;
}
public renderRows(
renderRow: (
{ id, item }: { id: GridRowId; item: Item | EmptyObject },
index: number,
) => TemplateResult,
) {
return renderRows<Item>(this.rows, renderRow);
}
}

View File

@ -3,6 +3,7 @@ import clsx from "clsx";
import { html, type TemplateResult } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import get from "lodash/fp/get";
import { TableCell } from "../table/table-cell";
@ -119,7 +120,7 @@ export class DataGridCell extends TableCell {
}
renderCell = ({ item }: { item: GridItem }) => {
return html`${(this.column && item[this.column.field]) ?? ""}`;
return html`${(this.column && get(this.column.field, item)) ?? ""}`;
};
renderEditCell = ({

View File

@ -4,6 +4,7 @@ import { html, type PropertyValues } from "lit";
import { customElement, property, queryAll, state } from "lit/decorators.js";
import { directive } from "lit/directive.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { when } from "lit/directives/when.js";
import isEqual from "lodash/fp/isEqual";
import { CellDirective } from "./cellDirective";
@ -58,18 +59,39 @@ export class DataGridRow extends FormControl(TableRow) {
@property({ type: Boolean })
removable = false;
/**
* Whether the row can be clicked.
*/
@property({ type: Boolean })
clickable = false;
/**
* Whether the row can be expanded.
*/
@property({ type: Boolean })
expandable = false;
/**
* Whether cells can be edited.
*/
@property({ type: Boolean })
editCells = false;
/**
* Vertical alignment of content.
*/
@property({ type: String })
alignContent: "start" | "center" | "end" = "center";
/**
* Form control name, if used in a form.
*/
@property({ type: String, reflect: true })
name?: string;
@state()
private expanded = false;
@state()
private cellValues: Partial<GridItem> = {};
@ -132,8 +154,31 @@ export class DataGridRow extends FormControl(TableRow) {
render() {
if (!this.columns?.length) return html``;
let expandCell = html``;
let removeCell = html``;
if (this.expandable) {
expandCell = html`
<btrix-data-grid-cell
class=${clsx(tw`border-l p-0`, cellStyle)}
@keydown=${this.onKeydown}
>
<sl-icon-button
class=${clsx(
tw`p-1 text-base transition-transform`,
this.expanded && tw`rotate-90`,
)}
name="chevron-right"
label=${this.expanded ? msg("Contract") : msg("Expand")}
@click=${(e: MouseEvent) => {
e.stopPropagation();
this.expanded = !this.expanded;
}}
></sl-icon-button>
</btrix-data-grid-cell>
`;
}
if (this.removable) {
removeCell = html`
<btrix-data-grid-cell
@ -160,57 +205,58 @@ export class DataGridRow extends FormControl(TableRow) {
`;
}
return html`${this.columns.map(this.renderCell)}${removeCell}`;
return html`${expandCell}${this.columns.map(this.renderCell)}${removeCell}
${when(this.expanded && this.item, (item) => this.renderDetails({ item }))} `;
}
renderDetails = (_row: { item: GridItem }) => html``;
private readonly renderCell = (col: GridColumn, i: number) => {
const validationMessage = this.#invalidInputsMap.get(col.field);
const item = this.item;
if (!item) return;
const editable = this.editCells && col.editable;
const tooltipContent = editable
? this.#invalidInputsMap.get(col.field)
: col.renderCellTooltip
? col.renderCellTooltip({ item })
: undefined;
return html`
<sl-tooltip
?disabled=${!validationMessage}
content=${validationMessage || ""}
class="[--max-width:40ch]"
?disabled=${!tooltipContent}
hoist
placement="bottom"
trigger=${
// Manually show/hide tooltip on blur/focus
"manual"
// Disable showing tooltip on focus by default
// so that it doesn't show along with the browser
// validation message on form submit.
// The tooltip is shown manually when tabbed to
// by checking `:focus-visible` on focus.
"hover"
}
>
<btrix-data-grid-cell
class=${clsx(
i > 0 && tw`border-l`,
!this.clickable && i > 0 && tw`border-l`,
cellStyle,
editable && editableCellStyle,
this.alignContent === "start" && tw`items-start`,
this.alignContent === "end" && tw`items-end`,
col.align === "center" && tw`justify-center`,
col.align === "end" && tw`justify-end`,
)}
.column=${col}
.item=${this.item}
.item=${item}
value=${ifDefined(this.cellValues[col.field] ?? undefined)}
?editable=${editable}
${cell(col)}
@keydown=${this.onKeydown}
@focus=${(e: CustomEvent) => {
e.stopPropagation();
const tableCell = e.target as DataGridCell;
const tooltip = tableCell.closest("sl-tooltip");
if (tooltip?.open) {
void tooltip.hide();
}
}}
@blur=${(e: CustomEvent) => {
e.stopPropagation();
const tableCell = e.target as DataGridCell;
const tooltip = tableCell.closest("sl-tooltip");
if (tooltip && !tooltip.disabled) {
void tooltip.show();
}
}}
></btrix-data-grid-cell>
<div slot="content">${tooltipContent}</div>
</sl-tooltip>
`;
};

View File

@ -0,0 +1,37 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
:host {
--border: 1px solid var(--sl-panel-border-color);
}
.data-grid-body--horizontalRule btrix-data-grid-row:nth-of-type(n + 2),
.data-grid-body--horizontalRule
::slotted(btrix-data-grid-row:nth-of-type(n + 2)) {
border-top: var(--border) !important;
}
.data-grid-body--rowsSelectable btrix-data-grid-row,
.data-grid-body--rowsSelectable ::slotted(btrix-data-grid-row) {
/* TODO Same ring color as edit cells */
@apply cursor-pointer ring-inset hover:bg-blue-50/50 hover:ring-1;
}
.data-grid-body--editCells btrix-data-grid-row,
.data-grid-body--editCells ::slotted(btrix-data-grid-row) {
/* TODO Support different input sizes */
min-height: calc(var(--sl-input-height-medium) + 1px);
}
.data-grid-body--not-stickyHeader btrix-data-grid-row:first-child,
.data-grid-body--not-stickyHeader ::slotted(btrix-data-grid-row:first-child) {
@apply rounded-t;
}
.data-grid-body--not-rowsAddible btrix-data-grid-row:last-child,
.data-grid-body--not-rowsAddible ::slotted(btrix-data-grid-row:last-child) {
@apply rounded-b;
}
}

View File

@ -1,7 +1,7 @@
import { localized, msg } from "@lit/localize";
import type { SlChangeEvent, SlInput } from "@shoelace-style/shoelace";
import clsx from "clsx";
import { css, html, nothing } from "lit";
import { html, nothing, unsafeCSS } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { nanoid } from "nanoid";
@ -9,40 +9,31 @@ import type { EmptyObject } from "type-fest";
import { DataGridRowsController } from "./controllers/rows";
import type { DataGridRow, RowRemoveEventDetail } from "./data-grid-row";
import { renderRows } from "./renderRows";
import stylesheet from "./data-grid.stylesheet.css";
import type { BtrixSelectRowEvent } from "./events/btrix-select-row";
import type { GridColumn, GridItem } from "./types";
import { TailwindElement } from "@/classes/TailwindElement";
import { pluralOf } from "@/utils/pluralize";
import { tw } from "@/utils/tailwind";
const styles = unsafeCSS(stylesheet);
/**
* Data grids structure data into rows and and columns.
*
* [Figma design file](https://www.figma.com/design/ySaSMMI2vctbxP3edAHXib/Webrecorder-Shoelace?node-id=1327-354&p=f)
*
* @slot label
* @slot rows
* @fires btrix-change
* @fires btrix-remove
* @fires btrix-select-row
*/
@customElement("btrix-data-grid")
@localized()
export class DataGrid extends TailwindElement {
static styles = css`
:host {
--border: 1px solid var(--sl-panel-border-color);
}
btrix-data-grid-row:not(:first-of-type),
btrix-table-body ::slotted(*:nth-of-type(n + 2)) {
border-top: var(--border) !important;
}
btrix-data-grid-row,
btrix-table-body ::slotted(btrix-data-grid-row) {
/* TODO Support different input sizes */
min-height: calc(var(--sl-input-height-medium) + 1px);
}
`;
static styles = styles;
/**
* Set of columns.
@ -71,17 +62,41 @@ export class DataGrid extends TailwindElement {
@property({ type: String })
rowKey?: string;
/**
* Whether rows can be selected, firing a `btrix-select-row` event.
*/
@property({ type: Boolean })
rowsSelectable = false;
/**
* Whether a single or multiple rows can be selected (multiple not yet implemented.)
*/
@property({ type: String })
selectMode: "single" | "multiple" = "single";
/**
* WIP: Whether rows can be expanded, revealing more content below the row.
*/
@property({ type: Boolean })
rowsExpandable = false;
/**
* Whether rows can be removed.
*/
@property({ type: Boolean })
removeRows = false;
rowsRemovable = false;
/**
* Whether rows can be added.
*/
@property({ type: Boolean })
addRows = false;
rowsAddible = false;
/**
* Vertical alignment of content in body rows.
*/
@property({ type: String })
alignRows: "start" | "center" | "end" = "center";
/**
* Make the number of rows being added configurable,
@ -145,7 +160,7 @@ export class DataGrid extends TailwindElement {
${this.renderTable()}
</div>
${this.addRows && !this.addRowsInputValue
${this.rowsAddible && !this.addRowsInputValue
? this.renderAddButton()
: nothing}
`;
@ -155,7 +170,7 @@ export class DataGrid extends TailwindElement {
if (!this.columns?.length) return;
const cssWidths = this.columns.map((col) => col.width ?? "1fr");
const addRowsInputValue = this.addRows && this.addRowsInputValue;
const addRowsInputValue = this.rowsAddible && this.addRowsInputValue;
return html`
<btrix-table
@ -168,8 +183,9 @@ export class DataGrid extends TailwindElement {
this.stickyHeader === "table" &&
tw`max-h-[calc(100vh-4rem)] overflow-y-auto`,
)}
style="--btrix-table-grid-template-columns: ${cssWidths.join(" ")}${this
.removeRows
style="--btrix-table-grid-template-columns: ${this.rowsExpandable
? "max-content "
: ""}${cssWidths.join(" ")}${this.rowsRemovable
? " max-content"
: ""}"
aria-labelledby=${ifDefined(
@ -181,14 +197,29 @@ export class DataGrid extends TailwindElement {
class=${clsx(
tw`[--btrix-table-cell-padding:var(--sl-spacing-x-small)]`,
this.stickyHeader
? tw`sticky top-0 z-10 rounded-t-[0.1875rem] border-b bg-neutral-50 [&>*:not(:first-of-type)]:border-l`
? [
tw`sticky top-0 z-10 self-start rounded-t-[0.1875rem] border-b bg-neutral-50`,
!this.rowsSelectable &&
tw`[&>*:not(:first-of-type)]:border-l`,
]
: tw`px-px`,
)}
>
${this.rowsExpandable
? html`
<btrix-table-header-cell>
<span class="sr-only">${msg("Expand row")}</span>
</btrix-table-header-cell>
`
: nothing}
${this.columns.map(
(col) => html`
<btrix-table-header-cell
class=${clsx(col.description && tw`flex-wrap`)}
class=${clsx(
col.description && tw`flex-wrap`,
col.align === "center" && tw`justify-center`,
col.align === "end" && tw`justify-end`,
)}
>
${col.label}
${col.description
@ -204,7 +235,7 @@ export class DataGrid extends TailwindElement {
</btrix-table-header-cell>
`,
)}
${this.removeRows
${this.rowsRemovable
? html`<btrix-table-header-cell>
<span class="sr-only">${msg("Remove row")}</span>
</btrix-table-header-cell>`
@ -212,12 +243,19 @@ export class DataGrid extends TailwindElement {
</btrix-table-head>
<btrix-table-body
class=${clsx(
"data-grid-body data-grid-body--horizontalRule",
this.stickyHeader
? "data-grid-body--stickyHeader"
: "data-grid-body--not-stickyHeader",
this.rowsSelectable && "data-grid-body--rowsSelectable",
this.rowsAddible
? "data-grid-body--rowsAddible"
: !this.addRowsInputValue && "data-grid-body--not-rowsAddible",
this.editCells && "data-grid-body--editCells",
tw`[--btrix-table-cell-padding:var(--sl-spacing-x-small)]`,
tw`bg-[var(--sl-panel-background-color)] leading-none`,
!this.stickyHeader && [
tw`border`,
addRowsInputValue ? tw`rounded-t` : tw`rounded`,
],
!this.stickyHeader && tw`border`,
addRowsInputValue ? tw`rounded-t` : tw`rounded`,
)}
@btrix-remove=${(e: CustomEvent<RowRemoveEventDetail>) => {
const { key } = e.detail;
@ -288,15 +326,31 @@ export class DataGrid extends TailwindElement {
return html`
<slot name="rows" class="contents" @slotchange=${this.onRowSlotChange}>
${this.items
? renderRows(
this.rowsController.rows,
? this.rowsController.renderRows(
({ id, item }) => html`
<btrix-data-grid-row
key=${id}
.item=${item}
.columns=${this.columns}
?removable=${this.removeRows}
alignContent=${ifDefined(this.alignRows)}
?removable=${this.rowsRemovable}
?clickable=${this.rowsSelectable}
?expandable=${this.rowsExpandable}
?editCells=${this.editCells}
@click=${() => {
if (this.rowsSelectable) {
this.dispatchEvent(
new CustomEvent<BtrixSelectRowEvent["detail"]>(
"btrix-select-row",
{
detail: { id, item },
bubbles: true,
composed: true,
},
),
);
}
}}
></btrix-data-grid-row>
`,
)
@ -337,7 +391,7 @@ export class DataGrid extends TailwindElement {
}
};
const removable = this.removeRows;
const removable = this.rowsRemovable;
const editCells = this.editCells;
rows.forEach((el) => {

View File

@ -0,0 +1,12 @@
import type { GridItem, GridRowId } from "../types";
export type BtrixSelectRowEvent<T = GridItem> = CustomEvent<{
id: GridRowId;
item: T;
}>;
declare global {
interface GlobalEventHandlersEventMap {
"btrix-select-row": BtrixSelectRowEvent;
}
}

View File

@ -1,18 +1,19 @@
import { type TemplateResult } from "lit";
import { repeat } from "lit/directives/repeat.js";
import type { EmptyObject } from "type-fest";
import type { GridItem, GridRowId, GridRows } from "./types";
export function renderRows<T = GridItem>(
rows: GridRows<GridItem>,
rows: GridRows<T | EmptyObject>,
renderRow: (
{ id, item }: { id: GridRowId; item: T },
{ id, item }: { id: GridRowId; item: T | EmptyObject },
index: number,
) => TemplateResult,
) {
return repeat(
rows,
([id]) => id,
([id, item], i) => renderRow({ id, item: item as T }, i),
([id, item], i) => renderRow({ id, item }, i),
);
}

View File

@ -25,7 +25,7 @@ export type GridColumnSelectType = {
}[];
};
export type GridColumn<T = string> = {
export type GridColumn<T = string, Item = GridItem> = {
field: T;
label: string | TemplateResult;
description?: string;
@ -33,11 +33,13 @@ export type GridColumn<T = string> = {
required?: boolean;
inputPlaceholder?: string;
width?: string;
align?: "start" | "center" | "end";
renderEditCell?: (props: {
item: GridItem;
value?: GridItem[keyof GridItem];
item: Item;
value?: Item[keyof Item];
}) => TemplateResult<1>;
renderCell?: (props: { item: GridItem }) => TemplateResult<1>;
renderCell?: (props: { item: Item }) => TemplateResult<1>;
renderCellTooltip?: (props: { item: Item }) => TemplateResult<1>;
} & (
| {
inputType?: GridColumnType;

View File

@ -5,10 +5,13 @@ import { html, type PropertyValues } from "lit";
import { customElement, property, queryAll } from "lit/decorators.js";
import { when } from "lit/directives/when.js";
import isEqual from "lodash/fp/isEqual";
import type { EmptyObject } from "type-fest";
import { BtrixElement } from "@/classes/BtrixElement";
import { DataGridRowsController } from "@/components/ui/data-grid/controllers/rows";
import { renderRows } from "@/components/ui/data-grid/renderRows";
import {
DataGridRowsController,
emptyItem,
} from "@/components/ui/data-grid/controllers/rows";
import type { SyntaxInput } from "@/components/ui/syntax-input";
import { FormControlController } from "@/controllers/formControl";
import type { BtrixChangeEvent } from "@/events/btrix-change";
@ -24,11 +27,6 @@ type SelectorItem = {
attribute: string;
};
const emptyItem = {
selector: "",
attribute: "",
};
/**
* Displays link selector crawl configuration in an editable table.
*
@ -48,7 +46,7 @@ export class LinkSelectorTable extends FormControl(BtrixElement) {
@property({ type: Boolean })
editable = false;
readonly #rowsController = new DataGridRowsController(this);
readonly #rowsController = new DataGridRowsController<SelectorItem>(this);
@queryAll("btrix-syntax-input")
private readonly syntaxInputs!: NodeListOf<SyntaxInput>;
@ -64,7 +62,7 @@ export class LinkSelectorTable extends FormControl(BtrixElement) {
const selectLinks: string[] = [];
this.#rowsController.rows.forEach((val) => {
if (val === emptyItem) return;
if (this.#rowsController.isEmpty(val)) return;
selectLinks.push(`${val.selector}${SELECTOR_DELIMITER}${val.attribute}`);
});
@ -76,7 +74,8 @@ export class LinkSelectorTable extends FormControl(BtrixElement) {
const selectLinks: string[] = [];
this.#rowsController.rows.forEach((val) => {
if (!val.selector || !val.attribute) return;
if (this.#rowsController.isEmpty(val) || !val.selector || !val.attribute)
return;
selectLinks.push(`${val.selector}${SELECTOR_DELIMITER}${val.attribute}`);
});
@ -122,7 +121,7 @@ export class LinkSelectorTable extends FormControl(BtrixElement) {
)}
</btrix-table-head>
<btrix-table-body class="overflow-auto">
${renderRows<SelectorItem>(this.#rowsController.rows, this.row)}
${this.#rowsController.renderRows(this.row)}
</btrix-table-body>
</btrix-table>
@ -144,11 +143,16 @@ export class LinkSelectorTable extends FormControl(BtrixElement) {
}
private readonly row = (
{ id, item }: { id: string; item: SelectorItem },
{ id, item }: { id: string; item: SelectorItem | EmptyObject },
i: number,
) => {
const sel = item.selector;
const attr = item.attribute;
let sel = "";
let attr = "";
if (!this.#rowsController.isEmpty(item)) {
sel = item.selector;
attr = item.attribute;
}
return html`
<btrix-table-row class=${i > 0 ? "border-t" : ""}>

View File

@ -33,6 +33,9 @@ type Story = StoryObj<RenderProps>;
* In its most basic configuration, the only required fields
* are a list of items, and a list of columns that define which
* key-value pairs of an item should be displayed.
*
* Nested keys are supported by specifying a deep path, e.g.
* `object.nestedObject.key`.
*/
export const Basic: Story = {
args: {},
@ -103,7 +106,7 @@ export const ColumnWidths: Story = {
*/
export const RemoveRows: Story = {
args: {
removeRows: true,
rowsRemovable: true,
},
};
@ -112,7 +115,7 @@ export const RemoveRows: Story = {
*/
export const AddRows: Story = {
args: {
addRows: true,
rowsAddible: true,
defaultItem: {
a: "A",
b: "--",
@ -129,7 +132,7 @@ export const AddRows: Story = {
export const AddRowsInput: Story = {
name: "Add more than one row",
args: {
addRows: true,
rowsAddible: true,
addRowsInputValue: 5,
defaultItem: {
a: "A",
@ -141,6 +144,18 @@ export const AddRowsInput: Story = {
},
};
/**
* Rows can be selected.
*
* Open your browser console logs to view the clicked row.
*/
export const SelectRow: Story = {
args: {
items: makeItems(5),
rowsSelectable: true,
},
};
/**
* Cells can be editable.
*/
@ -262,9 +277,9 @@ export const FormControl: Story = {
}
formControlLabel="Page QA Table"
stickyHeader="table"
addRows
rowsAddible
addRowsInputValue="10"
removeRows
rowsRemovable
editCells
>
${renderRows(

View File

@ -3,6 +3,7 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { nanoid } from "nanoid";
import type { DataGrid } from "@/components/ui/data-grid/data-grid";
import type { BtrixSelectRowEvent } from "@/components/ui/data-grid/events/btrix-select-row";
import "@/components/ui/data-grid";
@ -37,9 +38,11 @@ export const renderComponent = ({
items,
formControlLabel,
stickyHeader,
addRows,
rowsAddible,
addRowsInputValue,
removeRows,
rowsRemovable,
rowsSelectable,
selectMode,
editCells,
defaultItem,
}: Partial<RenderProps>) => {
@ -50,10 +53,15 @@ export const renderComponent = ({
.defaultItem=${defaultItem}
formControlLabel=${ifDefined(formControlLabel)}
stickyHeader=${ifDefined(stickyHeader)}
?addRows=${addRows}
?rowsAddible=${rowsAddible}
addRowsInputValue=${ifDefined(addRowsInputValue)}
?removeRows=${removeRows}
?rowsRemovable=${rowsRemovable}
?rowsSelectable=${rowsSelectable}
selectMode=${ifDefined(selectMode)}
?editCells=${editCells}
@btrix-select-row=${(e: BtrixSelectRowEvent) => {
console.log("row clicked:", e.detail);
}}
>
</btrix-data-grid>
`;