1- import { flushPromises , getWrapper , mountApp } from "@test/util" ;
1+ import { flushPromises , getWrapper , mockEndpoint , mountApp } from "@test/util" ;
22import { watch } from "vue" ;
33import { CListFilters } from ".." ;
4- import { ComplexModelListViewModel } from "@test-targets/viewmodels.g" ;
4+ import {
5+ ComplexModelListViewModel ,
6+ DateOnlyPkListViewModel ,
7+ } from "@test-targets/viewmodels.g" ;
8+ import { DateOnlyPk } from "@test-targets/models.g" ;
59
610describe ( "CListFilters" , ( ) => {
711 function setupListAndWatcher ( initialFilter ?: Record < string , any > ) {
@@ -25,23 +29,27 @@ describe("CListFilters", () => {
2529 return wrapper ;
2630 }
2731
28- async function openNameFilter ( wrapper : any ) {
32+ async function openPropertyFilter ( wrapper : any , propertyName : string ) {
2933 // Open the filters menu by clicking the filter button
3034 const filterButton = wrapper . find ( ".c-list-filters" ) ;
3135 await filterButton . trigger ( "click" ) ;
3236 await flushPromises ( ) ;
3337
34- // Find and click on the filter button for the "Name" property
35- const nameFilterButton = getWrapper ( ".v-overlay__content" )
38+ // Find and click on the filter button for the specified property
39+ const propFilterButton = getWrapper ( ".v-overlay__content" )
3640 . findAll ( ".v-list-item" )
37- . find ( ( item ) => item . text ( ) . includes ( "Name" ) ) !
41+ . find ( ( item ) => item . text ( ) . includes ( propertyName ) ) !
3842 . find ( ".fa-filter" )
3943 . element . closest ( "button" ) ;
40- expect ( nameFilterButton ) . toBeTruthy ( ) ;
44+ expect ( propFilterButton ) . toBeTruthy ( ) ;
4145
42- nameFilterButton ! . click ( ) ;
46+ propFilterButton ! . click ( ) ;
4347 await flushPromises ( ) ;
4448 }
49+
50+ async function openNameFilter ( wrapper : any ) {
51+ return openPropertyFilter ( wrapper , "Name" ) ;
52+ }
4553 test ( "doesn't mutate list.$params without user interaction" , async ( ) => {
4654 // There was a bug where c-list-filters was initializing list.$params.filters
4755 // (if it wasn't set) on mount, which was then incorrectly triggering list autoload.
@@ -144,4 +152,83 @@ describe("CListFilters", () => {
144152 expect ( watchTracker ) . toBeCalledTimes ( 1 ) ;
145153 expect ( list . $filter . name ) . toBe ( "" ) ;
146154 } ) ;
155+
156+ test ( "supports multiselect filtering for DateOnly primary keys" , async ( ) => {
157+ // DateOnly primary keys support multiselect (comma-separated values) filtering
158+
159+ const items = [
160+ new DateOnlyPk ( { dateOnlyPkId : new Date ( 2024 , 0 , 15 ) , name : "Item 1" } ) ,
161+ new DateOnlyPk ( { dateOnlyPkId : new Date ( 2024 , 1 , 20 ) , name : "Item 2" } ) ,
162+ new DateOnlyPk ( { dateOnlyPkId : new Date ( 2024 , 2 , 25 ) , name : "Item 3" } ) ,
163+ ] ;
164+
165+ // Mock the DateOnlyPk list endpoint
166+ mockEndpoint ( "/DateOnlyPk/list" , ( ) => ( {
167+ wasSuccessful : true ,
168+ list : items ,
169+ } ) ) ;
170+
171+ const list = new DateOnlyPkListViewModel ( ) ;
172+ const watchTracker = vitest . fn ( ) ;
173+ watch ( ( ) => list . $params , watchTracker , { deep : true } ) ;
174+
175+ // Mount the component
176+ const wrapper = mountApp ( ( ) => (
177+ < CListFilters list = { list } columnSelection > </ CListFilters >
178+ ) ) ;
179+ await flushPromises ( ) ;
180+ expect ( watchTracker ) . toBeCalledTimes ( 0 ) ;
181+
182+ // Open the filter for the Date Only Pk Id property
183+ await openPropertyFilter ( wrapper , "Date Only Pk Id" ) ;
184+
185+ // Verify that the filter menu shows a c-select component (for multiselect)
186+ const filterMenu = getWrapper ( ".c-list-filter--prop-menu" ) ;
187+ expect ( filterMenu . exists ( ) ) . toBe ( true ) ;
188+
189+ // Find the c-select component within the filter
190+ const selectComponent = filterMenu . find ( ".c-select" ) ;
191+ expect ( selectComponent . exists ( ) ) . toBe ( true ) ;
192+
193+ // Actually select multiple date values through the UI by opening the c-select menu
194+ const selectInput = selectComponent . find ( "input" ) ;
195+ await selectInput . trigger ( "focus" ) ;
196+ await selectInput . trigger ( "click" ) ;
197+
198+ // Find all overlays and get the last one (which should be the c-select menu)
199+ const overlays = getWrapper ( "body" ) . findAll ( ".v-overlay__content" ) ;
200+ const selectMenu = overlays . at ( - 1 ) ! ;
201+
202+ // The menu should show the available DateOnlyPk items by their display name
203+ expect ( selectMenu . text ( ) ) . toContain ( "Item 1" ) ;
204+ expect ( selectMenu . text ( ) ) . toContain ( "Item 2" ) ;
205+ expect ( selectMenu . text ( ) ) . toContain ( "Item 3" ) ;
206+
207+ // Click on the list items to select them
208+ const listItems = selectMenu . findAll ( ".v-list-item" ) ;
209+
210+ // Select Item 1
211+ await listItems . find ( ( i ) => i . text ( ) . includes ( "Item 1" ) ) ! . trigger ( "click" ) ;
212+ expect ( list . $filter . dateOnlyPkId ) . toBe ( "2024-01-15" ) ;
213+
214+ // Select Item 2
215+ await listItems . find ( ( i ) => i . text ( ) . includes ( "Item 2" ) ) ! . trigger ( "click" ) ;
216+ expect ( list . $filter . dateOnlyPkId ) . toBe ( "2024-01-15,2024-02-20" ) ;
217+
218+ // Select Item 3
219+ await listItems . find ( ( i ) => i . text ( ) . includes ( "Item 3" ) ) ! . trigger ( "click" ) ;
220+ expect ( list . $filter . dateOnlyPkId ) . toBe ( "2024-01-15,2024-02-20,2024-03-25" ) ;
221+
222+ // Deselect Item 2 (middle item)
223+ await listItems . find ( ( i ) => i . text ( ) . includes ( "Item 2" ) ) ! . trigger ( "click" ) ;
224+ expect ( list . $filter . dateOnlyPkId ) . toBe ( "2024-01-15,2024-03-25" ) ;
225+
226+ // Deselect Item 1 (first item)
227+ await listItems . find ( ( i ) => i . text ( ) . includes ( "Item 1" ) ) ! . trigger ( "click" ) ;
228+ expect ( list . $filter . dateOnlyPkId ) . toBe ( "2024-03-25" ) ;
229+
230+ // Deselect Item 3 (last remaining item)
231+ await listItems . find ( ( i ) => i . text ( ) . includes ( "Item 3" ) ) ! . trigger ( "click" ) ;
232+ expect ( list . $filter . dateOnlyPkId ) . toBe ( "" ) ;
233+ } ) ;
147234} ) ;
0 commit comments