Skip to content

Commit 8ac10e4

Browse files
lzl0304johnleider
andauthored
fix(VDataTable): support groupBy when sorting is disabled (#20047)
fixes #20046 Co-authored-by: John Leider <[email protected]>
1 parent 3bb61d9 commit 8ac10e4

File tree

6 files changed

+116
-11
lines changed

6 files changed

+116
-11
lines changed

packages/vuetify/src/components/VDataTable/VDataTable.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { provideDefaults } from '@/composables/defaults'
2121
import { makeFilterProps, useFilter } from '@/composables/filter'
2222

2323
// Utilities
24-
import { computed, toRef } from 'vue'
24+
import { computed, toRef, toRefs } from 'vue'
2525
import { genericComponent, propsFactory, useRender } from '@/util'
2626

2727
// Types
@@ -130,6 +130,7 @@ export const VDataTable = genericComponent<new <T extends readonly any[], V>(
130130
const { groupBy } = createGroupBy(props)
131131
const { sortBy, multiSort, mustSort } = createSort(props)
132132
const { page, itemsPerPage } = createPagination(props)
133+
const { disableSort } = toRefs(props)
133134

134135
const {
135136
columns,
@@ -152,7 +153,7 @@ export const VDataTable = genericComponent<new <T extends readonly any[], V>(
152153
})
153154

154155
const { toggleSort } = provideSort({ sortBy, multiSort, mustSort, page })
155-
const { sortByWithGroups, opened, extractRows, isGroupOpen, toggleGroup } = provideGroupBy({ groupBy, sortBy })
156+
const { sortByWithGroups, opened, extractRows, isGroupOpen, toggleGroup } = provideGroupBy({ groupBy, sortBy, disableSort })
156157

157158
const { sortedItems } = useSortedItems(props, filteredItems, sortByWithGroups, {
158159
transform: item => item.columns,

packages/vuetify/src/components/VDataTable/VDataTableServer.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { createSort, provideSort } from './composables/sort'
1818
import { provideDefaults } from '@/composables/defaults'
1919

2020
// Utilities
21-
import { computed, provide, toRef } from 'vue'
21+
import { computed, provide, toRef, toRefs } from 'vue'
2222
import { genericComponent, propsFactory, useRender } from '@/util'
2323

2424
// Types
@@ -69,6 +69,7 @@ export const VDataTableServer = genericComponent<new <T extends readonly any[],
6969
const { groupBy } = createGroupBy(props)
7070
const { sortBy, multiSort, mustSort } = createSort(props)
7171
const { page, itemsPerPage } = createPagination(props)
72+
const { disableSort } = toRefs(props)
7273
const itemsLength = computed(() => parseInt(props.itemsLength, 10))
7374

7475
const { columns, headers } = createHeaders(props, {
@@ -81,7 +82,7 @@ export const VDataTableServer = genericComponent<new <T extends readonly any[],
8182

8283
const { toggleSort } = provideSort({ sortBy, multiSort, mustSort, page })
8384

84-
const { opened, isGroupOpen, toggleGroup, extractRows } = provideGroupBy({ groupBy, sortBy })
85+
const { opened, isGroupOpen, toggleGroup, extractRows } = provideGroupBy({ groupBy, sortBy, disableSort })
8586

8687
const { pageCount, setItemsPerPage } = providePagination({ page, itemsPerPage, itemsLength })
8788

packages/vuetify/src/components/VDataTable/VDataTableVirtual.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { makeFilterProps, useFilter } from '@/composables/filter'
1919
import { makeVirtualProps, useVirtual } from '@/composables/virtual'
2020

2121
// Utilities
22-
import { computed, shallowRef, toRef } from 'vue'
22+
import { computed, shallowRef, toRef, toRefs } from 'vue'
2323
import { convertToUnit, genericComponent, propsFactory, useRender } from '@/util'
2424

2525
// Types
@@ -85,6 +85,7 @@ export const VDataTableVirtual = genericComponent<new <T extends readonly any[],
8585
setup (props, { attrs, slots }) {
8686
const { groupBy } = createGroupBy(props)
8787
const { sortBy, multiSort, mustSort } = createSort(props)
88+
const { disableSort } = toRefs(props)
8889

8990
const {
9091
columns,
@@ -106,7 +107,7 @@ export const VDataTableVirtual = genericComponent<new <T extends readonly any[],
106107
})
107108

108109
const { toggleSort } = provideSort({ sortBy, multiSort, mustSort })
109-
const { sortByWithGroups, opened, extractRows, isGroupOpen, toggleGroup } = provideGroupBy({ groupBy, sortBy })
110+
const { sortByWithGroups, opened, extractRows, isGroupOpen, toggleGroup } = provideGroupBy({ groupBy, sortBy, disableSort })
110111

111112
const { sortedItems } = useSortedItems(props, filteredItems, sortByWithGroups, {
112113
transform: item => item.columns,

packages/vuetify/src/components/VDataTable/__tests__/VDataTable.spec.cy.tsx

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const DESSERT_HEADERS = [
1414
{ title: 'Carbs (g)', key: 'carbs' },
1515
{ title: 'Protein (g)', key: 'protein' },
1616
{ title: 'Iron (%)', key: 'iron' },
17+
{ title: 'Group', key: 'group' },
1718
]
1819

1920
const DESSERT_ITEMS = [
@@ -24,6 +25,7 @@ const DESSERT_ITEMS = [
2425
carbs: 24,
2526
protein: 4.0,
2627
iron: '1%',
28+
group: 1,
2729
},
2830
{
2931
name: 'Ice cream sandwich',
@@ -32,6 +34,7 @@ const DESSERT_ITEMS = [
3234
carbs: 37,
3335
protein: 4.3,
3436
iron: '1%',
37+
group: 3,
3538
},
3639
{
3740
name: 'Eclair',
@@ -40,6 +43,7 @@ const DESSERT_ITEMS = [
4043
carbs: 23,
4144
protein: 6.0,
4245
iron: '7%',
46+
group: 2,
4347
},
4448
{
4549
name: 'Cupcake',
@@ -48,6 +52,7 @@ const DESSERT_ITEMS = [
4852
carbs: 67,
4953
protein: 4.3,
5054
iron: '8%',
55+
group: 2,
5156
},
5257
{
5358
name: 'Gingerbread',
@@ -56,6 +61,7 @@ const DESSERT_ITEMS = [
5661
carbs: 49,
5762
protein: 3.9,
5863
iron: '16%',
64+
group: 3,
5965
},
6066
{
6167
name: 'Jelly bean',
@@ -64,6 +70,7 @@ const DESSERT_ITEMS = [
6470
carbs: 94,
6571
protein: 0.0,
6672
iron: '0%',
73+
group: 1,
6774
},
6875
{
6976
name: 'Lollipop',
@@ -72,6 +79,7 @@ const DESSERT_ITEMS = [
7279
carbs: 98,
7380
protein: 0,
7481
iron: '2%',
82+
group: 2,
7583
},
7684
{
7785
name: 'Honeycomb',
@@ -80,6 +88,7 @@ const DESSERT_ITEMS = [
8088
carbs: 87,
8189
protein: 6.5,
8290
iron: '45%',
91+
group: 3,
8392
},
8493
{
8594
name: 'Donut',
@@ -88,6 +97,7 @@ const DESSERT_ITEMS = [
8897
carbs: 51,
8998
protein: 4.9,
9099
iron: '22%',
100+
group: 3,
91101
},
92102
{
93103
name: 'KitKat',
@@ -96,6 +106,7 @@ const DESSERT_ITEMS = [
96106
carbs: 65,
97107
protein: 7,
98108
iron: '6%',
109+
group: 1,
99110
},
100111
]
101112

@@ -356,4 +367,92 @@ describe('VDataTable', () => {
356367
cy.get('.v-data-table').find('h3').should('exist')
357368
})
358369
})
370+
371+
describe('sort', () => {
372+
it('should sort by sortBy', () => {
373+
cy.mount(() => (
374+
<Application>
375+
<VDataTable
376+
items={ DESSERT_ITEMS }
377+
headers={ DESSERT_HEADERS }
378+
itemsPerPage={ 10 }
379+
sortBy={[{ key: 'fat', order: 'asc' }]}
380+
/>
381+
</Application>
382+
))
383+
cy.get('thead .v-data-table__td').eq(2).should('have.class', 'v-data-table__th--sorted')
384+
.get('tbody td:nth-child(3)').then(rows => {
385+
const actualFat = Array.from(rows).map(row => {
386+
return Number(row.textContent)
387+
})
388+
const expectedFat = DESSERT_ITEMS.map(d => d.fat).sort((a, b) => a - b)
389+
expect(actualFat).to.deep.equal(expectedFat)
390+
})
391+
cy.get('thead .v-data-table__td').eq(2).click()
392+
.get('thead .v-data-table__td').eq(2).should('have.class', 'v-data-table__th--sorted')
393+
.get('tbody td:nth-child(3)').then(rows => {
394+
const actualFat = Array.from(rows).map(row => {
395+
return Number(row.textContent)
396+
})
397+
const expectedFat = DESSERT_ITEMS.map(d => d.fat).sort((a, b) => b - a)
398+
expect(actualFat).to.deep.equal(expectedFat)
399+
})
400+
})
401+
402+
it('should sort by groupBy and sortBy', () => {
403+
cy.mount(() => (
404+
<Application>
405+
<VDataTable
406+
items={ DESSERT_ITEMS }
407+
headers={ DESSERT_HEADERS }
408+
itemsPerPage={ 10 }
409+
groupBy={[{ key: 'group', order: 'desc' }]}
410+
sortBy={[{ key: 'calories', order: 'desc' }]}
411+
/>
412+
</Application>
413+
)).get('tr.v-data-table-group-header-row .v-data-table__td button + span').then(rows => {
414+
const actualGroup = Array.from(rows).map(row => {
415+
return Number(row.textContent)
416+
})
417+
const expectedGroup = [...new Set(DESSERT_ITEMS.map(d => d.group))].sort((a, b) => b - a)
418+
expect(actualGroup).to.deep.equal(expectedGroup)
419+
}).get('.v-data-table-group-header-row button').eq(0).click()
420+
.get('.v-data-table__tr td:nth-child(3)').then(rows => {
421+
const actualCalories = Array.from(rows).map(row => {
422+
return Number(row.textContent)
423+
})
424+
const expectedCalories = DESSERT_ITEMS.filter(d => d.group === 3).map(d => d.calories).sort((a, b) => b - a)
425+
expect(actualCalories).to.deep.equal(expectedCalories)
426+
})
427+
})
428+
429+
// https://github.com/vuetifyjs/vuetify/issues/20046
430+
it('should sort by groupBy while sort is disabled', () => {
431+
cy.mount(() => (
432+
<Application>
433+
<VDataTable
434+
items={ DESSERT_ITEMS }
435+
headers={ DESSERT_HEADERS }
436+
itemsPerPage={ 10 }
437+
groupBy={[{ key: 'group', order: 'desc' }]}
438+
sortBy={[{ key: 'calories', order: 'desc' }]}
439+
disableSort
440+
/>
441+
</Application>
442+
)).get('tr.v-data-table-group-header-row .v-data-table__td button + span').then(rows => {
443+
const actualGroup = Array.from(rows).map(row => {
444+
return Number(row.textContent)
445+
})
446+
const expectedGroup = [...new Set(DESSERT_ITEMS.map(d => d.group))].sort((a, b) => b - a)
447+
expect(actualGroup).to.deep.equal(expectedGroup)
448+
}).get('.v-data-table-group-header-row button').eq(0).click()
449+
.get('.v-data-table__tr td:nth-child(3)').then(rows => {
450+
const actualCalories = Array.from(rows).map(row => {
451+
return Number(row.textContent)
452+
})
453+
const expectedCalories = DESSERT_ITEMS.filter(d => d.group === 3).map(d => d.calories)
454+
expect(actualCalories).to.deep.equal(expectedCalories)
455+
})
456+
})
457+
})
359458
})

packages/vuetify/src/components/VDataTable/composables/group.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,19 @@ export function createGroupBy (props: GroupProps) {
5151
return { groupBy }
5252
}
5353

54-
export function provideGroupBy (options: { groupBy: Ref<readonly SortItem[]>, sortBy: Ref<readonly SortItem[]> }) {
55-
const { groupBy, sortBy } = options
54+
export function provideGroupBy (options: {
55+
groupBy: Ref<readonly SortItem[]>
56+
sortBy: Ref<readonly SortItem[]>
57+
disableSort?: Ref<boolean>
58+
}) {
59+
const { disableSort, groupBy, sortBy } = options
5660
const opened = ref(new Set<string>())
5761

5862
const sortByWithGroups = computed(() => {
5963
return groupBy.value.map<SortItem>(val => ({
6064
...val,
6165
order: val.order ?? false,
62-
})).concat(sortBy.value)
66+
})).concat(disableSort?.value ? [] : sortBy.value)
6367
})
6468

6569
function isGroupOpen (group: Group) {

packages/vuetify/src/components/VDataTable/composables/sort.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ export function useSort () {
9898
export function useSortedItems<T extends InternalItem> (
9999
props: {
100100
customKeySort: Record<string, DataTableCompareFunction> | undefined
101-
disableSort?: Boolean
102101
},
103102
items: Ref<T[]>,
104103
sortBy: Ref<readonly SortItem[]>,
@@ -110,7 +109,7 @@ export function useSortedItems<T extends InternalItem> (
110109
) {
111110
const locale = useLocale()
112111
const sortedItems = computed(() => {
113-
if (!sortBy.value.length || props.disableSort) return items.value
112+
if (!sortBy.value.length) return items.value
114113

115114
return sortItems(items.value, sortBy.value, locale.current.value, {
116115
transform: options?.transform,

0 commit comments

Comments
 (0)