File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -39,11 +39,15 @@ export const selectOptionsSchema = z
3939
4040/**
4141 * Cross-field rule: a `select` column must declare a non-empty option set;
42- * other types must not carry options. Skipped when `type` is absent (an
43- * options-only update on an existing select column).
42+ * other types must not carry options or `multiple` . Skipped when `type` is
43+ * absent (an options-only update on an existing select column).
4444 */
4545function refineColumnOptions (
46- data : { type ?: ( typeof COLUMN_TYPES ) [ number ] ; options ?: z . infer < typeof selectOptionsSchema > } ,
46+ data : {
47+ type ?: ( typeof COLUMN_TYPES ) [ number ]
48+ options ?: z . infer < typeof selectOptionsSchema >
49+ multiple ?: boolean
50+ } ,
4751 ctx : z . RefinementCtx
4852) : void {
4953 if ( data . type === 'select' ) {
@@ -54,13 +58,25 @@ function refineColumnOptions(
5458 message : 'A select column must define at least one option' ,
5559 } )
5660 }
57- } else if ( data . type !== undefined && data . options && data . options . length > 0 ) {
61+ return
62+ }
63+ if ( data . type === undefined ) return
64+ if ( data . options && data . options . length > 0 ) {
5865 ctx . addIssue ( {
5966 code : 'custom' ,
6067 path : [ 'options' ] ,
6168 message : 'options are only allowed on select columns' ,
6269 } )
6370 }
71+ // `multiple` stored on a non-select column is inert until a later
72+ // convert-to-select inherits it, silently producing a multiselect.
73+ if ( data . multiple ) {
74+ ctx . addIssue ( {
75+ code : 'custom' ,
76+ path : [ 'multiple' ] ,
77+ message : 'multiple is only allowed on select columns' ,
78+ } )
79+ }
6480}
6581
6682/**
Original file line number Diff line number Diff line change @@ -87,6 +87,27 @@ describe('Validation', () => {
8787 }
8888 } )
8989
90+ it ( 'rejects select-only fields on a non-select column' , ( ) => {
91+ // Both are inert on a string column, but `updateColumnType` inherits them
92+ // on a later convert-to-select — options would be silently replaced and
93+ // `multiple` would turn an intended single-select into a multiselect.
94+ const withOptions = validateColumnDefinition ( {
95+ name : 'status' ,
96+ type : 'string' ,
97+ options : [ { id : 'opt_a' , name : 'Open' } ] ,
98+ } )
99+ expect ( withOptions . valid ) . toBe ( false )
100+ expect ( withOptions . errors [ 0 ] ) . toContain ( 'cannot define options' )
101+
102+ const withMultiple = validateColumnDefinition ( {
103+ name : 'status' ,
104+ type : 'string' ,
105+ multiple : true ,
106+ } )
107+ expect ( withMultiple . valid ) . toBe ( false )
108+ expect ( withMultiple . errors [ 0 ] ) . toContain ( 'cannot be multiple' )
109+ } )
110+
90111 it ( 'should reject empty column name' , ( ) => {
91112 const result = validateColumnDefinition ( { name : '' , type : 'string' } )
92113 expect ( result . valid ) . toBe ( false )
Original file line number Diff line number Diff line change @@ -775,8 +775,17 @@ export function validateColumnDefinition(column: ColumnDefinition): ValidationRe
775775 if ( column . unique ) {
776776 errors . push ( `Column "${ column . name } " of type "select" cannot be unique` )
777777 }
778- } else if ( column . options !== undefined ) {
779- errors . push ( `Column "${ column . name } " cannot define options for type "${ column . type } "` )
778+ } else {
779+ if ( column . options !== undefined ) {
780+ errors . push ( `Column "${ column . name } " cannot define options for type "${ column . type } "` )
781+ }
782+ // A stored `multiple` on a non-select column is inert until the column is
783+ // converted, at which point `updateColumnType` inherits it — silently
784+ // turning an intended single-select into a multiselect and rewriting every
785+ // cell as an array.
786+ if ( column . multiple ) {
787+ errors . push ( `Column "${ column . name } " cannot be multiple for type "${ column . type } "` )
788+ }
780789 }
781790
782791 return { valid : errors . length === 0 , errors }
You can’t perform that action at this time.
0 commit comments