Skip to content

Commit ab342ac

Browse files
committed
fix: adding new requested tests and implementing requested changes
1 parent 4b1ed54 commit ab342ac

File tree

4 files changed

+201
-28
lines changed

4 files changed

+201
-28
lines changed

packages/module/patternfly-docs/content/extensions/data-view/examples/Toolbar/TreeFilterExample.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ const MyTable: React.FunctionComponent = () => {
218218
filterId="os"
219219
title="Operating System"
220220
items={osOptions}
221-
defaultExpanded={true}
221+
defaultExpanded={false}
222222
/>
223223
<DataViewTreeFilter
224224
filterId="tags"

packages/module/src/DataViewFilters/DataViewFilters.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export const DataViewFilters = <T extends object>({
6262

6363
useEffect(() => {
6464
filterItems.length > 0 && setActiveAttributeMenu(filterItems[0].title);
65-
}, [ filterItems.length ]);
65+
}, [ filterItems ]);
6666

6767
const handleClickOutside = (event: MouseEvent) =>
6868
isAttributeMenuOpen &&
Lines changed: 186 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,70 @@
1-
import { render } from '@testing-library/react';
1+
import { render, screen, waitFor } from '@testing-library/react';
2+
import '@testing-library/jest-dom';
3+
import userEvent from '@testing-library/user-event';
24
import DataViewTreeFilter, { DataViewTreeFilterProps } from './DataViewTreeFilter';
35
import DataViewToolbar from '../DataViewToolbar';
46
import { TreeViewDataItem } from '@patternfly/react-core';
57

68
describe('DataViewTreeFilter component', () => {
79
const treeItems: TreeViewDataItem[] = [
810
{
9-
name: 'Development Workspace',
10-
id: 'workspace-dev',
11-
checkProps: { 'aria-label': 'dev-workspace-check', checked: false }
11+
name: 'Linux',
12+
id: 'os-linux',
13+
checkProps: { 'aria-label': 'linux-check', checked: false },
14+
children: [
15+
{
16+
name: 'Ubuntu 22.04',
17+
id: 'os-ubuntu',
18+
checkProps: { checked: false }
19+
},
20+
{
21+
name: 'RHEL 9',
22+
id: 'os-rhel',
23+
checkProps: { checked: false }
24+
},
25+
{
26+
name: 'Debian 12',
27+
id: 'os-debian',
28+
checkProps: { checked: false }
29+
},
30+
{
31+
name: 'CentOS 8',
32+
id: 'os-centos',
33+
checkProps: { checked: false }
34+
},
35+
{
36+
name: 'Fedora 38',
37+
id: 'os-fedora',
38+
checkProps: { checked: false }
39+
}
40+
],
41+
defaultExpanded: true
1242
},
1343
{
14-
name: 'Production Workspace',
15-
id: 'workspace-prod',
16-
checkProps: { 'aria-label': 'prod-workspace-check', checked: false }
44+
name: 'Windows',
45+
id: 'os-windows',
46+
checkProps: { 'aria-label': 'windows-check', checked: false },
47+
children: [
48+
{
49+
name: 'Windows Server 2022',
50+
id: 'os-windows-2022',
51+
checkProps: { checked: false }
52+
}
53+
]
1754
},
1855
{
19-
name: 'Operating Systems',
20-
id: 'os-parent',
21-
checkProps: { 'aria-label': 'os-check', checked: false },
56+
name: 'macOS',
57+
id: 'os-macos',
58+
checkProps: { 'aria-label': 'macos-check', checked: false },
2259
children: [
2360
{
24-
name: 'Linux',
25-
id: 'os-linux',
61+
name: 'macOS Ventura',
62+
id: 'os-macos-ventura',
2663
checkProps: { checked: false }
2764
},
2865
{
29-
name: 'Windows',
30-
id: 'os-windows',
66+
name: 'macOS Sonoma',
67+
id: 'os-macos-sonoma',
3168
checkProps: { checked: false }
3269
}
3370
]
@@ -40,11 +77,146 @@ describe('DataViewTreeFilter component', () => {
4077
value: ['Linux'],
4178
items: treeItems
4279
};
80+
beforeEach(() => {
81+
jest.clearAllMocks();
82+
});
4383

4484
it('should render correctly', () => {
4585
const { container } = render(
4686
<DataViewToolbar filters={<DataViewTreeFilter {...defaultProps} />} />
4787
);
4888
expect(container).toMatchSnapshot();
4989
});
90+
describe('defaultExpanded', () => {
91+
it('should have expanded items by default', async () => {
92+
render(
93+
<DataViewToolbar
94+
filters={
95+
<DataViewTreeFilter
96+
filterId="os"
97+
title="Operating System"
98+
items={treeItems}
99+
defaultExpanded={true}
100+
/>
101+
}
102+
/>
103+
);
104+
105+
const openMenu = screen.getByRole('button', { name: /operating system/i });
106+
await userEvent.click(openMenu);
107+
await waitFor(() => {
108+
const node = screen.getByText('Ubuntu 22.04');
109+
expect(node).toHaveClass('pf-v6-c-tree-view__node-text');
110+
expect(node).toBeInTheDocument();
111+
});
112+
});
113+
});
114+
describe('onChange callback', () => {
115+
it('onChange should be called on toggle of node', async () => {
116+
const mockOnChange = jest.fn();
117+
render(
118+
<DataViewToolbar
119+
filters={
120+
<DataViewTreeFilter
121+
filterId="os"
122+
title="Operating System"
123+
items={treeItems}
124+
defaultExpanded={true}
125+
onChange={mockOnChange}
126+
/>
127+
}
128+
/>
129+
);
130+
131+
const openMenu = screen.getByRole('button', { name: /operating system/i });
132+
await userEvent.click(openMenu);
133+
134+
await waitFor(() => {
135+
const node = screen.getByText('Ubuntu 22.04');
136+
expect(node).toBeInTheDocument();
137+
});
138+
139+
const node = screen.getByText('Ubuntu 22.04');
140+
await userEvent.click(node);
141+
142+
await waitFor(() => {
143+
expect(mockOnChange).toHaveBeenCalled();
144+
});
145+
});
146+
});
147+
describe('onSelect callback', () => {
148+
it('onSelect should return list of selected items when item is selected', async () => {
149+
const mockOnSelect = jest.fn();
150+
render(
151+
<DataViewToolbar
152+
filters={
153+
<DataViewTreeFilter
154+
filterId="os"
155+
title="Operating System"
156+
items={treeItems}
157+
defaultExpanded={true}
158+
onSelect={mockOnSelect}
159+
/>
160+
}
161+
/>
162+
);
163+
164+
const openMenu = screen.getByRole('button', { name: /operating system/i });
165+
await userEvent.click(openMenu);
166+
167+
await waitFor(() => {
168+
const node = screen.getByText('Ubuntu 22.04');
169+
expect(node).toBeInTheDocument();
170+
});
171+
172+
const node = screen.getByText('Ubuntu 22.04');
173+
await userEvent.click(node);
174+
175+
await waitFor(() => {
176+
expect(mockOnSelect).toHaveBeenCalled();
177+
expect(mockOnSelect).toHaveBeenCalledWith(
178+
expect.arrayContaining([
179+
expect.objectContaining({
180+
name: 'Ubuntu 22.04',
181+
id: 'os-ubuntu'
182+
})
183+
])
184+
);
185+
});
186+
});
187+
});
188+
189+
describe('rendering all items', () => {
190+
it('all tree items should be rendered', async () => {
191+
render(
192+
<DataViewToolbar
193+
filters={
194+
<DataViewTreeFilter
195+
filterId="os"
196+
title="Operating System"
197+
items={treeItems}
198+
defaultExpanded={true}
199+
/>
200+
}
201+
/>
202+
);
203+
204+
const openMenu = screen.getByRole('button', { name: /operating system/i });
205+
await userEvent.click(openMenu);
206+
207+
await waitFor(() => {
208+
expect(screen.getByText('Linux')).toBeInTheDocument();
209+
expect(screen.getByText('Windows')).toBeInTheDocument();
210+
expect(screen.getByText('macOS')).toBeInTheDocument();
211+
expect(screen.getByText('Ubuntu 22.04')).toBeInTheDocument();
212+
expect(screen.getByText('RHEL 9')).toBeInTheDocument();
213+
expect(screen.getByText('Debian 12')).toBeInTheDocument();
214+
expect(screen.getByText('CentOS 8')).toBeInTheDocument();
215+
expect(screen.getByText('Fedora 38')).toBeInTheDocument();
216+
expect(screen.getByText('Windows Server 2022')).toBeInTheDocument();
217+
expect(screen.getByText('macOS Ventura')).toBeInTheDocument();
218+
expect(screen.getByText('macOS Sonoma')).toBeInTheDocument();
219+
});
220+
});
221+
});
50222
});

packages/module/src/DataViewTreeFilter/DataViewTreeFilter.tsx

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -156,22 +156,23 @@ export const DataViewTreeFilter: FC<DataViewTreeFilterProps> = ({
156156

157157
// Only call if there are actually selected values
158158
if (selectedValues.length > 0) {
159-
// Defer the callbacks to avoid updating parent during render
160-
queueMicrotask(() => {
161-
if (onChange) {
162-
onChange(undefined, selectedValues);
163-
}
164-
165-
if (onSelect) {
166-
const selectedItems = getAllCheckedItems(treeData);
167-
onSelect(selectedItems);
168-
}
169-
});
159+
// Calculate both values synchronously before calling callbacks
160+
const selectedItems = getAllCheckedItems(treeData);
161+
162+
// useEffect already runs after render, so this is safe
163+
if (onChange) {
164+
onChange(undefined, selectedValues);
165+
}
166+
167+
if (onSelect) {
168+
onSelect(selectedItems);
169+
}
170170

171171
hasCalledInitialOnChange.current = true;
172172
}
173173
}
174-
}, [treeData]);
174+
}, [treeData, onChange, onSelect, defaultSelected.length]);
175+
175176

176177
// Sync tree checkboxes when value prop changes (when clearAllFilters is called)
177178
useEffect(() => {

0 commit comments

Comments
 (0)