11import type { Meta , StoryObj } from '@storybook/react-vite' ;
22import { useState } from 'react' ;
3- import { expect , userEvent , waitFor , within } from 'storybook/test' ;
3+ import { expect , fn , userEvent , waitFor , within } from 'storybook/test' ;
44import DsTagFilter from './ds-tag-filter' ;
55import type { TagFilterItem } from './ds-tag-filter.types' ;
66import styles from './ds-tag-filter.stories.module.scss' ;
@@ -39,6 +39,10 @@ const meta: Meta<typeof DsTagFilter> = {
3939 action : 'select-item' ,
4040 description : 'Callback when item is selected' ,
4141 } ,
42+ onExpand : {
43+ action : 'expand' ,
44+ description : 'Callback when expand/collapse is clicked' ,
45+ } ,
4246 } ,
4347} ;
4448
@@ -57,8 +61,8 @@ const sampleFilters: TagFilterItem[] = [
5761 { id : '9' , label : 'Version: 000.0001-4' } ,
5862 { id : '10' , label : 'Version: 000.0001-5' } ,
5963 { id : '11' , label : 'Version: 000.0001-6' } ,
60- { id : '12' , label : 'Last editor: Maren Levin' } ,
61- { id : '13' , label : 'Last editor: Emery Franci ' } ,
64+ { id : '12' , label : 'Last editor: Kevin Levin' } ,
65+ { id : '13' , label : 'Last editor: Emery Dance ' } ,
6266] ;
6367
6468/**
@@ -125,7 +129,7 @@ export const Default: Story = {
125129 await expect ( canvas . getByRole ( 'button' , { name : 'Status: Active' } ) ) . toBeInTheDocument ( ) ;
126130 } ) ;
127131
128- await expect ( canvas . getAllByText ( 'Filtered by:' ) [ 0 ] ) . toBeInTheDocument ( ) ;
132+ await expect ( canvas . getByText ( 'Filtered by:' ) ) . toBeInTheDocument ( ) ;
129133 await expect ( canvas . getByRole ( 'button' , { name : / C l e a r a l l f i l t e r s / } ) ) . toBeInTheDocument ( ) ;
130134
131135 const firstTag = canvas . getByRole ( 'button' , { name : 'Status: Active' } ) ;
@@ -297,11 +301,13 @@ export const ReadOnly: Story = {
297301 play : async ( { canvasElement } ) => {
298302 const canvas = within ( canvasElement ) ;
299303
300- // Verify custom label is shown (use getAllByText and take first to avoid measurement container duplicate)
301- await expect ( canvas . getAllByText ( 'Applied filters:' ) [ 0 ] ) . toBeInTheDocument ( ) ;
304+ // Wait for layout calculation to complete and tags to be rendered
305+ await waitFor ( async ( ) => {
306+ await expect ( canvas . getByText ( 'Status: Active' ) ) . toBeInTheDocument ( ) ;
307+ } ) ;
302308
303- // Verify filters are visible
304- await expect ( canvas . getAllByText ( 'Status: Active' ) [ 0 ] ) . toBeInTheDocument ( ) ;
309+ // Verify custom label is shown
310+ await expect ( canvas . getByText ( 'Applied filters:' ) ) . toBeInTheDocument ( ) ;
305311
306312 // Verify delete buttons are NOT visible (read-only)
307313 await expect ( canvas . queryByRole ( 'button' , { name : 'Delete tag' } ) ) . not . toBeInTheDocument ( ) ;
@@ -402,8 +408,8 @@ export const CustomLocale: Story = {
402408 await expect ( canvas . getByRole ( 'button' , { name : 'Status: Active' } ) ) . toBeInTheDocument ( ) ;
403409 } ) ;
404410
405- // Verify custom label is rendered (use getAllByText and take first to avoid measurement container duplicate)
406- await expect ( canvas . getAllByText ( 'Active criteria:' ) [ 0 ] ) . toBeInTheDocument ( ) ;
411+ // Verify custom label is rendered
412+ await expect ( canvas . getByText ( 'Active criteria:' ) ) . toBeInTheDocument ( ) ;
407413
408414 await expect ( canvas . getByRole ( 'button' , { name : / R e s e t a l l / } ) ) . toBeInTheDocument ( ) ;
409415
@@ -412,6 +418,54 @@ export const CustomLocale: Story = {
412418 } ,
413419} ;
414420
421+ /**
422+ * Story testing the expand/collapse functionality and onExpand callback.
423+ */
424+ export const ExpandCollapse : Story = {
425+ args : {
426+ items : sampleFilters ,
427+ onExpand : fn ( ) ,
428+ } ,
429+ play : async ( { canvasElement, args } ) => {
430+ const canvas = within ( canvasElement ) ;
431+
432+ // Wait for layout calculation to complete and overflow tag to appear
433+ await waitFor ( async ( ) => {
434+ await expect ( canvas . getByRole ( 'button' , { name : / \+ \d + f i l t e r s ? / } ) ) . toBeInTheDocument ( ) ;
435+ } ) ;
436+
437+ const expandButton = canvas . getByRole ( 'button' , { name : / \+ \d + f i l t e r s ? / } ) ;
438+
439+ // Click expand button
440+ await userEvent . click ( expandButton ) ;
441+
442+ // Verify onExpand was called with true (expanded)
443+ await waitFor ( async ( ) => {
444+ await expect ( args . onExpand ) . toHaveBeenCalledWith ( true ) ;
445+ } ) ;
446+
447+ // Verify the button now shows "Collapse"
448+ await waitFor ( async ( ) => {
449+ await expect ( canvas . getByRole ( 'button' , { name : 'Collapse' } ) ) . toBeInTheDocument ( ) ;
450+ } ) ;
451+
452+ const collapseButton = canvas . getByRole ( 'button' , { name : 'Collapse' } ) ;
453+
454+ // Click collapse button
455+ await userEvent . click ( collapseButton ) ;
456+
457+ // Verify onExpand was called with false (collapsed)
458+ await waitFor ( async ( ) => {
459+ await expect ( args . onExpand ) . toHaveBeenCalledWith ( false ) ;
460+ } ) ;
461+
462+ // Verify the expand button is back
463+ await waitFor ( async ( ) => {
464+ await expect ( canvas . getByRole ( 'button' , { name : / \+ \d + f i l t e r s ? / } ) ) . toBeInTheDocument ( ) ;
465+ } ) ;
466+ } ,
467+ } ;
468+
415469/**
416470 * Story showing TagFilter with pre-selected items.
417471 */
0 commit comments