From ef561e7cba172b61f296d4ff11815ec31902fb4a Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 30 Oct 2024 19:21:48 +0500 Subject: [PATCH] feat(Table): customize `header` and `cell` through slots (#2457) Co-authored-by: Benjamin Canac --- .../examples/table/TableSlotsExample.vue | 81 +++++++ docs/content/3.components/table.md | 25 ++- src/runtime/components/Table.vue | 23 +- test/components/Table.spec.ts | 7 +- .../__snapshots__/Table-vue.spec.ts.snap | 208 ++++++++++++++++++ .../__snapshots__/Table.spec.ts.snap | 208 ++++++++++++++++++ 6 files changed, 542 insertions(+), 10 deletions(-) create mode 100644 docs/app/components/content/examples/table/TableSlotsExample.vue diff --git a/docs/app/components/content/examples/table/TableSlotsExample.vue b/docs/app/components/content/examples/table/TableSlotsExample.vue new file mode 100644 index 0000000000..ff2a8c44cf --- /dev/null +++ b/docs/app/components/content/examples/table/TableSlotsExample.vue @@ -0,0 +1,81 @@ + + + diff --git a/docs/content/3.components/table.md b/docs/content/3.components/table.md index e36e21af2f..9348706e82 100644 --- a/docs/content/3.components/table.md +++ b/docs/content/3.components/table.md @@ -80,6 +80,10 @@ Use the `columns` prop as an array of [ColumnDef](https://tanstack.com/table/lat In order to render components or other HTML elements, you will need to use the Vue [`h` function](https://vuejs.org/api/render-function.html#h) inside the `header` and `cell` props. This is different from other components that use slots but allows for more flexibility. +::tip{to="#with-slots"} +You can also use slots to customize the header and data cells of the table. +:: + ::component-example --- prettier: true @@ -92,8 +96,8 @@ highlights: --- :: -::tip -When rendering components with the `h` function, you can utilize the `resolveComponent` function to dynamically resolve and reference components. +::note +When rendering components with `h`, you can either use the `resolveComponent` function or import from `#components`. :: ### Loading @@ -396,6 +400,23 @@ class: '!p-0' --- :: +### With slots + +You can use slots to customize the header and data cells of the table. + +Use the `#-header` slot to customize the header of a column. You will have access to the `column`, `header` and `table` properties in the slot scope. + +Use the `#-cell` slot to customize the cell of a column. You will have access to the `cell`, `column`, `getValue`, `renderValue`, `row`, and `table` properties in the slot scope. + +::component-example +--- +prettier: true +collapse: true +name: 'table-slots-example' +class: '!p-0' +--- +:: + ## API ### Props diff --git a/src/runtime/components/Table.vue b/src/runtime/components/Table.vue index efd239634a..075fe8a603 100644 --- a/src/runtime/components/Table.vue +++ b/src/runtime/components/Table.vue @@ -19,7 +19,9 @@ import type { ExpandedOptions, SortingOptions, RowSelectionOptions, - Updater + Updater, + CellContext, + HeaderContext } from '@tanstack/vue-table' import _appConfig from '#build/app.config' import theme from '#build/ui/table' @@ -87,10 +89,13 @@ export interface TableProps { ui?: Partial } -export interface TableSlots { - expanded(props: { row: Row }): any - empty(props?: {}): any -} +type DynamicHeaderSlots = Record & Record<`${K extends string ? K : never}-header`, (props: HeaderContext) => any> +type DynamicCellSlots = Record & Record<`${K extends string ? K : never}-cell`, (props: CellContext) => any> + +export type TableSlots = { + expanded: (props: { row: Row }) => any + empty: (props?: {}) => any +} & DynamicHeaderSlots & DynamicCellSlots @@ -193,7 +198,9 @@ defineExpose({ :data-pinned="header.column.getIsPinned()" :class="ui.th({ class: [props.ui?.th], pinned: !!header.column.getIsPinned() })" > - + + + @@ -208,7 +215,9 @@ defineExpose({ :data-pinned="cell.column.getIsPinned()" :class="ui.td({ class: [props.ui?.td], pinned: !!cell.column.getIsPinned() })" > - + + + diff --git a/test/components/Table.spec.ts b/test/components/Table.spec.ts index 8d1fe5347f..be799fb67f 100644 --- a/test/components/Table.spec.ts +++ b/test/components/Table.spec.ts @@ -152,7 +152,12 @@ describe('Table', () => { ...loadingColors.map((loadingColor: string) => [`with loading color ${loadingColor}`, { props: { ...props, loading: true, loadingColor } }]), ...loadingAnimations.map((loadingAnimation: string) => [`with loading animation ${loadingAnimation}`, { props: { ...props, loading: true, loadingAnimation } }]), ['with class', { props: { ...props, class: 'absolute' } }], - ['with ui', { props: { ...props, ui: { base: 'table-auto' } } }] + ['with ui', { props: { ...props, ui: { base: 'table-auto' } } }], + // Slots + ['with header slot', { props, slots: { 'id-header': () => 'ID Header slot' } }], + ['with cell slot', { props, slots: { 'id-cell': () => 'ID Cell slot' } }], + ['with expanded slot', { props, slots: { expanded: () => 'Expanded slot' } }], + ['with empty slot', { props, slots: { empty: () => 'Empty slot' } }] ])('renders %s correctly', async (nameOrHtml: string, options: { props?: TableProps, slots?: Partial> }) => { const html = await ComponentRender(nameOrHtml, options, Table) expect(html).toMatchSnapshot() diff --git a/test/components/__snapshots__/Table-vue.spec.ts.snap b/test/components/__snapshots__/Table-vue.spec.ts.snap index 985eed911f..431d327435 100644 --- a/test/components/__snapshots__/Table-vue.spec.ts.snap +++ b/test/components/__snapshots__/Table-vue.spec.ts.snap @@ -1,5 +1,57 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`Table > renders with cell slot correctly 1`] = ` +"
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IdAmountStatusEmail
ID Cell slot316successken99@yahoo.com
ID Cell slot242successAbe45@gmail.com
ID Cell slot837processingMonserrat44@gmail.com
ID Cell slot874successSilas22@gmail.com
ID Cell slot721failedcarmella@hotmail.com
+
" +`; + exports[`Table > renders with class correctly 1`] = ` "
@@ -299,6 +351,162 @@ exports[`Table > renders with data correctly 1`] = ` " `; +exports[`Table > renders with empty slot correctly 1`] = ` +"
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IdAmountStatusEmail
m5gr84i9316successken99@yahoo.com
3u1reuv4242successAbe45@gmail.com
derv1ws0837processingMonserrat44@gmail.com
5kma53ae874successSilas22@gmail.com
bhqecj4p721failedcarmella@hotmail.com
+
" +`; + +exports[`Table > renders with expanded slot correctly 1`] = ` +"
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IdAmountStatusEmail
m5gr84i9316successken99@yahoo.com
3u1reuv4242successAbe45@gmail.com
derv1ws0837processingMonserrat44@gmail.com
5kma53ae874successSilas22@gmail.com
bhqecj4p721failedcarmella@hotmail.com
+
" +`; + +exports[`Table > renders with header slot correctly 1`] = ` +"
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ID Header slotAmountStatusEmail
m5gr84i9316successken99@yahoo.com
3u1reuv4242successAbe45@gmail.com
derv1ws0837processingMonserrat44@gmail.com
5kma53ae874successSilas22@gmail.com
bhqecj4p721failedcarmella@hotmail.com
+
" +`; + exports[`Table > renders with loading animation carousel correctly 1`] = ` "
diff --git a/test/components/__snapshots__/Table.spec.ts.snap b/test/components/__snapshots__/Table.spec.ts.snap index 3a2f372c7f..7579437e8d 100644 --- a/test/components/__snapshots__/Table.spec.ts.snap +++ b/test/components/__snapshots__/Table.spec.ts.snap @@ -1,5 +1,57 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`Table > renders with cell slot correctly 1`] = ` +"
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IdAmountStatusEmail
ID Cell slot316successken99@yahoo.com
ID Cell slot242successAbe45@gmail.com
ID Cell slot837processingMonserrat44@gmail.com
ID Cell slot874successSilas22@gmail.com
ID Cell slot721failedcarmella@hotmail.com
+
" +`; + exports[`Table > renders with class correctly 1`] = ` "
@@ -299,6 +351,162 @@ exports[`Table > renders with data correctly 1`] = ` " `; +exports[`Table > renders with empty slot correctly 1`] = ` +"
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IdAmountStatusEmail
m5gr84i9316successken99@yahoo.com
3u1reuv4242successAbe45@gmail.com
derv1ws0837processingMonserrat44@gmail.com
5kma53ae874successSilas22@gmail.com
bhqecj4p721failedcarmella@hotmail.com
+
" +`; + +exports[`Table > renders with expanded slot correctly 1`] = ` +"
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IdAmountStatusEmail
m5gr84i9316successken99@yahoo.com
3u1reuv4242successAbe45@gmail.com
derv1ws0837processingMonserrat44@gmail.com
5kma53ae874successSilas22@gmail.com
bhqecj4p721failedcarmella@hotmail.com
+
" +`; + +exports[`Table > renders with header slot correctly 1`] = ` +"
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ID Header slotAmountStatusEmail
m5gr84i9316successken99@yahoo.com
3u1reuv4242successAbe45@gmail.com
derv1ws0837processingMonserrat44@gmail.com
5kma53ae874successSilas22@gmail.com
bhqecj4p721failedcarmella@hotmail.com
+
" +`; + exports[`Table > renders with loading animation carousel correctly 1`] = ` "