From 8ac10e43a10ea3f966f1bb7cd9d87fdff52c93c9 Mon Sep 17 00:00:00 2001 From: Zhaolin Liang Date: Thu, 11 Jul 2024 00:55:24 +0800 Subject: [PATCH] fix(VDataTable): support groupBy when sorting is disabled (#20047) fixes #20046 Co-authored-by: John Leider --- .../src/components/VDataTable/VDataTable.tsx | 5 +- .../VDataTable/VDataTableServer.tsx | 5 +- .../VDataTable/VDataTableVirtual.tsx | 5 +- .../__tests__/VDataTable.spec.cy.tsx | 99 +++++++++++++++++++ .../VDataTable/composables/group.ts | 10 +- .../components/VDataTable/composables/sort.ts | 3 +- 6 files changed, 116 insertions(+), 11 deletions(-) diff --git a/packages/vuetify/src/components/VDataTable/VDataTable.tsx b/packages/vuetify/src/components/VDataTable/VDataTable.tsx index 830538b1acb..c98c3ba8147 100644 --- a/packages/vuetify/src/components/VDataTable/VDataTable.tsx +++ b/packages/vuetify/src/components/VDataTable/VDataTable.tsx @@ -21,7 +21,7 @@ import { provideDefaults } from '@/composables/defaults' import { makeFilterProps, useFilter } from '@/composables/filter' // Utilities -import { computed, toRef } from 'vue' +import { computed, toRef, toRefs } from 'vue' import { genericComponent, propsFactory, useRender } from '@/util' // Types @@ -130,6 +130,7 @@ export const VDataTable = genericComponent( const { groupBy } = createGroupBy(props) const { sortBy, multiSort, mustSort } = createSort(props) const { page, itemsPerPage } = createPagination(props) + const { disableSort } = toRefs(props) const { columns, @@ -152,7 +153,7 @@ export const VDataTable = genericComponent( }) const { toggleSort } = provideSort({ sortBy, multiSort, mustSort, page }) - const { sortByWithGroups, opened, extractRows, isGroupOpen, toggleGroup } = provideGroupBy({ groupBy, sortBy }) + const { sortByWithGroups, opened, extractRows, isGroupOpen, toggleGroup } = provideGroupBy({ groupBy, sortBy, disableSort }) const { sortedItems } = useSortedItems(props, filteredItems, sortByWithGroups, { transform: item => item.columns, diff --git a/packages/vuetify/src/components/VDataTable/VDataTableServer.tsx b/packages/vuetify/src/components/VDataTable/VDataTableServer.tsx index f3e9c5293aa..13005194dc3 100644 --- a/packages/vuetify/src/components/VDataTable/VDataTableServer.tsx +++ b/packages/vuetify/src/components/VDataTable/VDataTableServer.tsx @@ -18,7 +18,7 @@ import { createSort, provideSort } from './composables/sort' import { provideDefaults } from '@/composables/defaults' // Utilities -import { computed, provide, toRef } from 'vue' +import { computed, provide, toRef, toRefs } from 'vue' import { genericComponent, propsFactory, useRender } from '@/util' // Types @@ -69,6 +69,7 @@ export const VDataTableServer = genericComponent parseInt(props.itemsLength, 10)) const { columns, headers } = createHeaders(props, { @@ -81,7 +82,7 @@ export const VDataTableServer = genericComponent item.columns, diff --git a/packages/vuetify/src/components/VDataTable/__tests__/VDataTable.spec.cy.tsx b/packages/vuetify/src/components/VDataTable/__tests__/VDataTable.spec.cy.tsx index 0cc1a79df2c..cde77da1731 100644 --- a/packages/vuetify/src/components/VDataTable/__tests__/VDataTable.spec.cy.tsx +++ b/packages/vuetify/src/components/VDataTable/__tests__/VDataTable.spec.cy.tsx @@ -14,6 +14,7 @@ const DESSERT_HEADERS = [ { title: 'Carbs (g)', key: 'carbs' }, { title: 'Protein (g)', key: 'protein' }, { title: 'Iron (%)', key: 'iron' }, + { title: 'Group', key: 'group' }, ] const DESSERT_ITEMS = [ @@ -24,6 +25,7 @@ const DESSERT_ITEMS = [ carbs: 24, protein: 4.0, iron: '1%', + group: 1, }, { name: 'Ice cream sandwich', @@ -32,6 +34,7 @@ const DESSERT_ITEMS = [ carbs: 37, protein: 4.3, iron: '1%', + group: 3, }, { name: 'Eclair', @@ -40,6 +43,7 @@ const DESSERT_ITEMS = [ carbs: 23, protein: 6.0, iron: '7%', + group: 2, }, { name: 'Cupcake', @@ -48,6 +52,7 @@ const DESSERT_ITEMS = [ carbs: 67, protein: 4.3, iron: '8%', + group: 2, }, { name: 'Gingerbread', @@ -56,6 +61,7 @@ const DESSERT_ITEMS = [ carbs: 49, protein: 3.9, iron: '16%', + group: 3, }, { name: 'Jelly bean', @@ -64,6 +70,7 @@ const DESSERT_ITEMS = [ carbs: 94, protein: 0.0, iron: '0%', + group: 1, }, { name: 'Lollipop', @@ -72,6 +79,7 @@ const DESSERT_ITEMS = [ carbs: 98, protein: 0, iron: '2%', + group: 2, }, { name: 'Honeycomb', @@ -80,6 +88,7 @@ const DESSERT_ITEMS = [ carbs: 87, protein: 6.5, iron: '45%', + group: 3, }, { name: 'Donut', @@ -88,6 +97,7 @@ const DESSERT_ITEMS = [ carbs: 51, protein: 4.9, iron: '22%', + group: 3, }, { name: 'KitKat', @@ -96,6 +106,7 @@ const DESSERT_ITEMS = [ carbs: 65, protein: 7, iron: '6%', + group: 1, }, ] @@ -356,4 +367,92 @@ describe('VDataTable', () => { cy.get('.v-data-table').find('h3').should('exist') }) }) + + describe('sort', () => { + it('should sort by sortBy', () => { + cy.mount(() => ( + + + + )) + cy.get('thead .v-data-table__td').eq(2).should('have.class', 'v-data-table__th--sorted') + .get('tbody td:nth-child(3)').then(rows => { + const actualFat = Array.from(rows).map(row => { + return Number(row.textContent) + }) + const expectedFat = DESSERT_ITEMS.map(d => d.fat).sort((a, b) => a - b) + expect(actualFat).to.deep.equal(expectedFat) + }) + cy.get('thead .v-data-table__td').eq(2).click() + .get('thead .v-data-table__td').eq(2).should('have.class', 'v-data-table__th--sorted') + .get('tbody td:nth-child(3)').then(rows => { + const actualFat = Array.from(rows).map(row => { + return Number(row.textContent) + }) + const expectedFat = DESSERT_ITEMS.map(d => d.fat).sort((a, b) => b - a) + expect(actualFat).to.deep.equal(expectedFat) + }) + }) + + it('should sort by groupBy and sortBy', () => { + cy.mount(() => ( + + + + )).get('tr.v-data-table-group-header-row .v-data-table__td button + span').then(rows => { + const actualGroup = Array.from(rows).map(row => { + return Number(row.textContent) + }) + const expectedGroup = [...new Set(DESSERT_ITEMS.map(d => d.group))].sort((a, b) => b - a) + expect(actualGroup).to.deep.equal(expectedGroup) + }).get('.v-data-table-group-header-row button').eq(0).click() + .get('.v-data-table__tr td:nth-child(3)').then(rows => { + const actualCalories = Array.from(rows).map(row => { + return Number(row.textContent) + }) + const expectedCalories = DESSERT_ITEMS.filter(d => d.group === 3).map(d => d.calories).sort((a, b) => b - a) + expect(actualCalories).to.deep.equal(expectedCalories) + }) + }) + + // https://github.com/vuetifyjs/vuetify/issues/20046 + it('should sort by groupBy while sort is disabled', () => { + cy.mount(() => ( + + + + )).get('tr.v-data-table-group-header-row .v-data-table__td button + span').then(rows => { + const actualGroup = Array.from(rows).map(row => { + return Number(row.textContent) + }) + const expectedGroup = [...new Set(DESSERT_ITEMS.map(d => d.group))].sort((a, b) => b - a) + expect(actualGroup).to.deep.equal(expectedGroup) + }).get('.v-data-table-group-header-row button').eq(0).click() + .get('.v-data-table__tr td:nth-child(3)').then(rows => { + const actualCalories = Array.from(rows).map(row => { + return Number(row.textContent) + }) + const expectedCalories = DESSERT_ITEMS.filter(d => d.group === 3).map(d => d.calories) + expect(actualCalories).to.deep.equal(expectedCalories) + }) + }) + }) }) diff --git a/packages/vuetify/src/components/VDataTable/composables/group.ts b/packages/vuetify/src/components/VDataTable/composables/group.ts index b66d3b3c14e..7f4ac2be468 100644 --- a/packages/vuetify/src/components/VDataTable/composables/group.ts +++ b/packages/vuetify/src/components/VDataTable/composables/group.ts @@ -51,15 +51,19 @@ export function createGroupBy (props: GroupProps) { return { groupBy } } -export function provideGroupBy (options: { groupBy: Ref, sortBy: Ref }) { - const { groupBy, sortBy } = options +export function provideGroupBy (options: { + groupBy: Ref + sortBy: Ref + disableSort?: Ref +}) { + const { disableSort, groupBy, sortBy } = options const opened = ref(new Set()) const sortByWithGroups = computed(() => { return groupBy.value.map(val => ({ ...val, order: val.order ?? false, - })).concat(sortBy.value) + })).concat(disableSort?.value ? [] : sortBy.value) }) function isGroupOpen (group: Group) { diff --git a/packages/vuetify/src/components/VDataTable/composables/sort.ts b/packages/vuetify/src/components/VDataTable/composables/sort.ts index 0c9beba1c2f..d2e316ddf96 100644 --- a/packages/vuetify/src/components/VDataTable/composables/sort.ts +++ b/packages/vuetify/src/components/VDataTable/composables/sort.ts @@ -98,7 +98,6 @@ export function useSort () { export function useSortedItems ( props: { customKeySort: Record | undefined - disableSort?: Boolean }, items: Ref, sortBy: Ref, @@ -110,7 +109,7 @@ export function useSortedItems ( ) { const locale = useLocale() const sortedItems = computed(() => { - if (!sortBy.value.length || props.disableSort) return items.value + if (!sortBy.value.length) return items.value return sortItems(items.value, sortBy.value, locale.current.value, { transform: options?.transform,