devex: Add Storybook for component development (#2556)
Adds Storybook in preparation for UI component refactoring.
This commit is contained in:
parent
c2a11ccf10
commit
78e2dadf0a
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@ -7,6 +7,7 @@
|
||||
"redhat.vscode-yaml",
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
"ms-python.black-formatter",
|
||||
"ms-python.pylint"
|
||||
"ms-python.pylint",
|
||||
"unifiedjs.vscode-mdx"
|
||||
]
|
||||
}
|
||||
|
@ -1 +1,2 @@
|
||||
node_modules/
|
||||
src/stories
|
||||
|
@ -12,6 +12,7 @@ module.exports = {
|
||||
"plugin:import-x/recommended",
|
||||
"plugin:wc/recommended",
|
||||
"plugin:lit/recommended",
|
||||
"plugin:storybook/recommended",
|
||||
"prettier",
|
||||
],
|
||||
plugins: ["@typescript-eslint", "lit"],
|
||||
|
2
frontend/.gitignore
vendored
2
frontend/.gitignore
vendored
@ -27,3 +27,5 @@ custom-elements.json
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/playwright/.cache/
|
||||
|
||||
*storybook.log
|
||||
|
107
frontend/.storybook/main.ts
Normal file
107
frontend/.storybook/main.ts
Normal file
@ -0,0 +1,107 @@
|
||||
import path from "path";
|
||||
|
||||
import type { StorybookConfig } from "@storybook/web-components-webpack5";
|
||||
import type { WebpackConfiguration } from "webpack-dev-server";
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
|
||||
addons: [
|
||||
"@storybook/addon-webpack5-compiler-swc",
|
||||
"@storybook/addon-essentials",
|
||||
{
|
||||
name: "@storybook/addon-styling-webpack",
|
||||
options: {
|
||||
// TODO Consolidate with webpack.config.js
|
||||
rules: [
|
||||
{
|
||||
// Global styles and assets, like fonts and Shoelace,
|
||||
// that get added to document styles
|
||||
test: /\.css$/,
|
||||
sideEffects: true,
|
||||
include: [
|
||||
path.resolve(__dirname, "../src"),
|
||||
path.resolve(
|
||||
__dirname,
|
||||
"../node_modules/@shoelace-style/shoelace",
|
||||
),
|
||||
],
|
||||
exclude: /\.stylesheet\.css$/,
|
||||
use: [
|
||||
require.resolve("style-loader"),
|
||||
{
|
||||
loader: require.resolve("css-loader"),
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve("postcss-loader"),
|
||||
options: {
|
||||
implementation: require.resolve("postcss"),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
// CSS loaded as raw string and used as a CSSStyleSheet
|
||||
test: /\.stylesheet\.css$/,
|
||||
sideEffects: true,
|
||||
type: "asset/source",
|
||||
use: [
|
||||
{
|
||||
loader: require.resolve("postcss-loader"),
|
||||
options: {
|
||||
implementation: require.resolve("postcss"),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
framework: {
|
||||
name: "@storybook/web-components-webpack5",
|
||||
options: {},
|
||||
},
|
||||
webpackFinal: async (config) => {
|
||||
// Show eslint errors from Storybook files in Webpack overlay
|
||||
const ESLintPlugin = require("eslint-webpack-plugin");
|
||||
|
||||
config.plugins?.push(
|
||||
new ESLintPlugin({
|
||||
files: ["**/stories/*.ts", "**/.storybook/*.ts"],
|
||||
}),
|
||||
);
|
||||
|
||||
// Watch for changes to custom-elements.json to re-render element
|
||||
// attributes whenever the custom elements manifest is generated
|
||||
(config as WebpackConfiguration).devServer = {
|
||||
watchFiles: ["**/custom-elements.json"],
|
||||
};
|
||||
|
||||
return config;
|
||||
},
|
||||
swc: {
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: "typescript",
|
||||
decorators: true,
|
||||
},
|
||||
// TODO Consolidate with tsconfig.json
|
||||
transform: {
|
||||
useDefineForClassFields: false,
|
||||
},
|
||||
baseUrl: path.resolve(__dirname, ".."),
|
||||
// TODO Consolidate with tsconfig.json
|
||||
paths: {
|
||||
"@/*": ["./src/*"],
|
||||
"~assets/*": ["./assets/src/*"],
|
||||
},
|
||||
},
|
||||
},
|
||||
core: {
|
||||
disableTelemetry: true,
|
||||
},
|
||||
};
|
||||
export default config;
|
25
frontend/.storybook/preview.ts
Normal file
25
frontend/.storybook/preview.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import "../src/global";
|
||||
|
||||
import {
|
||||
setCustomElementsManifest,
|
||||
type Preview,
|
||||
} from "@storybook/web-components";
|
||||
|
||||
import customElements from "../src/__generated__/custom-elements.json";
|
||||
|
||||
// Automatically document component properties
|
||||
setCustomElementsManifest(customElements);
|
||||
|
||||
const preview: Preview = {
|
||||
parameters: {
|
||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/i,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default preview;
|
18
frontend/custom-elements-manifest.config.mjs
Normal file
18
frontend/custom-elements-manifest.config.mjs
Normal file
@ -0,0 +1,18 @@
|
||||
export default {
|
||||
/** Globs to analyze */
|
||||
globs: ["src/**/*.ts"],
|
||||
/** Globs to exclude */
|
||||
exclude: ["__generated__", "__mocks__"],
|
||||
/** Directory to output CEM to */
|
||||
outdir: "src/__generated__",
|
||||
/** Run in dev mode, provides extra logging */
|
||||
// dev: true,
|
||||
/** Run in watch mode, runs on file changes */
|
||||
// watch: true,
|
||||
/** Include third party custom elements manifests */
|
||||
// dependencies: true,
|
||||
/** Output CEM path to `package.json`, defaults to true */
|
||||
packagejson: false,
|
||||
/** Enable special handling for litelement */
|
||||
litelement: true,
|
||||
};
|
@ -56,11 +56,11 @@ class MyCustomComponent extends BtrixElement {
|
||||
}
|
||||
```
|
||||
|
||||
### VS Code Snippet
|
||||
## VS Code Snippet
|
||||
|
||||
If developing with [Visual Studio Code](https://code.visualstudio.com/), you can generate boilerplate for a `BtrixElement` Browsertrix component by typing in `component` to any TypeScript file and selecting "Btrix Component". Hit ++tab++ to move your cursor between fillable fields in the boilerplate code.
|
||||
|
||||
### Unit Testing
|
||||
## Unit Testing
|
||||
|
||||
Unit test files live next to the component file and are suffixed with `.test` (ex: `my-custom-component.test.ts`).
|
||||
|
||||
|
13
frontend/docs/docs/develop/ui/storybook.md
Normal file
13
frontend/docs/docs/develop/ui/storybook.md
Normal file
@ -0,0 +1,13 @@
|
||||
# Using Storybook
|
||||
|
||||
[Storybook](https://storybook.js.org/) is a tool for documenting and building UI components in isolation. Component documentation is organized into ["stories"](https://storybook.js.org/docs/writing-stories) that show a variety of possible rendered states of a UI component.
|
||||
|
||||
Browsertrix component stories live in `frontend/src/stories`. Component attributes that are public properties (i.e. defined with Lit `@property({ type: Type })`) or documented in a TSDoc comment will automatically appear in stories through the [Custom Elements Manifest](https://custom-elements-manifest.open-wc.org/analyzer/getting-started/) file.
|
||||
|
||||
To develop using Storybook, run:
|
||||
|
||||
```sh
|
||||
yarn storybook:watch
|
||||
```
|
||||
|
||||
This will open Storybook in your default browser. Changes to Browsertrix components and stories wil automatically refresh the page.
|
@ -94,6 +94,7 @@ nav:
|
||||
- UI Development:
|
||||
- develop/frontend-dev.md
|
||||
- develop/ui/components.md
|
||||
- develop/ui/storybook.md
|
||||
- develop/localization.md
|
||||
- Design:
|
||||
- develop/ui/design-action-menus.md
|
||||
|
@ -47,6 +47,7 @@
|
||||
"eslint-import-resolver-typescript": "^3.6.1",
|
||||
"eslint-plugin-import-x": "4.5.1",
|
||||
"eslint-plugin-lit": "^1.11.0",
|
||||
"eslint-plugin-storybook": "^0.12.0",
|
||||
"eslint-plugin-wc": "^2.0.4",
|
||||
"eslint-webpack-plugin": "^4.1.0",
|
||||
"fork-ts-checker-webpack-plugin": "^6.2.6",
|
||||
@ -110,19 +111,35 @@
|
||||
"format": "prettier --write .",
|
||||
"format:check": "prettier --check .",
|
||||
"localize:extract": "lit-localize extract && prettier --write xliff/*.xlf",
|
||||
"localize:build": "lit-localize build"
|
||||
"localize:build": "lit-localize build",
|
||||
"cem": "custom-elements-manifest analyze",
|
||||
"prestorybook": "yarn cem",
|
||||
"prestorybook:build": "yarn cem",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"storybook:watch": "concurrently 'yarn cem --watch' 'yarn storybook'",
|
||||
"storybook:build": "storybook build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@custom-elements-manifest/analyzer": "^0.10.4",
|
||||
"@lit/localize-tools": "^0.8.0",
|
||||
"@storybook/addon-essentials": "^8.6.12",
|
||||
"@storybook/addon-styling-webpack": "^1.0.1",
|
||||
"@storybook/addon-webpack5-compiler-swc": "^3.0.0",
|
||||
"@storybook/blocks": "^8.6.12",
|
||||
"@storybook/test": "^8.6.12",
|
||||
"@storybook/web-components": "^8.6.12",
|
||||
"@storybook/web-components-webpack5": "^8.6.12",
|
||||
"@types/webpack-bundle-analyzer": "^4.7.0",
|
||||
"@web/dev-server-esbuild": "^0.3.3",
|
||||
"@web/dev-server-import-maps": "^0.2.0",
|
||||
"@web/dev-server-rollup": "^0.6.1",
|
||||
"concurrently": "^9.1.2",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^13.1.0",
|
||||
"prettier-plugin-tailwindcss": "^0.5.12",
|
||||
"rollup-plugin-typescript-paths": "^1.4.0",
|
||||
"sinon": "^12.0.1",
|
||||
"storybook": "^8.6.12",
|
||||
"ts-lit-plugin": "^2.0.1",
|
||||
"webpack-bundle-analyzer": "^4.10.1",
|
||||
"webpack-dev-server": "^5.2.0"
|
||||
|
@ -37,5 +37,11 @@ module.exports = {
|
||||
xmlSelfClosingSpace: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: "**/*.mdx",
|
||||
options: {
|
||||
proseWrap: "always",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -12,12 +12,7 @@ import { tw } from "@/utils/tailwind";
|
||||
type Variant = "neutral" | "danger";
|
||||
|
||||
/**
|
||||
* Custom styled button
|
||||
*
|
||||
* Usage example:
|
||||
* ```ts
|
||||
* <btrix-button>Click me</btrix-button>
|
||||
* ```
|
||||
* Custom styled button.
|
||||
*/
|
||||
@customElement("btrix-button")
|
||||
export class Button extends TailwindElement {
|
||||
|
@ -22,26 +22,6 @@ tableCSS.split("}").forEach((rule: string) => {
|
||||
* Low-level component for displaying content as a table.
|
||||
* To style tables, use TailwindCSS utility classes.
|
||||
*
|
||||
* @example Usage:
|
||||
* ```ts
|
||||
* <btrix-table>
|
||||
* <btrix-table-head class="border-b">
|
||||
* <btrix-table-header-cell class="border-r">col 1 </btrix-table-header-cell>
|
||||
* <btrix-table-header-cell>col 2</btrix-table-header-cell>
|
||||
* </btrix-table-head>
|
||||
* <btrix-table-body>
|
||||
* <btrix-table-row class="border-b">
|
||||
* <btrix-table-cell class="border-r">row 1 col 1</btrix-table-cell>
|
||||
* <btrix-table-cell>row 1 col 2</btrix-table-cell>
|
||||
* </btrix-table-row>
|
||||
* <btrix-table-row>
|
||||
* <btrix-table-cellclass="border-r">row 2 col 1</btrix-table-cell>
|
||||
* <btrix-table-cell>row 2 col 2</btrix-table-cell>
|
||||
* </btrix-table-row>
|
||||
* </btrix-table-body>
|
||||
* </btrix-table>
|
||||
* ```
|
||||
*
|
||||
* Table columns will be automatically sized according to its content.
|
||||
* To specify column size, use `grid-template-columns`.
|
||||
*
|
||||
|
11
frontend/src/global.ts
Normal file
11
frontend/src/global.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import "broadcastchannel-polyfill";
|
||||
import "construct-style-sheets-polyfill";
|
||||
import "./shoelace";
|
||||
import "./assets/fonts/Inter/inter.css";
|
||||
import "./assets/fonts/Recursive/recursive.css";
|
||||
import "./styles.css";
|
||||
|
||||
import { theme } from "@/theme";
|
||||
|
||||
// Make theme CSS available in document
|
||||
document.adoptedStyleSheets = [theme];
|
@ -1,4 +1,5 @@
|
||||
import "./utils/polyfills";
|
||||
import "./global";
|
||||
|
||||
import { provide } from "@lit/context";
|
||||
import { localized, msg, str } from "@lit/localize";
|
||||
@ -14,15 +15,9 @@ import { until } from "lit/directives/until.js";
|
||||
import { when } from "lit/directives/when.js";
|
||||
import isEqual from "lodash/fp/isEqual";
|
||||
|
||||
import "broadcastchannel-polyfill";
|
||||
import "construct-style-sheets-polyfill";
|
||||
import "./shoelace";
|
||||
import "./components";
|
||||
import "./features";
|
||||
import "./pages";
|
||||
import "./assets/fonts/Inter/inter.css";
|
||||
import "./assets/fonts/Recursive/recursive.css";
|
||||
import "./styles.css";
|
||||
|
||||
import { viewStateContext } from "./context/view-state";
|
||||
import { OrgTab, RouteNamespace } from "./routes";
|
||||
@ -38,7 +33,6 @@ import AuthService, {
|
||||
import { BtrixElement } from "@/classes/BtrixElement";
|
||||
import type { NavigateEventDetail } from "@/controllers/navigate";
|
||||
import type { NotifyEventDetail } from "@/controllers/notify";
|
||||
import { theme } from "@/theme";
|
||||
import { type Auth } from "@/types/auth";
|
||||
import {
|
||||
translatedLocales,
|
||||
@ -53,9 +47,6 @@ import { AppStateService } from "@/utils/state";
|
||||
import { formatAPIUser } from "@/utils/user";
|
||||
import brandLockupColor from "~assets/brand/browsertrix-lockup-color.svg";
|
||||
|
||||
// Make theme CSS available in document
|
||||
document.adoptedStyleSheets = [theme];
|
||||
|
||||
type DialogContent = {
|
||||
label?: TemplateResult | string;
|
||||
body?: TemplateResult | string;
|
||||
|
86
frontend/src/stories/Button.stories.ts
Normal file
86
frontend/src/stories/Button.stories.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import type { Meta, StoryObj } from "@storybook/web-components";
|
||||
import { html } from "lit";
|
||||
|
||||
import { renderButton, type RenderProps } from "./Button";
|
||||
|
||||
const meta = {
|
||||
component: "btrix-button",
|
||||
tags: ["autodocs"],
|
||||
render: renderButton,
|
||||
argTypes: {
|
||||
type: {
|
||||
control: { type: "select" },
|
||||
options: ["button", "submit"] satisfies RenderProps["type"][],
|
||||
},
|
||||
variant: {
|
||||
control: { type: "select" },
|
||||
options: ["neutral", "danger"] satisfies RenderProps["variant"][],
|
||||
},
|
||||
size: {
|
||||
control: { type: "select" },
|
||||
options: ["x-small", "small", "medium"] satisfies RenderProps["size"][],
|
||||
},
|
||||
},
|
||||
args: {
|
||||
label: "Button",
|
||||
filled: true,
|
||||
},
|
||||
parameters: {
|
||||
options: {
|
||||
showPanel: false,
|
||||
},
|
||||
},
|
||||
} satisfies Meta<RenderProps>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<RenderProps>;
|
||||
|
||||
export const Raised: Story = {
|
||||
args: {
|
||||
raised: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const Loading: Story = {
|
||||
args: {
|
||||
loading: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const Variants: Story = {
|
||||
render: () => html`
|
||||
${renderButton({
|
||||
variant: "neutral",
|
||||
label: "Neutral (Default)",
|
||||
filled: true,
|
||||
})}
|
||||
${renderButton({ variant: "danger", label: "Danger", filled: true })}
|
||||
`,
|
||||
};
|
||||
|
||||
export const Sizes: Story = {
|
||||
render: () => html`
|
||||
${renderButton({
|
||||
size: "x-small",
|
||||
label: "X-Small",
|
||||
filled: true,
|
||||
})}
|
||||
${renderButton({
|
||||
size: "small",
|
||||
label: "Small",
|
||||
filled: true,
|
||||
})}
|
||||
${renderButton({
|
||||
size: "medium",
|
||||
label: "Medium (Default",
|
||||
filled: true,
|
||||
})}
|
||||
`,
|
||||
};
|
||||
|
||||
export const Link: Story = {
|
||||
args: {
|
||||
href: "https://webrecorder.net",
|
||||
label: "Button Link",
|
||||
},
|
||||
};
|
30
frontend/src/stories/Button.ts
Normal file
30
frontend/src/stories/Button.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { html } from "lit";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import type { Button } from "@/components/ui/button";
|
||||
|
||||
import "@/components/ui/button";
|
||||
|
||||
export type RenderProps = Button;
|
||||
|
||||
export const renderButton = ({
|
||||
variant,
|
||||
filled,
|
||||
label,
|
||||
raised,
|
||||
loading,
|
||||
href,
|
||||
}: Partial<RenderProps>) => {
|
||||
return html`
|
||||
<btrix-button
|
||||
variant=${ifDefined(variant)}
|
||||
label=${ifDefined(label)}
|
||||
href=${ifDefined(href)}
|
||||
?filled=${filled}
|
||||
?raised=${raised}
|
||||
?loading=${loading}
|
||||
>
|
||||
${label}
|
||||
</btrix-button>
|
||||
`;
|
||||
};
|
28
frontend/src/stories/Intro.mdx
Normal file
28
frontend/src/stories/Intro.mdx
Normal file
@ -0,0 +1,28 @@
|
||||
import { Meta } from "@storybook/blocks";
|
||||
|
||||
<Meta title="Introduction" />
|
||||
|
||||
# Introduction
|
||||
|
||||
{/* TODO Consolidate with storybook.md in frontend/docs */}
|
||||
|
||||
Browsertrix component stories live in `frontend/src/stories`. Component
|
||||
attributes that are public properties (i.e. defined with Lit
|
||||
`@property({ type: Type })`) or documented in a TSDoc comment will automatically
|
||||
appear in stories through the
|
||||
[Custom Elements Manifest](https://custom-elements-manifest.open-wc.org/analyzer/getting-started/)
|
||||
file.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Component attributes aren't updating in Storybook**
|
||||
|
||||
Ensure you're running Storybook with `yarn storybook:watch` instead of
|
||||
`yarn storybook`. The "watch" script automatically regenerates
|
||||
`custom-elements.json`, which is the source of element attributes documentation.
|
||||
|
||||
## Storybook Docs
|
||||
|
||||
- [How to write stories](https://storybook.js.org/docs/writing-stories/?renderer=web-components)
|
||||
- [About autodocs](https://storybook.js.org/docs/writing-docs/autodocs/?renderer=web-components)
|
||||
- [Configure Storybook](https://storybook.js.org/docs/configure/?renderer=web-components)
|
31
frontend/src/stories/Table.stories.ts
Normal file
31
frontend/src/stories/Table.stories.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import type { Meta, StoryObj } from "@storybook/web-components";
|
||||
|
||||
import { defaultArgs, renderTable, type RenderProps } from "./Table";
|
||||
|
||||
import type { Table as TableComponent } from "@/components/ui/table/table";
|
||||
|
||||
const meta = {
|
||||
component: "btrix-table",
|
||||
subcomponents: {
|
||||
TableRow: "btrix-table-row",
|
||||
},
|
||||
render: renderTable,
|
||||
tags: ["autodocs"],
|
||||
argTypes: {
|
||||
columns: { table: { disable: true } },
|
||||
rows: { table: { disable: true } },
|
||||
},
|
||||
args: defaultArgs,
|
||||
parameters: {
|
||||
options: {
|
||||
showPanel: false,
|
||||
},
|
||||
},
|
||||
} satisfies Meta<RenderProps>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<TableComponent>;
|
||||
|
||||
export const BasicTable: Story = {
|
||||
args: {},
|
||||
};
|
81
frontend/src/stories/Table.ts
Normal file
81
frontend/src/stories/Table.ts
Normal file
@ -0,0 +1,81 @@
|
||||
import { html, type TemplateResult } from "lit";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
import "@/components/ui/table";
|
||||
|
||||
const columns = {
|
||||
name: {
|
||||
title: "Name",
|
||||
},
|
||||
email: {
|
||||
title: "Email",
|
||||
},
|
||||
role: {
|
||||
title: "Role",
|
||||
},
|
||||
remove: {
|
||||
title: html`<span class="sr-only">Remove</span>`,
|
||||
renderItem: () => html`<sl-icon name="trash3"></sl-icon>`,
|
||||
},
|
||||
} satisfies RenderProps["columns"];
|
||||
const rows: { data: Omit<Record<keyof typeof columns, unknown>, "remove"> }[] =
|
||||
[
|
||||
{
|
||||
data: {
|
||||
name: "Alice",
|
||||
email: "alice@example.com",
|
||||
role: 40,
|
||||
},
|
||||
},
|
||||
{
|
||||
data: { name: "Bob", email: "bob@example.com", role: 20 },
|
||||
},
|
||||
] satisfies RenderProps["rows"];
|
||||
|
||||
export interface RenderProps {
|
||||
columns: Record<
|
||||
string,
|
||||
{
|
||||
title: string | TemplateResult;
|
||||
classes?: string;
|
||||
renderItem?: (data: Record<string, unknown>) => TemplateResult;
|
||||
}
|
||||
>;
|
||||
rows: {
|
||||
data: Record<string, unknown>;
|
||||
classes?: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export const defaultArgs = { columns, rows } satisfies RenderProps;
|
||||
|
||||
export const renderTable = ({ columns: headers, rows: items }: RenderProps) => {
|
||||
return html`
|
||||
<btrix-table>
|
||||
<btrix-table-head>
|
||||
${Object.values(headers).map(
|
||||
({ title, classes }) => html`
|
||||
<btrix-table-header-cell class=${ifDefined(classes)}>
|
||||
${title}
|
||||
</btrix-table-header-cell>
|
||||
`,
|
||||
)}
|
||||
</btrix-table-head>
|
||||
<btrix-table-body>
|
||||
${items.map(
|
||||
({ classes, data }) => html`
|
||||
<btrix-table-row class=${ifDefined(classes)}>
|
||||
${Object.entries(headers).map(
|
||||
([key, { renderItem }]) => html`
|
||||
<btrix-table-cell class=${ifDefined(classes)}>
|
||||
${renderItem ? renderItem(data) : data[key]}
|
||||
</btrix-table-cell>
|
||||
`,
|
||||
)}
|
||||
</btrix-table-row>
|
||||
`,
|
||||
)}
|
||||
</btrix-table-body>
|
||||
</btrix-table>
|
||||
`;
|
||||
};
|
@ -14,6 +14,7 @@
|
||||
"inlineSources": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"useDefineForClassFields": false,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "ts-lit-plugin",
|
||||
|
1527
frontend/yarn.lock
generated
1527
frontend/yarn.lock
generated
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user