Skip to content

Commit de14dae

Browse files
committed
feat(defaultActive): set correct filter after clicking on clear
1 parent 8ad3414 commit de14dae

3 files changed

Lines changed: 99 additions & 4 deletions

File tree

cypress/component/DataViewFilters.cy.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ describe('DataViewFilters', () => {
9898
cy.get('[aria-label="Close Main branch"]').should('have.length', 0);
9999
});
100100

101-
it('clears all filters using the clear-all button', () => {
101+
it('clears all filters using the clear-all button and resets attribute to first', () => {
102102
cy.mount(<DataViewToolbarWithState />);
103103
cy.get('[data-ouia-component-id="DataViewFilters"] .pf-v6-c-menu-toggle').click();
104104
cy.contains('Name').click();
@@ -108,6 +108,8 @@ describe('DataViewFilters', () => {
108108
cy.contains('Branch').click();
109109
cy.get('input[placeholder="Filter by branch"]').type('Main branch');
110110

111+
cy.get('[data-ouia-component-id="DataViewFilters"]').should('contain.text', 'Branch');
111112
cy.get('[data-ouia-component-id="FiltersExampleHeader-clear-all-filters"]').should('exist').click();
113+
cy.get('[data-ouia-component-id="DataViewFilters"]').should('contain.text', 'Name');
112114
});
113115
});

packages/module/src/DataViewFilters/DataViewFilters.test.tsx

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,78 @@ describe('DataViewFilters component', () => {
3737
fireEvent.input(input, { target: { value: 'abc' } });
3838
expect(mockOnChange).toHaveBeenCalledWith('one', { one: 'abc' });
3939
});
40+
41+
it('should use defaultActiveFilter as initial active filter', () => {
42+
const { container } = render(
43+
<DataViewToolbar
44+
filters={
45+
<DataViewFilters onChange={mockOnChange} values={{ one: '', two: '' }} defaultActiveFilter="two">
46+
<DataViewTextFilter filterId="one" title="One" />
47+
<DataViewTextFilter filterId="two" title="Two" />
48+
</DataViewFilters>
49+
}
50+
/>
51+
);
52+
53+
const toggle = container.querySelector('[data-ouia-component-id="DataViewFilters"] .pf-v6-c-menu-toggle') as HTMLButtonElement;
54+
expect(toggle.textContent).toContain('Two');
55+
});
56+
57+
it('should reset active filter to defaultActiveFilter when all values are cleared', () => {
58+
const { container, rerender } = render(
59+
<DataViewToolbar
60+
filters={
61+
<DataViewFilters onChange={mockOnChange} values={{ one: 'test', two: '' }} defaultActiveFilter="two">
62+
<DataViewTextFilter filterId="one" title="One" />
63+
<DataViewTextFilter filterId="two" title="Two" />
64+
</DataViewFilters>
65+
}
66+
/>
67+
);
68+
69+
const toggle = container.querySelector('[data-ouia-component-id="DataViewFilters"] .pf-v6-c-menu-toggle') as HTMLButtonElement;
70+
expect(toggle.textContent).toContain('Two');
71+
72+
rerender(
73+
<DataViewToolbar
74+
filters={
75+
<DataViewFilters onChange={mockOnChange} values={{ one: '', two: '' }} defaultActiveFilter="two">
76+
<DataViewTextFilter filterId="one" title="One" />
77+
<DataViewTextFilter filterId="two" title="Two" />
78+
</DataViewFilters>
79+
}
80+
/>
81+
);
82+
83+
expect(toggle.textContent).toContain('Two');
84+
});
85+
86+
it('should reset active filter to first item when all values are cleared without defaultActiveFilter', () => {
87+
const { container, rerender } = render(
88+
<DataViewToolbar
89+
filters={
90+
<DataViewFilters onChange={mockOnChange} values={{ one: 'test', two: '' }}>
91+
<DataViewTextFilter filterId="one" title="One" />
92+
<DataViewTextFilter filterId="two" title="Two" />
93+
</DataViewFilters>
94+
}
95+
/>
96+
);
97+
98+
const toggle = container.querySelector('[data-ouia-component-id="DataViewFilters"] .pf-v6-c-menu-toggle') as HTMLButtonElement;
99+
expect(toggle.textContent).toContain('One');
100+
101+
rerender(
102+
<DataViewToolbar
103+
filters={
104+
<DataViewFilters onChange={mockOnChange} values={{ one: '', two: '' }}>
105+
<DataViewTextFilter filterId="one" title="One" />
106+
<DataViewTextFilter filterId="two" title="Two" />
107+
</DataViewFilters>
108+
}
109+
/>
110+
);
111+
112+
expect(toggle.textContent).toContain('One');
113+
});
40114
});

packages/module/src/DataViewFilters/DataViewFilters.tsx

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Children, isValidElement, cloneElement, useMemo, useState, useRef, useEffect, ReactElement, ReactNode } from 'react';
1+
import { Children, isValidElement, cloneElement, useMemo, useCallback, useState, useRef, useEffect, ReactElement, ReactNode } from 'react';
22
import {
33
Menu, MenuContent, MenuItem, MenuList, MenuToggle, Popper, ToolbarGroup, ToolbarToggleGroup, ToolbarToggleGroupProps,
44
} from '@patternfly/react-core';
@@ -31,6 +31,8 @@ export interface DataViewFiltersProps<T extends object> extends Omit<ToolbarTogg
3131
breakpoint?: ToolbarToggleGroupProps['breakpoint'];
3232
/** Custom OUIA ID */
3333
ouiaId?: string;
34+
/** Optional filterId to use as the default active filter. Falls back to the first filter when not specified. The active filter resets to this value when all filter values are cleared. */
35+
defaultActiveFilter?: string;
3436
};
3537

3638

@@ -41,6 +43,7 @@ export const DataViewFilters = <T extends object>({
4143
breakpoint = 'xl',
4244
onChange,
4345
values,
46+
defaultActiveFilter,
4447
...props
4548
}: DataViewFiltersProps<T>) => {
4649
const [ activeAttributeMenu, setActiveAttributeMenu ] = useState<string>('');
@@ -60,9 +63,25 @@ export const DataViewFilters = <T extends object>({
6063
isValidElement(child) ? { filterId: String((child.props as any).filterId), title: String((child.props as any).title) } : undefined
6164
).filter((item): item is DataViewFilterIdentifiers => !!item), [ childrenHash ]); // eslint-disable-line react-hooks/exhaustive-deps
6265

66+
const getDefaultTitle = useCallback(() => {
67+
if (defaultActiveFilter) {
68+
const match = filterItems.find(item => item.filterId === defaultActiveFilter);
69+
if (match) {
70+
return match.title;
71+
}
72+
}
73+
return filterItems.length > 0 ? filterItems[0].title : '';
74+
}, [ defaultActiveFilter, filterItems ]);
75+
76+
useEffect(() => {
77+
setActiveAttributeMenu(getDefaultTitle());
78+
}, [ filterItems, getDefaultTitle ]);
79+
6380
useEffect(() => {
64-
filterItems.length > 0 && setActiveAttributeMenu(filterItems[0].title);
65-
}, [ filterItems ]);
81+
if (values && Object.values(values).every(v => v === '' || v === undefined || (Array.isArray(v) && v.length === 0))) {
82+
setActiveAttributeMenu(getDefaultTitle());
83+
}
84+
}, [ values, getDefaultTitle ]);
6685

6786
const handleClickOutside = (event: MouseEvent) =>
6887
isAttributeMenuOpen &&

0 commit comments

Comments
 (0)