@@ -2300,3 +2300,170 @@ describe('ListViewSchema — Airtable Interface parity fields', () => {
23002300 expect ( listView . allowPrinting ) . toBeUndefined ( ) ;
23012301 } ) ;
23022302} ) ;
2303+
2304+ // ============================================================================
2305+ // UI Extension Fields: source, groupBy, typeOptions
2306+ // ============================================================================
2307+
2308+ describe ( 'ListViewSchema — UI Extension fields (source, groupBy, typeOptions)' , ( ) => {
2309+ // --- source ---
2310+
2311+ it ( 'should accept list view with source field' , ( ) => {
2312+ const listView = ListViewSchema . parse ( {
2313+ columns : [ 'name' , 'status' ] ,
2314+ source : 'project_task' ,
2315+ } ) ;
2316+ expect ( listView . source ) . toBe ( 'project_task' ) ;
2317+ } ) ;
2318+
2319+ it ( 'should accept list view without source (optional)' , ( ) => {
2320+ const listView = ListViewSchema . parse ( {
2321+ columns : [ 'name' , 'status' ] ,
2322+ } ) ;
2323+ expect ( listView . source ) . toBeUndefined ( ) ;
2324+ } ) ;
2325+
2326+ it ( 'should accept source alongside data provider' , ( ) => {
2327+ const listView = ListViewSchema . parse ( {
2328+ columns : [ 'name' , 'status' ] ,
2329+ source : 'project_task' ,
2330+ data : { provider : 'object' , object : 'project_task' } ,
2331+ } ) ;
2332+ expect ( listView . source ) . toBe ( 'project_task' ) ;
2333+ expect ( listView . data ?. provider ) . toBe ( 'object' ) ;
2334+ } ) ;
2335+
2336+ // --- groupBy ---
2337+
2338+ it ( 'should accept list view with groupBy field' , ( ) => {
2339+ const listView = ListViewSchema . parse ( {
2340+ columns : [ 'name' , 'status' , 'department' ] ,
2341+ groupBy : 'department' ,
2342+ } ) ;
2343+ expect ( listView . groupBy ) . toBe ( 'department' ) ;
2344+ } ) ;
2345+
2346+ it ( 'should accept list view without groupBy (optional)' , ( ) => {
2347+ const listView = ListViewSchema . parse ( {
2348+ columns : [ 'name' , 'status' ] ,
2349+ } ) ;
2350+ expect ( listView . groupBy ) . toBeUndefined ( ) ;
2351+ } ) ;
2352+
2353+ it ( 'should accept groupBy alongside full grouping config' , ( ) => {
2354+ const listView = ListViewSchema . parse ( {
2355+ columns : [ 'name' , 'status' , 'department' ] ,
2356+ groupBy : 'status' ,
2357+ grouping : {
2358+ fields : [
2359+ { field : 'department' , order : 'asc' } ,
2360+ { field : 'status' , order : 'desc' , collapsed : true } ,
2361+ ] ,
2362+ } ,
2363+ } ) ;
2364+ expect ( listView . groupBy ) . toBe ( 'status' ) ;
2365+ expect ( listView . grouping ?. fields ) . toHaveLength ( 2 ) ;
2366+ } ) ;
2367+
2368+ // --- typeOptions ---
2369+
2370+ it ( 'should accept list view with typeOptions' , ( ) => {
2371+ const listView = ListViewSchema . parse ( {
2372+ columns : [ 'name' , 'status' ] ,
2373+ type : 'calendar' ,
2374+ typeOptions : {
2375+ showWeekends : false ,
2376+ firstDayOfWeek : 1 ,
2377+ } ,
2378+ } ) ;
2379+ expect ( listView . typeOptions ) . toEqual ( {
2380+ showWeekends : false ,
2381+ firstDayOfWeek : 1 ,
2382+ } ) ;
2383+ } ) ;
2384+
2385+ it ( 'should accept list view without typeOptions (optional)' , ( ) => {
2386+ const listView = ListViewSchema . parse ( {
2387+ columns : [ 'name' , 'status' ] ,
2388+ } ) ;
2389+ expect ( listView . typeOptions ) . toBeUndefined ( ) ;
2390+ } ) ;
2391+
2392+ it ( 'should accept typeOptions with nested objects' , ( ) => {
2393+ const listView = ListViewSchema . parse ( {
2394+ columns : [ 'name' , 'status' ] ,
2395+ type : 'kanban' ,
2396+ typeOptions : {
2397+ cardTemplate : 'compact' ,
2398+ showAvatar : true ,
2399+ cardLayout : { maxFields : 4 , showCover : true } ,
2400+ } ,
2401+ } ) ;
2402+ expect ( listView . typeOptions ?. cardTemplate ) . toBe ( 'compact' ) ;
2403+ expect ( listView . typeOptions ?. cardLayout ) . toEqual ( { maxFields : 4 , showCover : true } ) ;
2404+ } ) ;
2405+
2406+ it ( 'should accept typeOptions alongside built-in type config' , ( ) => {
2407+ const listView = ListViewSchema . parse ( {
2408+ columns : [ 'name' , 'status' ] ,
2409+ type : 'kanban' ,
2410+ kanban : {
2411+ groupByField : 'stage' ,
2412+ summarizeField : 'amount' ,
2413+ columns : [ 'name' , 'owner' ] ,
2414+ } ,
2415+ typeOptions : {
2416+ swimlanes : true ,
2417+ wipLimit : 5 ,
2418+ } ,
2419+ } ) ;
2420+ expect ( listView . kanban ?. groupByField ) . toBe ( 'stage' ) ;
2421+ expect ( listView . typeOptions ?. swimlanes ) . toBe ( true ) ;
2422+ expect ( listView . typeOptions ?. wipLimit ) . toBe ( 5 ) ;
2423+ } ) ;
2424+
2425+ it ( 'should accept empty typeOptions' , ( ) => {
2426+ const listView = ListViewSchema . parse ( {
2427+ columns : [ 'name' , 'status' ] ,
2428+ typeOptions : { } ,
2429+ } ) ;
2430+ expect ( listView . typeOptions ) . toEqual ( { } ) ;
2431+ } ) ;
2432+
2433+ // --- Combined: all UI extension fields together ---
2434+
2435+ it ( 'should accept all UI extension fields together' , ( ) => {
2436+ const listView = ListViewSchema . parse ( {
2437+ name : 'task_board' ,
2438+ label : 'Task Board' ,
2439+ description : 'Project task tracking board' ,
2440+ type : 'kanban' ,
2441+ columns : [ 'name' , 'status' , 'assignee' ] ,
2442+ source : 'project_task' ,
2443+ groupBy : 'status' ,
2444+ typeOptions : {
2445+ swimlanes : true ,
2446+ wipLimit : 10 ,
2447+ } ,
2448+ kanban : {
2449+ groupByField : 'status' ,
2450+ summarizeField : 'story_points' ,
2451+ columns : [ 'name' , 'assignee' , 'priority' ] ,
2452+ } ,
2453+ } ) ;
2454+ expect ( listView . name ) . toBe ( 'task_board' ) ;
2455+ expect ( listView . description ) . toBe ( 'Project task tracking board' ) ;
2456+ expect ( listView . source ) . toBe ( 'project_task' ) ;
2457+ expect ( listView . groupBy ) . toBe ( 'status' ) ;
2458+ expect ( listView . typeOptions ?. swimlanes ) . toBe ( true ) ;
2459+ } ) ;
2460+
2461+ it ( 'should maintain backward compatibility — new fields are all optional' , ( ) => {
2462+ const listView = ListViewSchema . parse ( {
2463+ columns : [ 'name' , 'status' ] ,
2464+ } ) ;
2465+ expect ( listView . source ) . toBeUndefined ( ) ;
2466+ expect ( listView . groupBy ) . toBeUndefined ( ) ;
2467+ expect ( listView . typeOptions ) . toBeUndefined ( ) ;
2468+ } ) ;
2469+ } ) ;
0 commit comments