Skip to content

Commit

Permalink
fix(VDataTable): support groupBy when sorting is disabled (#20047)
Browse files Browse the repository at this point in the history
fixes #20046

Co-authored-by: John Leider <[email protected]>
  • Loading branch information
lzl0304 and johnleider committed Jul 10, 2024
1 parent 3bb61d9 commit 8ac10e4
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 11 deletions.
5 changes: 3 additions & 2 deletions packages/vuetify/src/components/VDataTable/VDataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -130,6 +130,7 @@ export const VDataTable = genericComponent<new <T extends readonly any[], V>(
const { groupBy } = createGroupBy(props)
const { sortBy, multiSort, mustSort } = createSort(props)
const { page, itemsPerPage } = createPagination(props)
const { disableSort } = toRefs(props)

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

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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -69,6 +69,7 @@ export const VDataTableServer = genericComponent<new <T extends readonly any[],
const { groupBy } = createGroupBy(props)
const { sortBy, multiSort, mustSort } = createSort(props)
const { page, itemsPerPage } = createPagination(props)
const { disableSort } = toRefs(props)
const itemsLength = computed(() => parseInt(props.itemsLength, 10))

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

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

const { opened, isGroupOpen, toggleGroup, extractRows } = provideGroupBy({ groupBy, sortBy })
const { opened, isGroupOpen, toggleGroup, extractRows } = provideGroupBy({ groupBy, sortBy, disableSort })

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { makeFilterProps, useFilter } from '@/composables/filter'
import { makeVirtualProps, useVirtual } from '@/composables/virtual'

// Utilities
import { computed, shallowRef, toRef } from 'vue'
import { computed, shallowRef, toRef, toRefs } from 'vue'
import { convertToUnit, genericComponent, propsFactory, useRender } from '@/util'

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

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

const { toggleSort } = provideSort({ sortBy, multiSort, mustSort })
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand All @@ -24,6 +25,7 @@ const DESSERT_ITEMS = [
carbs: 24,
protein: 4.0,
iron: '1%',
group: 1,
},
{
name: 'Ice cream sandwich',
Expand All @@ -32,6 +34,7 @@ const DESSERT_ITEMS = [
carbs: 37,
protein: 4.3,
iron: '1%',
group: 3,
},
{
name: 'Eclair',
Expand All @@ -40,6 +43,7 @@ const DESSERT_ITEMS = [
carbs: 23,
protein: 6.0,
iron: '7%',
group: 2,
},
{
name: 'Cupcake',
Expand All @@ -48,6 +52,7 @@ const DESSERT_ITEMS = [
carbs: 67,
protein: 4.3,
iron: '8%',
group: 2,
},
{
name: 'Gingerbread',
Expand All @@ -56,6 +61,7 @@ const DESSERT_ITEMS = [
carbs: 49,
protein: 3.9,
iron: '16%',
group: 3,
},
{
name: 'Jelly bean',
Expand All @@ -64,6 +70,7 @@ const DESSERT_ITEMS = [
carbs: 94,
protein: 0.0,
iron: '0%',
group: 1,
},
{
name: 'Lollipop',
Expand All @@ -72,6 +79,7 @@ const DESSERT_ITEMS = [
carbs: 98,
protein: 0,
iron: '2%',
group: 2,
},
{
name: 'Honeycomb',
Expand All @@ -80,6 +88,7 @@ const DESSERT_ITEMS = [
carbs: 87,
protein: 6.5,
iron: '45%',
group: 3,
},
{
name: 'Donut',
Expand All @@ -88,6 +97,7 @@ const DESSERT_ITEMS = [
carbs: 51,
protein: 4.9,
iron: '22%',
group: 3,
},
{
name: 'KitKat',
Expand All @@ -96,6 +106,7 @@ const DESSERT_ITEMS = [
carbs: 65,
protein: 7,
iron: '6%',
group: 1,
},
]

Expand Down Expand Up @@ -356,4 +367,92 @@ describe('VDataTable', () => {
cy.get('.v-data-table').find('h3').should('exist')
})
})

describe('sort', () => {
it('should sort by sortBy', () => {
cy.mount(() => (
<Application>
<VDataTable
items={ DESSERT_ITEMS }
headers={ DESSERT_HEADERS }
itemsPerPage={ 10 }
sortBy={[{ key: 'fat', order: 'asc' }]}
/>
</Application>
))
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(() => (
<Application>
<VDataTable
items={ DESSERT_ITEMS }
headers={ DESSERT_HEADERS }
itemsPerPage={ 10 }
groupBy={[{ key: 'group', order: 'desc' }]}
sortBy={[{ key: 'calories', order: 'desc' }]}
/>
</Application>
)).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(() => (
<Application>
<VDataTable
items={ DESSERT_ITEMS }
headers={ DESSERT_HEADERS }
itemsPerPage={ 10 }
groupBy={[{ key: 'group', order: 'desc' }]}
sortBy={[{ key: 'calories', order: 'desc' }]}
disableSort
/>
</Application>
)).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)
})
})
})
})
10 changes: 7 additions & 3 deletions packages/vuetify/src/components/VDataTable/composables/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,19 @@ export function createGroupBy (props: GroupProps) {
return { groupBy }
}

export function provideGroupBy (options: { groupBy: Ref<readonly SortItem[]>, sortBy: Ref<readonly SortItem[]> }) {
const { groupBy, sortBy } = options
export function provideGroupBy (options: {
groupBy: Ref<readonly SortItem[]>
sortBy: Ref<readonly SortItem[]>
disableSort?: Ref<boolean>
}) {
const { disableSort, groupBy, sortBy } = options
const opened = ref(new Set<string>())

const sortByWithGroups = computed(() => {
return groupBy.value.map<SortItem>(val => ({
...val,
order: val.order ?? false,
})).concat(sortBy.value)
})).concat(disableSort?.value ? [] : sortBy.value)
})

function isGroupOpen (group: Group) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ export function useSort () {
export function useSortedItems<T extends InternalItem> (
props: {
customKeySort: Record<string, DataTableCompareFunction> | undefined
disableSort?: Boolean
},
items: Ref<T[]>,
sortBy: Ref<readonly SortItem[]>,
Expand All @@ -110,7 +109,7 @@ export function useSortedItems<T extends InternalItem> (
) {
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,
Expand Down

0 comments on commit 8ac10e4

Please sign in to comment.