Merge pull request #341 from IBM/datatable

add expandable DataTable
This commit is contained in:
Eric Liu 2020-10-17 13:55:47 -07:00 committed by GitHub
commit ed377a999b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 482 additions and 2767 deletions

View file

@ -773,7 +773,7 @@ interface ComboBoxItem {
| Prop name | Type | Default value | Description |
| :--------------- | :---------------------------------------------------------- | :------------ | :------------------------------------------------------------------------ |
| items | <code>ComboBoxItem[]</code> | -- | Set the combobox items. |
| items | <code>ComboBoxItem[]</code> | `[]` | Set the combobox items. |
| itemToString | <code>(item: ComboBoxItem) => string</code> | -- | Override the display of a combobox item. |
| selectedIndex | <code>number</code> | -- | Set the selected item by value index. |
| value | <code>string</code> | `""` | Specify the selected combobox value. |
@ -981,19 +981,22 @@ import { DataTable } from "carbon-components-svelte";
### Props
| Prop name | Type | Default value | Description |
| :----------- | :-------------------------------------------------- | :------------ | :------------------------------------------------------------------------------------------------- |
| headers | <code>{key: string; value: string;}[]</code> | -- | Specify the data table headers. |
| rows | <code>Object[]</code> | -- | Specify the rows the data table should render. keys defined in `headers` are used for the row ids. |
| :------------- | :-------------------------------------------------- | :------------ | :--------------------------------------------------------------------------------------------------- |
| headers | <code>{key: string; value: string;}[]</code> | `[]` | Specify the data table headers. |
| rows | <code>Object[]</code> | `[]` | Specify the rows the data table should render. keys defined in `headers` are used for the row ids. |
| size | <code>"compact" &#124; "short" &#124; "tall"</code> | -- | Set the size of the data table. |
| title | <code>string</code> | `""` | Specify the title of the data table. |
| description | <code>string</code> | `""` | Specify the description of the data table. |
| zebra | <code>boolean</code> | `false` | Set to `true` to use zebra styles. |
| sortable | <code>boolean</code> | `false` | Set to `true` for the sortable variant. |
| expandable | <code>boolean</code> | `false` | Set to `true` for the expandable variant. Automatically set to `true` if `batchExpansion` is `true`. |
| batchExpansion | <code>boolean</code> | `false` | Set to `true` to enable batch expansion. |
| expandedRowIds | <code>string[]</code> | `[]` | Specify the row ids to be expanded. |
| stickyHeader | <code>boolean</code> | `false` | Set to `true` to enable a sticky header. |
### Slots
- `<slot>...</slot>`
- `<slot name="expanded-row">...</slot>`
### Forwarded events
@ -1001,9 +1004,11 @@ No forwarded events.
### Dispatched events
- `on:click:header--expand`
- `on:click`
- `on:click:header`
- `on:click:row`
- `on:click:row--expand`
- `on:click:cell`
---
@ -1025,7 +1030,7 @@ import { DataTableSkeleton } from "carbon-components-svelte";
| size | <code>"compact" &#124; "short" &#124; "tall"</code> | -- | Set the size of the data table. |
| zebra | <code>boolean</code> | `false` | Set to `true` to apply zebra styles to the datatable rows. |
| showHeader | <code>boolean</code> | `true` | Set to `false` to hide the header. |
| headers | <code>string[]</code> | -- | Set the column headers. If `headers` has one more items, `count` is ignored. |
| headers | <code>string[]</code> | `[]` | Set the column headers. If `headers` has one more items, `count` is ignored. |
| showToolbar | <code>boolean</code> | `true` | Set to `false` to hide the toolbar. |
### Slots
@ -1184,7 +1189,7 @@ interface DropdownItem {
| Prop name | Type | Default value | Description |
| :-------------- | :------------------------------------------ | :------------ | :--------------------------------------------- |
| items | <code>DropdownItem[]</code> | -- | Set the dropdown items. |
| items | <code>DropdownItem[]</code> | `[]` | Set the dropdown items. |
| itemToString | <code>(item: DropdownItem) => string</code> | -- | Override the display of a dropdown item. |
| selectedIndex | <code>number</code> | -- | Specify the selected item index. |
| type | <code>"default" &#124; "inline"</code> | `"default"` | Specify the type of dropdown. |
@ -1302,8 +1307,8 @@ import { FileUploader } from "carbon-components-svelte";
| Prop name | Type | Default value | Description |
| :---------------------- | :----------------------------------------------------------------------------------------- | :--------------------------- | :--------------------------------------------------------------------- |
| status | <code>"uploading" &#124; "edit" &#124; "complete"</code> | `"uploading"` | Specify the file uploader status. |
| accept | <code>string[]</code> | -- | Specify the accepted file types. |
| files | <code>string[]</code> | -- | Obtain the uploaded file names. |
| accept | <code>string[]</code> | `[]` | Specify the accepted file types. |
| files | <code>string[]</code> | `[]` | Obtain the uploaded file names. |
| multiple | <code>boolean</code> | `false` | Set to `true` to allow multiple files. |
| clearFiles (`constant`) | <code>() => any</code> | -- | Override the default behavior of clearing the array of uploaded files. |
| labelDescription | <code>string</code> | `""` | Specify the label description. |
@ -1345,7 +1350,7 @@ import { FileUploaderButton } from "carbon-components-svelte";
| Prop name | Type | Default value | Description |
| :------------------ | :----------------------------------------------------------------------------------------- | :------------ | :-------------------------------------------- |
| accept | <code>string[]</code> | -- | Specify the accepted file types. |
| accept | <code>string[]</code> | `[]` | Specify the accepted file types. |
| multiple | <code>boolean</code> | `false` | Set to `true` to allow multiple files. |
| disabled | <code>boolean</code> | `false` | Set to `true` to disable the input. |
| disableLabelChanges | <code>boolean</code> | `false` | Set to `true` to disable label changes. |
@ -1391,7 +1396,7 @@ type Files = string[];
| Prop name | Type | Default value | Description |
| :------------ | :---------------------------------------- | :------------ | :-------------------------------------------------------------------------------------------------------- |
| accept | <code>string[]</code> | -- | Specify the accepted file types. |
| accept | <code>string[]</code> | `[]` | Specify the accepted file types. |
| multiple | <code>boolean</code> | `false` | Set to `true` to allow multiple files. |
| validateFiles | <code>(files: Files) => Files</code> | -- | Override the default behavior of validating uploaded files. The default behavior does not validate files. |
| labelText | <code>string</code> | `"Add file"` | Specify the label text. |
@ -2688,9 +2693,9 @@ interface MultiSelectItem {
| Prop name | Type | Default value | Description |
| :---------------- | :----------------------------------------------------------------------- | :------------------- | :--------------------------------------------------------------------------------- |
| items | <code>MultiSelectItem[]</code> | -- | Set the multiselect items. |
| items | <code>MultiSelectItem[]</code> | `[]` | Set the multiselect items. |
| itemToString | <code>(item: MultiSelectItem) => string</code> | -- | Override the display of a multiselect item. |
| selectedIds | <code>MultiSelectItemId[]</code> | -- | Set the selected ids. |
| selectedIds | <code>MultiSelectItemId[]</code> | `[]` | Set the selected ids. |
| value | <code>string</code> | `""` | Specify the multiselect value. |
| size | <code>"sm" &#124; "lg" &#124; "xl"</code> | -- | Set the size of the combobox. |
| type | <code>"default" &#124; "inline"</code> | `"default"` | Specify the type of multiselect. |
@ -3073,7 +3078,7 @@ import { Pagination } from "carbon-components-svelte";
| pageInputDisabled | <code>boolean</code> | `false` | Set to `true` to disable the page input. |
| pageSizeInputDisabled | <code>boolean</code> | `false` | Set to `true` to disable the page size input. |
| pageSize | <code>number</code> | `10` | Specify the number of items to display in a page. |
| pageSizes | <code>number[]</code> | -- | Specify the available page sizes. |
| pageSizes | <code>number[]</code> | `[10]` | Specify the available page sizes. |
| pagesUnknown | <code>boolean</code> | `false` | Set to `true` if the number of pages is unknown. |
| pageText | <code>(page: number) => string</code> | -- | Override the page text. |
| pageRangeText | <code>(current: number, total: number) => string</code> | -- | Override the page range text. |

File diff suppressed because it is too large Load diff

View file

@ -55,7 +55,8 @@
<h3 id="props">Props</h3>
<div class="overflow">
{#if component.props.length > 0}
<div class="overflow">
<StructuredList
style="margin-left: calc(-1 * var(--cds-spacing-05)); margin-right: calc(-1 * var(--cds-spacing-05))"
>
@ -106,7 +107,9 @@
</div>
{/each}
</StructuredListCell>
<StructuredListCell><code>{prop[1].value}</code></StructuredListCell>
<StructuredListCell>
<code>{prop[1].value}</code>
</StructuredListCell>
<StructuredListCell>
{#each prop[1].description.split('\n') as line}
<div class="description">{line}.</div>
@ -116,7 +119,8 @@
{/each}
</StructuredListBody>
</StructuredList>
</div>
</div>
{:else}No props.{/if}
<h3 id="slots">Slots</h3>
{#if component.slots.length > 0}
<UnorderedList class="my-layout-01-03">

View file

@ -13,7 +13,6 @@
import { theme } from "../store";
export let component = $page.title;
export let source = "";
metatags.title = $page.title;

View file

@ -388,6 +388,128 @@
]}"
/>
### Expandable
<DataTable expandable
headers="{[
{ key: "name", value: "Name" },
{ key: "protocol", value: "Protocol" },
{ key: "port", value: "Port" },
{ key: "rule", value: "Rule" }
]}"
rows="{[
{
id: "a",
name: "Load Balancer 3",
protocol: "HTTP",
port: 3000,
rule: "Round robin"
},
{
id: "b",
name: "Load Balancer 1",
protocol: "HTTP",
port: 443,
rule: "Round robin"
},
{
id: "c",
name: "Load Balancer 2",
protocol: "HTTP",
port: 80,
rule: "DNS delegation"
},
{
id: "d",
name: "Load Balancer 6",
protocol: "HTTP",
port: 3000,
rule: "Round robin"
},
{
id: "e",
name: "Load Balancer 4",
protocol: "HTTP",
port: 443,
rule: "Round robin"
},
{
id: "f",
name: "Load Balancer 5",
protocol: "HTTP",
port: 80,
rule: "DNS delegation"
},
]}"
>
<div slot="expanded-row" let:row>
<pre>
{JSON.stringify(row, null, 2)}
</pre>
</div>
</DataTable>
### Batch expansion
<DataTable batchExpansion
headers="{[
{ key: "name", value: "Name" },
{ key: "protocol", value: "Protocol" },
{ key: "port", value: "Port" },
{ key: "rule", value: "Rule" }
]}"
rows="{[
{
id: "a",
name: "Load Balancer 3",
protocol: "HTTP",
port: 3000,
rule: "Round robin"
},
{
id: "b",
name: "Load Balancer 1",
protocol: "HTTP",
port: 443,
rule: "Round robin"
},
{
id: "c",
name: "Load Balancer 2",
protocol: "HTTP",
port: 80,
rule: "DNS delegation"
},
{
id: "d",
name: "Load Balancer 6",
protocol: "HTTP",
port: 3000,
rule: "Round robin"
},
{
id: "e",
name: "Load Balancer 4",
protocol: "HTTP",
port: 443,
rule: "Round robin"
},
{
id: "f",
name: "Load Balancer 5",
protocol: "HTTP",
port: 80,
rule: "DNS delegation"
},
]}"
>
<div slot="expanded-row" let:row>
<pre>
{JSON.stringify(row, null, 2)}
</pre>
</div>
</DataTable>
### Skeleton
<DataTableSkeleton />

View file

@ -44,7 +44,10 @@ export function parseComponent(source, hooks) {
let description = null;
if (init != null) {
if (init.type === "ObjectExpression") {
if (
init.type === "ObjectExpression" ||
init.type === "ArrayExpression"
) {
value = source.slice(init.start, init.end).replace(/\n/g, " ");
type = value;
} else {

View file

@ -42,6 +42,25 @@
*/
export let sortable = false;
/**
* Set to `true` for the expandable variant
* Automatically set to `true` if `batchExpansion` is `true`
* @type {boolean} [expandable=false]
*/
export let expandable = false;
/**
* Set to `true` to enable batch expansion
* @type {boolean} [batchExpansion=false]
*/
export let batchExpansion = false;
/**
* Specify the row ids to be expanded
* @type {string[]} [expandedRowIds=[]]
*/
export let expandedRowIds = [];
/**
* Set to `true` to enable a sticky header
* @type {boolean} [stickyHeader=false]
@ -50,6 +69,7 @@
import { createEventDispatcher, setContext } from "svelte";
import { writable, derived } from "svelte/store";
import ChevronRight16 from "carbon-icons-svelte/lib/ChevronRight16";
import Table from "./Table.svelte";
import TableBody from "./TableBody.svelte";
import TableCell from "./TableCell.svelte";
@ -81,6 +101,14 @@
},
});
let expanded = false;
let parentRowId = null;
$: expandedRows = expandedRowIds.reduce(
(a, id) => ({ ...a, [id]: true }),
{}
);
$: if (batchExpansion) expandable = true;
$: tableSortable.set(sortable);
$: headerKeys = headers.map(({ key }) => key);
$: rows = rows.map((row) => ({
@ -109,14 +137,9 @@
});
}
}
$: props = {
headers,
rows,
};
</script>
<slot props="{props}">
<TableContainer title="{title}" description="{description}" {...$$restProps}>
<TableContainer title="{title}" description="{description}" {...$$restProps}>
<Table
zebra="{zebra}"
size="{size}"
@ -125,6 +148,28 @@
>
<TableHead>
<TableRow>
{#if expandable}
<th
scope="col"
class:bx--table-expand="{true}"
data-previous-value="{expanded ? 'collapsed' : undefined}"
>
{#if batchExpansion}
<button
type="button"
class:bx--table-expand__button="{true}"
on:click="{() => {
expanded = !expanded;
expandedRowIds = expanded ? rows.map((row) => row.id) : [];
dispatch('click:header--expand', { expanded });
}}"
>
<ChevronRight16 class="bx--table-expand__svg" />
</button>
{/if}
</th>
{/if}
{#each headers as header, i (header.key)}
<TableHeader
on:click="{() => {
@ -148,11 +193,37 @@
<TableBody>
{#each sorting ? sortedRows : rows as row, i (row.id)}
<TableRow
class="{expandedRows[row.id] ? 'bx--expandable-row' : ''} {expandable ? 'bx--parent-row' : ''} {expandable && parentRowId === row.id ? 'bx--expandable-row--hover' : ''}"
on:click="{() => {
dispatch('click', { row });
dispatch('click:row', row);
}}"
>
{#if expandable}
<TableCell
class="bx--table-expand"
headers="expand"
data-previous-value="{expandedRows[row.id] ? 'collapsed' : undefined}"
>
<button
type="button"
class:bx--table-expand__button="{true}"
aria-label="{expandedRows[row.id] ? 'Collapse current row' : 'Expand current row'}"
on:click="{() => {
const rowExpanded = !!expandedRows[row.id];
expandedRowIds = rowExpanded ? expandedRowIds.filter((id) => id !== row.id) : [...expandedRowIds, row.id];
dispatch('click:row--expand', {
row,
expanded: !rowExpanded,
});
}}"
>
<ChevronRight16 class="bx--table-expand__svg" />
</button>
</TableCell>
{/if}
{#each row.cells as cell, j (cell.key)}
<TableCell
on:click="{() => {
@ -164,8 +235,26 @@
</TableCell>
{/each}
</TableRow>
{#if expandable && expandedRows[row.id]}
<tr
data-child-row
class:bx--expandable-row="{true}"
on:mouseenter="{() => {
parentRowId = row.id;
}}"
on:mouseleave="{() => {
parentRowId = null;
}}"
>
<TableCell colspan="{headers.length + 1}">
<div class:bx--child-row-inner-container="{true}">
<slot name="expanded-row" row="{row}" />
</div>
</TableCell>
</tr>
{/if}
{/each}
</TableBody>
</Table>
</TableContainer>
</slot>
</TableContainer>

View file

@ -5,7 +5,7 @@
*/
export let isSelected = false;
// TODO: include ariaLabel, onExpand, isExpanded, isSelected
// TODO: include ariaLabel, isSelected
</script>
<tr

33
types/index.d.ts vendored
View file

@ -564,6 +564,7 @@ export class ComboBox extends CarbonSvelteComponent {
$$prop_def: {
/**
* Set the combobox items
* @default []
*/
items?: ComboBoxItem[];
@ -784,12 +785,14 @@ export class DataTable extends CarbonSvelteComponent {
$$prop_def: {
/**
* Specify the data table headers
* @default []
*/
headers?: { key: string; value: string }[];
/**
* Specify the rows the data table should render
* keys defined in `headers` are used for the row ids
* @default []
*/
rows?: Object[];
@ -822,6 +825,25 @@ export class DataTable extends CarbonSvelteComponent {
*/
sortable?: boolean;
/**
* Set to `true` for the expandable variant
* Automatically set to `true` if `batchExpansion` is `true`
* @default false
*/
expandable?: boolean;
/**
* Set to `true` to enable batch expansion
* @default false
*/
batchExpansion?: boolean;
/**
* Specify the row ids to be expanded
* @default []
*/
expandedRowIds?: string[];
/**
* Set to `true` to enable a sticky header
* @default false
@ -829,7 +851,7 @@ export class DataTable extends CarbonSvelteComponent {
stickyHeader?: boolean;
};
$$slot_def: { default: { props: any } };
$$slot_def: { "expanded-row": { row: any } };
}
export class DataTableSkeleton extends CarbonSvelteComponent {
@ -866,6 +888,7 @@ export class DataTableSkeleton extends CarbonSvelteComponent {
/**
* Set the column headers
* If `headers` has one more items, `count` is ignored
* @default []
*/
headers?: string[];
@ -1048,6 +1071,7 @@ export class Dropdown extends CarbonSvelteComponent {
$$prop_def: {
/**
* Set the dropdown items
* @default []
*/
items?: DropdownItem[];
@ -1227,11 +1251,13 @@ export class FileUploader extends CarbonSvelteComponent {
/**
* Specify the accepted file types
* @default []
*/
accept?: string[];
/**
* Obtain the uploaded file names
* @default []
*/
files?: string[];
@ -1289,6 +1315,7 @@ export class FileUploaderButton extends CarbonSvelteComponent {
$$prop_def: {
/**
* Specify the accepted file types
* @default []
*/
accept?: string[];
@ -1359,6 +1386,7 @@ export class FileUploaderDropContainer extends CarbonSvelteComponent {
$$prop_def: {
/**
* Specify the accepted file types
* @default []
*/
accept?: string[];
@ -2436,6 +2464,7 @@ export class MultiSelect extends CarbonSvelteComponent {
$$prop_def: {
/**
* Set the multiselect items
* @default []
*/
items?: MultiSelectItem[];
@ -2446,6 +2475,7 @@ export class MultiSelect extends CarbonSvelteComponent {
/**
* Set the selected ids
* @default []
*/
selectedIds?: MultiSelectItemId[];
@ -2996,6 +3026,7 @@ export class Pagination extends CarbonSvelteComponent {
/**
* Specify the available page sizes
* @default [10]
*/
pageSizes?: number[];