@patternfly/react-data-view 5.5.0 → 5.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/cjs/DataViewCheckboxFilter/DataViewCheckboxFilter.d.ts +29 -0
  2. package/dist/cjs/DataViewCheckboxFilter/DataViewCheckboxFilter.js +70 -0
  3. package/dist/cjs/DataViewCheckboxFilter/DataViewCheckboxFilter.test.d.ts +1 -0
  4. package/dist/cjs/DataViewCheckboxFilter/DataViewCheckboxFilter.test.js +25 -0
  5. package/dist/cjs/DataViewCheckboxFilter/index.d.ts +2 -0
  6. package/dist/cjs/DataViewCheckboxFilter/index.js +23 -0
  7. package/dist/cjs/DataViewFilters/DataViewFilters.d.ts +7 -1
  8. package/dist/cjs/DataViewFilters/DataViewFilters.js +16 -1
  9. package/dist/cjs/DataViewTableTree/DataViewTableTree.js +26 -14
  10. package/dist/cjs/DataViewTextFilter/DataViewTextFilter.js +1 -1
  11. package/dist/cjs/Hooks/filters.js +13 -14
  12. package/dist/cjs/index.d.ts +2 -0
  13. package/dist/cjs/index.js +4 -1
  14. package/dist/dynamic/DataViewCheckboxFilter/package.json +1 -0
  15. package/dist/esm/DataViewCheckboxFilter/DataViewCheckboxFilter.d.ts +29 -0
  16. package/dist/esm/DataViewCheckboxFilter/DataViewCheckboxFilter.js +62 -0
  17. package/dist/esm/DataViewCheckboxFilter/DataViewCheckboxFilter.test.d.ts +1 -0
  18. package/dist/esm/DataViewCheckboxFilter/DataViewCheckboxFilter.test.js +20 -0
  19. package/dist/esm/DataViewCheckboxFilter/index.d.ts +2 -0
  20. package/dist/esm/DataViewCheckboxFilter/index.js +2 -0
  21. package/dist/esm/DataViewFilters/DataViewFilters.d.ts +7 -1
  22. package/dist/esm/DataViewFilters/DataViewFilters.js +16 -1
  23. package/dist/esm/DataViewTableTree/DataViewTableTree.js +26 -14
  24. package/dist/esm/DataViewTextFilter/DataViewTextFilter.js +1 -1
  25. package/dist/esm/Hooks/filters.js +13 -14
  26. package/dist/esm/index.d.ts +2 -0
  27. package/dist/esm/index.js +2 -0
  28. package/package.json +1 -1
  29. package/patternfly-docs/content/extensions/data-view/examples/Functionality/FiltersExample.tsx +31 -16
  30. package/patternfly-docs/content/extensions/data-view/examples/Functionality/Functionality.md +4 -3
  31. package/src/DataViewCheckboxFilter/DataViewCheckboxFilter.test.tsx +24 -0
  32. package/src/DataViewCheckboxFilter/DataViewCheckboxFilter.tsx +175 -0
  33. package/src/DataViewCheckboxFilter/__snapshots__/DataViewCheckboxFilter.test.tsx.snap +194 -0
  34. package/src/DataViewCheckboxFilter/index.ts +2 -0
  35. package/src/DataViewFilters/DataViewFilters.tsx +26 -7
  36. package/src/DataViewTableTree/DataViewTableTree.tsx +39 -18
  37. package/src/DataViewTextFilter/DataViewTextFilter.tsx +1 -0
  38. package/src/Hooks/filters.ts +14 -13
  39. package/src/index.ts +3 -0
@@ -12,24 +12,45 @@ import { DataViewTableHead } from '../DataViewTableHead';
12
12
  import { DataViewTh, DataViewTrTree, isDataViewTdObject } from '../DataViewTable';
13
13
  import { DataViewState } from '../DataView/DataView';
14
14
 
15
- const getDescendants = (node: DataViewTrTree): DataViewTrTree[] => (!node.children || !node.children.length) ? [ node ] : node.children.flatMap(getDescendants);
16
-
17
- const isNodeChecked = (node: DataViewTrTree, isSelected: (node: DataViewTrTree) => boolean) => {
18
- let allSelected = true;
19
- let someSelected = false;
20
-
21
- for (const descendant of getDescendants(node)) {
22
- const selected = !!isSelected?.(descendant);
23
-
24
- someSelected ||= selected;
25
- allSelected &&= selected;
26
-
27
- if (!allSelected && someSelected) { return null }
28
- }
29
-
30
- return allSelected;
15
+ const getNodesAffectedBySelection = (
16
+ allRows: DataViewTrTree[],
17
+ node: DataViewTrTree,
18
+ isChecking: boolean,
19
+ isSelected?: (item: DataViewTrTree) => boolean
20
+ ): DataViewTrTree[] => {
21
+
22
+ const getDescendants = (node: DataViewTrTree): DataViewTrTree[] =>
23
+ node.children ? node.children.flatMap(getDescendants).concat(node) : [ node ];
24
+
25
+ const findParent = (child: DataViewTrTree, rows: DataViewTrTree[]): DataViewTrTree | undefined =>
26
+ rows.find(row => row.children?.some(c => c === child)) ??
27
+ rows.flatMap(row => row.children ?? []).map(c => findParent(child, [ c ])).find(p => p);
28
+
29
+ const getAncestors = (node: DataViewTrTree): DataViewTrTree[] => {
30
+ const ancestors: DataViewTrTree[] = [];
31
+ let parent = findParent(node, allRows);
32
+ while (parent) {
33
+ ancestors.push(parent);
34
+ parent = findParent(parent, allRows);
35
+ }
36
+ return ancestors;
37
+ };
38
+
39
+ const affectedNodes = new Set([ node, ...getDescendants(node) ]);
40
+
41
+ getAncestors(node).forEach(ancestor => {
42
+ const allChildrenSelected = ancestor.children?.every(child => isSelected?.(child) || affectedNodes.has(child));
43
+ const anyChildAffected = ancestor.children?.some(child => affectedNodes.has(child) || child.id === node.id);
44
+
45
+ if (isChecking ? !isSelected?.(ancestor) && allChildrenSelected : isSelected?.(ancestor) && anyChildAffected) {
46
+ affectedNodes.add(ancestor);
47
+ }
48
+ });
49
+
50
+ return Array.from(affectedNodes);
31
51
  };
32
52
 
53
+
33
54
  /** extends TableProps */
34
55
  export interface DataViewTableTreeProps extends Omit<TableProps, 'onSelect' | 'rows'> {
35
56
  /** Columns definition */
@@ -83,7 +104,7 @@ export const DataViewTableTree: React.FC<DataViewTableTreeProps> = ({
83
104
  }
84
105
  const isExpanded = expandedNodeIds.includes(node.id);
85
106
  const isDetailsExpanded = expandedDetailsNodeNames.includes(node.id);
86
- const isChecked = isSelected && isNodeChecked(node, isSelected);
107
+ const isChecked = isSelected?.(node);
87
108
  let icon = leafIcon;
88
109
  if (node.children) {
89
110
  icon = isExpanded ? expandedIcon : collapsedIcon;
@@ -100,7 +121,7 @@ export const DataViewTableTree: React.FC<DataViewTableTreeProps> = ({
100
121
  const otherDetailsExpandedNodeIds = prevDetailsExpanded.filter(id => id !== node.id);
101
122
  return isDetailsExpanded ? otherDetailsExpandedNodeIds : [ ...otherDetailsExpandedNodeIds, node.id ];
102
123
  }),
103
- onCheckChange: (isSelectDisabled?.(node) || !onSelect) ? undefined : (_event, isChecking) => onSelect?.(isChecking, getDescendants(node)),
124
+ onCheckChange: (isSelectDisabled?.(node) || !onSelect) ? undefined : (_event, isChecking) => onSelect?.(isChecking, getNodesAffectedBySelection(rows, node, isChecking, isSelected)),
104
125
  rowIndex,
105
126
  props: {
106
127
  isExpanded,
@@ -31,6 +31,7 @@ export const DataViewTextFilter: React.FC<DataViewTextFilterProps> = ({
31
31
  ...props
32
32
  }: DataViewTextFilterProps) => (
33
33
  <ToolbarFilter
34
+ key={ouiaId}
34
35
  data-ouia-component-id={ouiaId}
35
36
  chips={value.length > 0 ? [ { key: title, node: value } ] : []}
36
37
  deleteChip={() => onChange?.(undefined, '')}
@@ -16,15 +16,19 @@ export const useDataViewFilters = <T extends object>({
16
16
  }: UseDataViewFiltersProps<T>) => {
17
17
  const isUrlSyncEnabled = useMemo(() => searchParams && !!setSearchParams, [ searchParams, setSearchParams ]);
18
18
 
19
- const getInitialFilters = useCallback((): T => isUrlSyncEnabled ? Object.keys(initialFilters).reduce((loadedFilters, key) => {
20
- const urlValue = searchParams?.get(key);
21
- loadedFilters[key as keyof T] = urlValue
22
- ? (urlValue as T[keyof T] | T[keyof T])
23
- : initialFilters[key as keyof T];
24
- return loadedFilters;
25
- // eslint-disable-next-line react-hooks/exhaustive-deps
26
- }, { ...initialFilters }) : initialFilters, [ isUrlSyncEnabled, JSON.stringify(initialFilters), searchParams?.toString() ]);
19
+ const getInitialFilters = useCallback((): T => isUrlSyncEnabled
20
+ ? Object.keys(initialFilters).reduce((loadedFilters, key) => {
21
+ const urlValue = searchParams?.get(key);
22
+ const isArrayFilter = Array.isArray(initialFilters[key]);
27
23
 
24
+ // eslint-disable-next-line no-nested-ternary
25
+ loadedFilters[key] = urlValue
26
+ ? (isArrayFilter && !Array.isArray(urlValue) ? [ urlValue ] : urlValue)
27
+ : initialFilters[key];
28
+
29
+ return loadedFilters;
30
+ }, { ...initialFilters })
31
+ : initialFilters, [ isUrlSyncEnabled, initialFilters, searchParams ]);
28
32
  const [ filters, setFilters ] = useState<T>(getInitialFilters());
29
33
 
30
34
  const updateSearchParams = useCallback(
@@ -32,11 +36,8 @@ export const useDataViewFilters = <T extends object>({
32
36
  if (isUrlSyncEnabled) {
33
37
  const params = new URLSearchParams(searchParams);
34
38
  Object.entries(newFilters).forEach(([ key, value ]) => {
35
- if (value) {
36
- params.set(key, Array.isArray(value) ? value.join(',') : value);
37
- } else {
38
- params.delete(key);
39
- }
39
+ params.delete(key);
40
+ (Array.isArray(value) ? value : [ value ]).forEach((val) => value && params.append(key, val));
40
41
  });
41
42
  setSearchParams?.(params);
42
43
  }
package/src/index.ts CHANGED
@@ -25,5 +25,8 @@ export * from './DataViewTable';
25
25
  export { default as DataViewEventsContext } from './DataViewEventsContext';
26
26
  export * from './DataViewEventsContext';
27
27
 
28
+ export { default as DataViewCheckboxFilter } from './DataViewCheckboxFilter';
29
+ export * from './DataViewCheckboxFilter';
30
+
28
31
  export { default as DataView } from './DataView';
29
32
  export * from './DataView';