diff --git a/packages/api-generator/src/locale/en/VDataTable.json b/packages/api-generator/src/locale/en/VDataTable.json index 2e5bdcdefb6..3953f79ba36 100644 --- a/packages/api-generator/src/locale/en/VDataTable.json +++ b/packages/api-generator/src/locale/en/VDataTable.json @@ -74,6 +74,7 @@ "pagination": "Emits when something changed to the `pagination` which can be provided via the `pagination` prop.", "toggleSelectAll": "Emits when the `select-all` checkbox in table header is clicked. This checkbox is enabled by the **show-select** prop.", "update:expanded": "Emits when the **expanded** property of the **options** prop is updated.", + "update:filteredItems": "Emits i.e. when **searching** and return the filtered items.", "update:groupBy": "Emits when the **group-by** property of the **options** property is updated.", "update:groupDesc": "Emits when the **group-desc** property of the **options** prop is updated.", "update:itemsPerPage": "Emits when the **items-per-page** property of the **options** prop is updated.", @@ -83,6 +84,7 @@ "update:options": "Emits when one of the **options** properties is updated.", "update:page": "Emits when the **page** property of the **options** prop is updated.", "update:sortBy": "Emits when the **sortBy** property of the **options** prop is updated.", - "update:sortDesc": "Emits when the **sort-desc** property of the **options** prop is updated." + "update:sortDesc": "Emits when the **sort-desc** property of the **options** prop is updated.", + "update:sortedItems": "Emits when **sorting** and return items according to its current sorting." } } diff --git a/packages/vuetify/src/components/VDataTable/VDataTable.tsx b/packages/vuetify/src/components/VDataTable/VDataTable.tsx index 830538b1acb..90d191fcede 100644 --- a/packages/vuetify/src/components/VDataTable/VDataTable.tsx +++ b/packages/vuetify/src/components/VDataTable/VDataTable.tsx @@ -124,6 +124,8 @@ export const VDataTable = genericComponent( 'update:groupBy': (value: any) => true, 'update:expanded': (value: any) => true, 'update:currentItems': (value: any) => true, + 'update:sortedItems': (value: any) => true, + 'update:filteredItems': (value: any) => true, }, setup (props, { attrs, slots }) { diff --git a/packages/vuetify/src/components/VDataTable/composables/sort.ts b/packages/vuetify/src/components/VDataTable/composables/sort.ts index 0c9beba1c2f..dc1410af43d 100644 --- a/packages/vuetify/src/components/VDataTable/composables/sort.ts +++ b/packages/vuetify/src/components/VDataTable/composables/sort.ts @@ -3,8 +3,8 @@ import { useLocale } from '@/composables' import { useProxiedModel } from '@/composables/proxiedModel' // Utilities -import { computed, inject, provide, toRef } from 'vue' -import { isEmpty, propsFactory } from '@/util' +import { computed, inject, provide, toRef, watch } from 'vue' +import { getCurrentInstance, isEmpty, propsFactory } from '@/util' // Types import type { InjectionKey, PropType, Ref } from 'vue' @@ -108,6 +108,7 @@ export function useSortedItems ( sortRawFunctions?: Ref | undefined> }, ) { + const vm = getCurrentInstance('userSortedItems') const locale = useLocale() const sortedItems = computed(() => { if (!sortBy.value.length || props.disableSort) return items.value @@ -122,6 +123,10 @@ export function useSortedItems ( }) }) + watch(sortedItems, val => { + vm.emit('update:sortedItems', val) + }) + return { sortedItems } } diff --git a/packages/vuetify/src/composables/__tests__/filter.spec.ts b/packages/vuetify/src/composables/__tests__/filter.spec.ts index 6640d1e9398..74dcf834ec9 100644 --- a/packages/vuetify/src/composables/__tests__/filter.spec.ts +++ b/packages/vuetify/src/composables/__tests__/filter.spec.ts @@ -5,6 +5,7 @@ import { transformItem, transformItems } from '../list-items' // Utilities import { describe, expect, it } from '@jest/globals' import { nextTick, ref } from 'vue' +import { withSetup } from '../../util/withSetup' import { deepEqual } from '@/util' const itemProps = { @@ -186,9 +187,8 @@ describe('filter', () => { ['14', 1], ['foo', 0], ])('should return an array of filtered items from value %s', (text: any, expected: number) => { - const { filteredItems } = useFilter({}, ref(transformItems(itemProps, items)), ref(text)) - - expect(filteredItems.value).toHaveLength(expected) + const [result] = withSetup(() => useFilter({}, ref(transformItems(itemProps, items)), ref(text))) + expect((result as any)?.filteredItems?.value).toHaveLength(expected) }) it('should accept a custom filter function', async () => { @@ -204,19 +204,22 @@ describe('filter', () => { { title: 'fizz' }, { title: 'buzz' }, ])) - const { filteredItems } = useFilter(props, items, query) - expect(filteredItems.value.map(item => item.raw.title)).toEqual(['fizz', 'buzz']) + const [result] = withSetup(() => useFilter(props, items, query)) + + const filteredItems = (result as any)?.filteredItems + + expect(filteredItems.value.map((item: any) => item.raw.title)).toEqual(['fizz', 'buzz']) query.value = 'foo' await nextTick() - expect(filteredItems.value.map(item => item.raw.title)).toEqual(['foo']) + expect(filteredItems.value.map((item: any) => item.raw.title)).toEqual(['foo']) items.value.push(transformItem(itemProps, { title: 'foobar' })) await nextTick() - expect(filteredItems.value.map(item => item.raw.title)).toEqual(['foo', 'foobar']) + expect(filteredItems.value.map((item: any) => item.raw.title)).toEqual(['foo', 'foobar']) }) }) }) diff --git a/packages/vuetify/src/composables/filter.ts b/packages/vuetify/src/composables/filter.ts index 43d83e90237..7853646786c 100644 --- a/packages/vuetify/src/composables/filter.ts +++ b/packages/vuetify/src/composables/filter.ts @@ -3,7 +3,7 @@ // Utilities import { computed, ref, unref, watchEffect } from 'vue' -import { getPropertyFromItem, propsFactory, wrapInArray } from '@/util' +import { getCurrentInstance, getPropertyFromItem, propsFactory, wrapInArray } from '@/util' // Types import type { PropType, Ref } from 'vue' @@ -139,6 +139,7 @@ export function useFilter ( customKeyFilter?: MaybeRef } ) { + const vm = getCurrentInstance('useFilter') const filteredItems: Ref = ref([]) const filteredMatches: Ref>> = ref(new Map()) const transformedItems = computed(() => ( @@ -180,6 +181,8 @@ export function useFilter ( }) filteredItems.value = _filteredItems filteredMatches.value = _filteredMatches + + vm.emit('update:filteredItems', filteredItems.value) }) function getMatches (item: T) { diff --git a/packages/vuetify/src/util/withSetup.ts b/packages/vuetify/src/util/withSetup.ts new file mode 100644 index 00000000000..20f21fbbe60 --- /dev/null +++ b/packages/vuetify/src/util/withSetup.ts @@ -0,0 +1,18 @@ +/* From vuejs.org docs: https://vuejs.org/guide/scaling-up/testing.html#testing-composables */ +// Utilities +import { createApp } from 'vue' + +export function withSetup (composable: any) { + let result + const app = createApp({ + setup () { + result = composable() + // suppress missing template warning + return () => { } + }, + }) + app.mount(document.createElement('div')) + // return the result and the app instance + // for testing provide/unmount + return [result, app] +}