@@ -136,6 +136,218 @@ describe("Dropdown", () => {
136136 expect ( computedStyle . width ) . toBe ( "300px" ) ;
137137 } ) ;
138138 } ) ;
139+
140+ it ( "uses maxWidth value as width when width prop not supplied" , async ( ) => {
141+ const Component = ( ) => (
142+ < GoabDropdown
143+ name = "favcolor"
144+ testId = "favcolor-maxonly"
145+ maxWidth = "320px"
146+ onChange = { noop }
147+ >
148+ < GoabDropdownItem label = "Red" value = "red" />
149+ < GoabDropdownItem label = "Blue" value = "blue" />
150+ < GoabDropdownItem label = "Green" value = "green" />
151+ </ GoabDropdown >
152+ ) ;
153+
154+ const result = render ( < Component /> ) ;
155+ const dropdown = result . getByTestId ( "favcolor-maxonly" ) ;
156+
157+ await vi . waitFor ( ( ) => {
158+ const styleAttr = dropdown . element ( ) . getAttribute ( "style" ) || "" ;
159+ // internal logic sets --width to maxWidth when width not provided
160+ expect ( styleAttr ) . toContain ( "--width: 320px" ) ;
161+ const computedStyle = window . getComputedStyle ( dropdown . element ( ) ) ;
162+ expect ( computedStyle . width ) . toBe ( "320px" ) ;
163+ } ) ;
164+ } ) ;
165+
166+ it ( "ignores maxwidth when width prop is provided" , async ( ) => {
167+ const Component = ( ) => (
168+ < GoabDropdown
169+ name = "favcolor"
170+ testId = "favcolor-width-wins"
171+ width = "800px"
172+ maxWidth = "320px"
173+ onChange = { noop }
174+ >
175+ < GoabDropdownItem label = "Red" value = "red" />
176+ < GoabDropdownItem label = "Blue" value = "blue" />
177+ < GoabDropdownItem label = "Green" value = "green" />
178+ </ GoabDropdown >
179+ ) ;
180+
181+ const result = render ( < Component /> ) ;
182+ const dropdown = result . getByTestId ( "favcolor-width-wins" ) ;
183+ await vi . waitFor ( ( ) => {
184+ const styleAttr = dropdown . element ( ) . getAttribute ( "style" ) || "" ;
185+ expect ( styleAttr ) . toContain ( "--width: 800px" ) ;
186+ const computedStyle = window . getComputedStyle ( dropdown . element ( ) ) ;
187+ expect ( parseFloat ( computedStyle . width ) ) . toBeGreaterThan ( 320 ) ;
188+ } ) ;
189+ } ) ;
190+
191+ it ( "caps natural width with percentage maxWidth (container-controlled)" , async ( ) => {
192+ const Component = ( ) => (
193+ < div style = { { width : "600px" } } data-testid = "container-600" >
194+ < GoabDropdown
195+ name = "favcolor"
196+ testId = "percentage-maxwidth-dropdown"
197+ maxWidth = "50%"
198+ onChange = { noop }
199+ >
200+ < GoabDropdownItem
201+ label = "Extremely Long Option Label To Grow Width"
202+ value = "long"
203+ />
204+ < GoabDropdownItem label = "Blue" value = "blue" />
205+ < GoabDropdownItem label = "Green" value = "green" />
206+ </ GoabDropdown >
207+ </ div >
208+ ) ;
209+
210+ const result = render ( < Component /> ) ;
211+ const dropdown = result . getByTestId ( "percentage-maxwidth-dropdown" ) ;
212+ const container = result . getByTestId ( "container-600" ) ;
213+
214+ await vi . waitFor ( ( ) => {
215+ const containerWidth = parseFloat (
216+ window . getComputedStyle ( container . element ( ) ) . width ,
217+ ) ;
218+ const computedWidth = parseFloat (
219+ window . getComputedStyle ( dropdown . element ( ) ) . width ,
220+ ) ;
221+ // Target ~300px (50% of 600) within tolerance
222+ expect ( computedWidth ) . toBeGreaterThan ( 250 ) ;
223+ expect ( computedWidth ) . toBeLessThan ( 310 ) ;
224+ expect ( Math . abs ( computedWidth - containerWidth * 0.5 ) ) . toBeLessThanOrEqual ( 15 ) ;
225+ } ) ;
226+ } ) ;
227+
228+ it ( "caps natural width with character (ch) maxWidth" , async ( ) => {
229+ const Component = ( ) => (
230+ < GoabDropdown
231+ name = "favcolor"
232+ testId = "ch-maxwidth-dropdown"
233+ maxWidth = "25ch"
234+ onChange = { noop }
235+ >
236+ < GoabDropdownItem label = "Red" value = "red" />
237+ < GoabDropdownItem label = "Blue" value = "blue" />
238+ < GoabDropdownItem label = "Green" value = "green" />
239+ </ GoabDropdown >
240+ ) ;
241+
242+ const result = render ( < Component /> ) ;
243+ const dropdown = result . getByTestId ( "ch-maxwidth-dropdown" ) ;
244+
245+ await vi . waitFor ( ( ) => {
246+ const pxWidth = parseFloat ( window . getComputedStyle ( dropdown . element ( ) ) . width ) ;
247+ expect ( pxWidth ) . toBeGreaterThan ( 50 ) ;
248+ expect ( pxWidth ) . toBeLessThan ( 600 ) ;
249+ } ) ;
250+ } ) ;
251+
252+ it ( "supports percentage width units" , async ( ) => {
253+ const Component = ( ) => {
254+ return (
255+ < GoabDropdown
256+ name = "favcolor"
257+ testId = "percentage-dropdown"
258+ width = "75%"
259+ onChange = { noop }
260+ >
261+ < GoabDropdownItem label = "Red" value = "red" />
262+ < GoabDropdownItem label = "Blue" value = "blue" />
263+ < GoabDropdownItem label = "Green" value = "green" />
264+ </ GoabDropdown >
265+ ) ;
266+ } ;
267+
268+ const result = render ( < Component /> ) ;
269+ const dropdown = result . getByTestId ( "percentage-dropdown" ) ;
270+
271+ await vi . waitFor ( ( ) => {
272+ // Check that width is set with percentage unit
273+ const styleAttr = dropdown . element ( ) . getAttribute ( "style" ) || "" ;
274+ expect ( styleAttr ) . toContain ( "--width: 75%" ) ;
275+
276+ // Check computed width is percentage of container
277+ const computedStyle = window . getComputedStyle ( dropdown . element ( ) ) ;
278+ expect ( computedStyle . width ) . toMatch ( / ^ \d + ( \. \d + ) ? p x $ / ) ; // Should be converted to pixels
279+
280+ // Check that it's a reasonable percentage width (should be substantial but not too large)
281+ const dropdownWidth = parseFloat ( computedStyle . width ) ;
282+ expect ( dropdownWidth ) . toBeGreaterThan ( 100 ) ; // Should be substantial
283+ expect ( dropdownWidth ) . toBeLessThan ( 800 ) ; // But not too large for 75%
284+ } ) ;
285+ } ) ;
286+
287+ it ( "supports character (ch) width units" , async ( ) => {
288+ const Component = ( ) => {
289+ return (
290+ < GoabDropdown
291+ name = "favcolor"
292+ testId = "ch-dropdown"
293+ width = "30ch"
294+ onChange = { noop }
295+ >
296+ < GoabDropdownItem label = "Red" value = "red" />
297+ < GoabDropdownItem label = "Blue" value = "blue" />
298+ < GoabDropdownItem label = "Green" value = "green" />
299+ </ GoabDropdown >
300+ ) ;
301+ } ;
302+
303+ const result = render ( < Component /> ) ;
304+ const dropdown = result . getByTestId ( "ch-dropdown" ) ;
305+
306+ await vi . waitFor ( ( ) => {
307+ // Check that width is set with ch unit
308+ const styleAttr = dropdown . element ( ) . getAttribute ( "style" ) || "" ;
309+ expect ( styleAttr ) . toContain ( "--width: 30ch" ) ;
310+
311+ // Check computed width is applied
312+ const computedStyle = window . getComputedStyle ( dropdown . element ( ) ) ;
313+ expect ( computedStyle . width ) . toMatch ( / ^ \d + ( \. \d + ) ? p x $ / ) ; // Browser converts ch to px
314+
315+ // Should have a reasonable width (ch is approximately font width)
316+ const dropdownWidth = parseFloat ( computedStyle . width ) ;
317+ expect ( dropdownWidth ) . toBeGreaterThan ( 200 ) ; // Should be substantial width
318+ expect ( dropdownWidth ) . toBeLessThan ( 600 ) ; // But not too large
319+ } ) ;
320+ } ) ;
321+
322+ it ( "defaults to px when no unit is provided" , async ( ) => {
323+ const Component = ( ) => {
324+ return (
325+ < GoabDropdown
326+ name = "favcolor"
327+ testId = "no-unit-dropdown"
328+ width = "250"
329+ onChange = { noop }
330+ >
331+ < GoabDropdownItem label = "Red" value = "red" />
332+ < GoabDropdownItem label = "Blue" value = "blue" />
333+ < GoabDropdownItem label = "Green" value = "green" />
334+ </ GoabDropdown >
335+ ) ;
336+ } ;
337+
338+ const result = render ( < Component /> ) ;
339+ const dropdown = result . getByTestId ( "no-unit-dropdown" ) ;
340+
341+ await vi . waitFor ( ( ) => {
342+ // Check that width is converted to px when no unit provided
343+ const styleAttr = dropdown . element ( ) . getAttribute ( "style" ) || "" ;
344+ expect ( styleAttr ) . toContain ( "--width: 250px" ) ;
345+
346+ // Check computed width matches expected px value
347+ const computedStyle = window . getComputedStyle ( dropdown . element ( ) ) ;
348+ expect ( computedStyle . width ) . toBe ( "250px" ) ;
349+ } ) ;
350+ } ) ;
139351 } ) ;
140352
141353 describe ( "Popover position" , ( ) => {
0 commit comments