Skip to content

Commit

Permalink
Merge pull request #38 from bryanmylee/custom-data-id
Browse files Browse the repository at this point in the history
Custom `dataId` on BodyRows
  • Loading branch information
bryanmylee committed Jul 29, 2022
2 parents b520894 + 39da9f9 commit 5575005
Show file tree
Hide file tree
Showing 14 changed files with 203 additions and 29 deletions.
11 changes: 11 additions & 0 deletions .eslintignore
@@ -0,0 +1,11 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
/coverage
pnpm-lock.yaml
/docs
3 changes: 2 additions & 1 deletion .prettierignore
Expand Up @@ -7,4 +7,5 @@ node_modules
.env.*
!.env.example
/coverage
pnpm-lock.yaml
pnpm-lock.yaml
/docs
14 changes: 7 additions & 7 deletions README.md
Expand Up @@ -24,13 +24,13 @@ Visit the [documentation](https://svelte-headless-table.bryanmylee.com/) for cod

Svelte Headless Table is designed to work **seamlessly** with Svelte. If you love Svelte, you will love Svelte Headless Table.

* **Full TypeScript support**
* Compatible with **SvelteKit** and SSR
* Manage state with Svelte stores
* Headless and fully customizable
* Intuitive column-first declarative model
* Highly performant
* Feature-rich
- **Full TypeScript support**
- Compatible with **SvelteKit** and SSR
- Manage state with Svelte stores
- Headless and fully customizable
- Intuitive column-first declarative model
- Highly performant
- Feature-rich

## All the features you could ever need!

Expand Down
14 changes: 13 additions & 1 deletion docs/src/routes/docs/[...2]api/[...4]create-view-model.md
Expand Up @@ -17,10 +17,12 @@ sidebar_title: createViewModel

---

### `Table#createViewModel: (columns) => TableViewModel`
### `Table#createViewModel: (columns, options) => TableViewModel`

`columns` is an array of `Column` instances returned from [`Table#createColumns`](./create-columns.md#table-createcolumns-columns-column).

`options` is an optional configuration object to configure the view model.

```ts {4}
const table = createTable(data);
const columns = table.createColumns(...);
Expand All @@ -32,3 +34,13 @@ const {
tableBodyAttrs,
} = viewModel;
```

#### `options.rowDataId?: (item, index) => string`

Defines a custom [`dataId`](./body-row.md#dataid-string) for each row.

:::admonition type="warning"
In the context of sub-rows, the `index` of the first sub-row is `0`.
:::

_Defaults to the item index_.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "svelte-headless-table",
"version": "0.11.1",
"version": "0.12.0",
"scripts": {
"dev": "vite dev",
"build": "vite build",
Expand All @@ -9,7 +9,7 @@
"prepare": "svelte-kit sync",
"check": "svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --ignore-path .prettierignore --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
"lint": "prettier --ignore-path .prettierignore --check --plugin-search-dir=. . && eslint --ignore-path .eslintignore .",
"format": "prettier --ignore-path .prettierignore --write --plugin-search-dir=. .",
"test": "jest --collect-coverage",
"test:watch": "npm test -- --watch"
Expand Down
1 change: 1 addition & 0 deletions src/lib/bodyCells.DataBodyCell.render.test.ts
Expand Up @@ -23,6 +23,7 @@ const user: User = {

const row = new DataBodyRow({
id: '0',
dataId: '0',
original: user,
cells: [],
cellForId: {},
Expand Down
1 change: 1 addition & 0 deletions src/lib/bodyCells.DisplayBodyCell.render.test.ts
Expand Up @@ -23,6 +23,7 @@ const user: User = {

const row = new DataBodyRow({
id: '0',
dataId: '0',
original: user,
cells: [],
cellForId: {},
Expand Down
132 changes: 128 additions & 4 deletions src/lib/bodyRows.getBodyRows.test.ts
Expand Up @@ -59,7 +59,13 @@ it('transforms empty data', () => {
it('transforms data for data columns', () => {
const actual = getBodyRows(data, dataColumns);

const row0 = new DataBodyRow<User>({ id: '0', original: data[0], cells: [], cellForId: {} });
const row0 = new DataBodyRow<User>({
id: '0',
dataId: '0',
original: data[0],
cells: [],
cellForId: {},
});
const cells0 = [
new DataBodyCell<User>({
row: row0,
Expand All @@ -85,7 +91,113 @@ it('transforms data for data columns', () => {
};
row0.cellForId = cellForId0;

const row1 = new DataBodyRow<User>({ id: '1', original: data[1], cells: [], cellForId: {} });
const row1 = new DataBodyRow<User>({
id: '1',
dataId: '1',
original: data[1],
cells: [],
cellForId: {},
});
const cells1 = [
new DataBodyCell<User>({
row: row1,
column: dataColumns[0],
value: 'Becky',
}),
new DataBodyCell<User>({
row: row1,
column: dataColumns[1],
value: 'White',
}),
new DataBodyCell<User>({
row: row1,
column: dataColumns[2],
value: 43,
}),
];
row1.cells = cells1;
const cellForId1 = {
firstName: cells1[0],
lastName: cells1[1],
progress: cells1[2],
};
row1.cellForId = cellForId1;

const expected: DataBodyRow<User>[] = [row0, row1];

[0, 1].forEach((rowIdx) => {
const row = actual[rowIdx];
expect(row).toBeInstanceOf(DataBodyRow);
if (!(row instanceof DataBodyRow)) {
throw new Error('Incorrect BodyRow subtype');
}
expect(row.original).toStrictEqual(expected[rowIdx].original);
expect(row.cells.length).toStrictEqual(expected[rowIdx].cells.length);
actual[rowIdx].cells.forEach((_, colIdx) => {
const cell = actual[rowIdx].cells[colIdx];
expect(cell).toBeInstanceOf(DataBodyCell);
const expectedCell = expected[rowIdx].cells[colIdx];
if (!(cell instanceof DataBodyCell && expectedCell instanceof DataBodyCell)) {
throw new Error('Incorrect instance type');
}
expect(cell.value).toStrictEqual(expectedCell.value);
});
['firstName', 'lastName', 'progress'].forEach((id) => {
const cell = actual[rowIdx].cellForId[id];
expect(cell).toBeInstanceOf(DataBodyCell);
const expectedCell = expected[rowIdx].cellForId[id];
if (!(cell instanceof DataBodyCell && expectedCell instanceof DataBodyCell)) {
throw new Error('Incorrect instance type');
}
expect(cell.value).toStrictEqual(expectedCell.value);
});
});
});

it('transforms data for data columns with custom rowDataId', () => {
const actual = getBodyRows(data, dataColumns, {
rowDataId: ({ firstName, lastName }) => `${firstName}-${lastName}`,
});

const row0 = new DataBodyRow<User>({
id: '0',
dataId: 'Adam-West',
original: data[0],
cells: [],
cellForId: {},
});
const cells0 = [
new DataBodyCell<User>({
row: row0,
column: dataColumns[0],
value: 'Adam',
}),
new DataBodyCell<User>({
row: row0,
column: dataColumns[1],
value: 'West',
}),
new DataBodyCell<User>({
row: row0,
column: dataColumns[2],
value: 75,
}),
];
row0.cells = cells0;
const cellForId0 = {
firstName: cells0[0],
lastName: cells0[1],
progress: cells0[2],
};
row0.cellForId = cellForId0;

const row1 = new DataBodyRow<User>({
id: '1',
dataId: 'Becky-White',
original: data[1],
cells: [],
cellForId: {},
});
const cells1 = [
new DataBodyCell<User>({
row: row1,
Expand Down Expand Up @@ -161,7 +273,13 @@ it('transforms data with display columns', () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const actual = getBodyRows(data, displayColumns as any);

const row0 = new DataBodyRow<User>({ id: '0', original: data[0], cells: [], cellForId: {} });
const row0 = new DataBodyRow<User>({
id: '0',
dataId: '0',
original: data[0],
cells: [],
cellForId: {},
});
const cells0 = [
new DisplayBodyCell<User>({
row: row0,
Expand All @@ -181,7 +299,13 @@ it('transforms data with display columns', () => {
};
row0.cellForId = cellForId0;

const row1 = new DataBodyRow<User>({ id: '1', original: data[1], cells: [], cellForId: {} });
const row1 = new DataBodyRow<User>({
id: '1',
dataId: '1',
original: data[1],
cells: [],
cellForId: {},
});
const cells1 = [
new DisplayBodyCell<User>({
row: row1,
Expand Down
20 changes: 16 additions & 4 deletions src/lib/bodyRows.ts
Expand Up @@ -61,6 +61,7 @@ export type DataBodyRowInit<Item, Plugins extends AnyPlugins = AnyPlugins> = Bod
Item,
Plugins
> & {
dataId: string;
original: Item;
};

Expand All @@ -72,20 +73,22 @@ export class DataBodyRow<Item, Plugins extends AnyPlugins = AnyPlugins> extends
original: Item;
constructor({
id,
dataId,
original,
cells,
cellForId,
depth = 0,
parentRow,
}: DataBodyRowInit<Item, Plugins>) {
super({ id, cells, cellForId, depth, parentRow });
this.dataId = id;
this.dataId = dataId;
this.original = original;
}

clone({ includeCells = false }: BodyRowCloneProps = {}): DataBodyRow<Item, Plugins> {
const clonedRow = new DataBodyRow({
id: this.id,
dataId: this.dataId,
cellForId: this.cellForId,
cells: this.cells,
original: this.original,
Expand Down Expand Up @@ -145,6 +148,10 @@ export class DisplayBodyRow<Item, Plugins extends AnyPlugins = AnyPlugins> exten
}
}

export interface BodyRowsOptions<Item> {
rowDataId?: (item: Item, index: number) => string;
}

/**
* Converts an array of items into an array of table `BodyRow`s based on the column structure.
* @param data The data to display.
Expand All @@ -156,11 +163,14 @@ export const getBodyRows = <Item, Plugins extends AnyPlugins = AnyPlugins>(
/**
* Flat columns before column transformations.
*/
flatColumns: FlatColumn<Item, Plugins>[]
flatColumns: FlatColumn<Item, Plugins>[],
{ rowDataId }: BodyRowsOptions<Item> = {}
): BodyRow<Item, Plugins>[] => {
const rows: BodyRow<Item, Plugins>[] = data.map((item, idx) => {
const id = idx.toString();
return new DataBodyRow({
id: idx.toString(),
id,
dataId: rowDataId !== undefined ? rowDataId(item, idx) : id,
original: item,
cells: [],
cellForId: {},
Expand Down Expand Up @@ -248,12 +258,14 @@ export const getColumnedBodyRows = <Item, Plugins extends AnyPlugins = AnyPlugin
*/
export const getSubRows = <Item, Plugins extends AnyPlugins = AnyPlugins>(
subItems: Item[],
parentRow: BodyRow<Item, Plugins>
parentRow: BodyRow<Item, Plugins>,
{ rowDataId }: BodyRowsOptions<Item> = {}
): BodyRow<Item, Plugins>[] => {
const subRows = subItems.map((item, idx) => {
const id = `${parentRow.id}>${idx}`;
return new DataBodyRow<Item, Plugins>({
id,
dataId: rowDataId !== undefined ? rowDataId(item, idx) : id,
original: item,
cells: [],
cellForId: {},
Expand Down
13 changes: 10 additions & 3 deletions src/lib/createTable.ts
Expand Up @@ -15,7 +15,11 @@ import {
import type { AnyPlugins } from './types/TablePlugin';
import type { ReadOrWritable } from './utils/store';
import { getDuplicates } from './utils/array';
import { createViewModel, type TableViewModel } from './createViewModel';
import {
createViewModel,
type CreateViewModelOptions,
type TableViewModel,
} from './createViewModel';

export class Table<Item, Plugins extends AnyPlugins = AnyPlugins> {
data: ReadOrWritable<Item[]>;
Expand Down Expand Up @@ -58,8 +62,11 @@ export class Table<Item, Plugins extends AnyPlugins = AnyPlugins> {
return new DisplayColumn(def);
}

createViewModel(columns: Column<Item, Plugins>[]): TableViewModel<Item, Plugins> {
return createViewModel(this, columns);
createViewModel(
columns: Column<Item, Plugins>[],
options?: CreateViewModelOptions<Item>
): TableViewModel<Item, Plugins> {
return createViewModel(this, columns, options);
}
}

Expand Down

1 comment on commit 5575005

@vercel
Copy link

@vercel vercel bot commented on 5575005 Jul 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.