playbook_ui 16.3.0 → 16.4.0.pre.alpha.displaybreakpoints15091

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 (114) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/Components/CustomCell.tsx +17 -4
  3. data/app/pb_kits/playbook/pb_advanced_table/Components/TableHeaderCell.tsx +3 -1
  4. data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableActions.ts +21 -9
  5. data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableState.ts +5 -2
  6. data/app/pb_kits/playbook/pb_advanced_table/Utilities/ExpansionControlHelpers.tsx +25 -1
  7. data/app/pb_kits/playbook/pb_advanced_table/Utilities/RowModelUtils.ts +100 -0
  8. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +9 -2
  9. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +109 -2
  10. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_cascade_collapse.jsx +50 -0
  11. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_cascade_collapse.md +1 -0
  12. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_sort_parent_only.jsx +175 -0
  13. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_sort_parent_only.md +5 -0
  14. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +2 -0
  15. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +3 -1
  16. data/app/pb_kits/playbook/pb_advanced_table/index.js +48 -29
  17. data/app/pb_kits/playbook/pb_button/_button_mixins.scss +6 -1
  18. data/app/pb_kits/playbook/pb_collapsible/index.js +15 -26
  19. data/app/pb_kits/playbook/pb_date_picker/date_picker_helper.ts +3 -1
  20. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_compound_components.html.erb +1 -1
  21. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_compound_components.jsx +6 -3
  22. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height.html.erb +3 -3
  23. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height.jsx +6 -3
  24. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height_placement.html.erb +3 -3
  25. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_full_height_placement.jsx +6 -3
  26. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +3 -0
  27. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +1 -0
  28. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_closing_options_rails.html.erb +16 -0
  29. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_closing_options_rails.md +1 -0
  30. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_custom_event_type.html.erb +224 -0
  31. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_custom_event_type.md +7 -0
  32. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +2 -0
  33. data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +8 -1
  34. data/app/pb_kits/playbook/pb_dropdown/index.js +255 -46
  35. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +19 -14
  36. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/_fixed_confirmation_toast.scss +4 -0
  37. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/_fixed_confirmation_toast.tsx +3 -0
  38. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_fixed_confirmation_toast_nav_margin.html.erb +46 -0
  39. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_fixed_confirmation_toast_nav_margin.jsx +42 -0
  40. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_fixed_confirmation_toast_nav_margin_rails.md +1 -0
  41. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_fixed_confirmation_toast_nav_margin_react.md +1 -0
  42. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/example.yml +2 -0
  43. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/index.js +2 -1
  44. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/fixed_confirmation_toast.rb +7 -1
  45. data/app/pb_kits/playbook/pb_multi_level_select/_helper_functions.tsx +1 -1
  46. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +24 -15
  47. data/app/pb_kits/playbook/pb_popover/docs/_popover_placement.jsx +81 -0
  48. data/app/pb_kits/playbook/pb_popover/docs/_popover_placement_react.md +1 -0
  49. data/app/pb_kits/playbook/pb_popover/docs/_popover_position.html.erb +128 -0
  50. data/app/pb_kits/playbook/pb_popover/docs/_popover_position_rails.md +1 -0
  51. data/app/pb_kits/playbook/pb_popover/docs/example.yml +2 -0
  52. data/app/pb_kits/playbook/pb_popover/docs/index.js +2 -1
  53. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.tsx +35 -134
  54. data/app/pb_kits/playbook/pb_rich_text_editor/_tiptap_editor.tsx +51 -0
  55. data/app/pb_kits/playbook/pb_rich_text_editor/_trix_editor.tsx +206 -0
  56. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_default.jsx +56 -0
  57. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_default.md +1 -0
  58. data/app/pb_kits/playbook/pb_rich_text_editor/docs/example.yml +13 -21
  59. data/app/pb_kits/playbook/pb_rich_text_editor/docs/index.js +0 -10
  60. data/app/pb_kits/playbook/pb_rich_text_editor/inlineFocus.ts +5 -4
  61. data/app/pb_kits/playbook/pb_table/_table.tsx +24 -21
  62. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_with_card_title_props.jsx +152 -0
  63. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_with_card_title_props.md +17 -0
  64. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_with_card_title_props_rails.html.erb +121 -0
  65. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_with_card_title_props_rails.md +17 -0
  66. data/app/pb_kits/playbook/pb_table/docs/example.yml +2 -0
  67. data/app/pb_kits/playbook/pb_table/docs/index.js +1 -0
  68. data/app/pb_kits/playbook/pb_table/table.html.erb +12 -11
  69. data/app/pb_kits/playbook/pb_table/table.rb +4 -0
  70. data/app/pb_kits/playbook/pb_table/table.test.js +33 -0
  71. data/app/pb_kits/playbook/pb_textarea/_textarea.scss +4 -1
  72. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +105 -3
  73. data/app/pb_kits/playbook/utilities/_display.scss +1 -0
  74. data/app/pb_kits/playbook/utilities/domHelpers.ts +50 -0
  75. data/dist/chunks/{_pb_line_graph-CKBPxTmM.js → _pb_line_graph-D6s5rymw.js} +1 -1
  76. data/dist/chunks/_typeahead-Bh0RF1X-.js +1 -0
  77. data/dist/chunks/componentRegistry-DRSp5D_e.js +1 -0
  78. data/dist/chunks/{globalProps-DLCfJwiU.js → globalProps-Ds_6HBhX.js} +1 -1
  79. data/dist/chunks/lib-BaO72ugL.js +29 -0
  80. data/dist/chunks/vendor.js +5 -5
  81. data/dist/menu.yml +3 -2
  82. data/dist/playbook-rails-react-bindings.js +1 -1
  83. data/dist/playbook-rails.js +1 -1
  84. data/dist/playbook.css +1 -1
  85. data/lib/playbook/pb_forms_helper.rb +3 -0
  86. data/lib/playbook/version.rb +2 -2
  87. metadata +32 -31
  88. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_attributes.html.erb +0 -5
  89. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_attributes.jsx +0 -15
  90. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_default.html.erb +0 -1
  91. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_focus.html.erb +0 -3
  92. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_focus.jsx +0 -17
  93. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_inline.html.erb +0 -6
  94. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_inline.jsx +0 -16
  95. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_label.jsx +0 -28
  96. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_label.md +0 -1
  97. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_preview.html.erb +0 -35
  98. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_preview.jsx +0 -45
  99. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_required_indicator.html.erb +0 -10
  100. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_required_indicator.jsx +0 -22
  101. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_required_indicator.md +0 -3
  102. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_simple.html.erb +0 -1
  103. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_simple.jsx +0 -13
  104. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_sticky.html.erb +0 -1
  105. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_sticky.jsx +0 -15
  106. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_templates.html.erb +0 -115
  107. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_templates.jsx +0 -42
  108. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_toolbar_bottom.html.erb +0 -4
  109. data/app/pb_kits/playbook/pb_rich_text_editor/docs/_rich_text_editor_toolbar_bottom.jsx +0 -14
  110. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.html.erb +0 -5
  111. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.rb +0 -63
  112. data/dist/chunks/_typeahead-B7bktFm6.js +0 -1
  113. data/dist/chunks/componentRegistry-DzmmLR2x.js +0 -1
  114. data/dist/chunks/lib-QT_7rPYf.js +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6ab403f777f646b3d7d608303239044ad0a7740e582035beccb22ddff5a24ce1
4
- data.tar.gz: d4dc694b6f85c099e9b6f53d512e8fa0adc96a428a9c0d5699745594c6cd2d93
3
+ metadata.gz: f4f468cd871371ba03a7e9cf2c14be1fbc8288c82d602c002d236c999249ae3e
4
+ data.tar.gz: 71dcca837b8732f4bab69f44d0ecbcc057ceaf2e711c249bcd88134e3cef6f0b
5
5
  SHA512:
6
- metadata.gz: ccb47fdb993c29446e42924ae80737f6f33d20ee3b9f6fb5ffe35fbfd97431edbb1743e958b418021030ec02f116eaf7de26675bf3d792e08ec4c7a25ee4ba0e
7
- data.tar.gz: a85463fbd073388ae9218fa64b56fa72b857883a1bf635154a5ba168cccbaea2d45e8f626b463d53042fded52f26556f87da8429f9b3e8ad742dac50324b7b9f
6
+ metadata.gz: d8d9241711a074c871ecdb5a42e34c9135b8203a2e4b99f39b00fc0b5cee6363a49508b0c3775db90158f28d266dabc2c65d6b70a9487b01917eda4b9813a089
7
+ data.tar.gz: c11eefcc856323279d5ad899ca305c3ba2eb28cd9c1ee575f4952c57e247a8de6e78eb9aec8323502010b35e51bff170551051580658364774ba3250a553f30c
@@ -11,6 +11,7 @@ import Icon from "../../pb_icon/_icon"
11
11
  import Checkbox from "../../pb_checkbox/_checkbox"
12
12
 
13
13
  import AdvancedTableContext from "../Context/AdvancedTableContext"
14
+ import { getDescendantRowIds } from "../Utilities/ExpansionControlHelpers"
14
15
 
15
16
  interface CustomCellProps {
16
17
  getValue?: Getter<string>
@@ -31,13 +32,25 @@ export const CustomCell = ({
31
32
  selectableRows,
32
33
  customStyle = {},
33
34
  }: CustomCellProps & GlobalProps) => {
34
- const { setExpanded, expanded, expandedControl, inlineRowLoading, hasAnySubRows } = useContext(AdvancedTableContext);
35
+ const { setExpanded, expanded, expandedControl, inlineRowLoading, hasAnySubRows, cascadeCollapse } = useContext(AdvancedTableContext);
35
36
 
36
37
  const handleOnExpand = (row: Row<GenericObject>) => {
37
38
  onRowToggleClick && onRowToggleClick(row);
38
-
39
- if (!expandedControl) {
40
- setExpanded({ ...expanded, [row.id]: !row.getIsExpanded() });
39
+
40
+ const willBeExpanded = !row.getIsExpanded();
41
+ if (willBeExpanded) {
42
+ if (!expandedControl) {
43
+ setExpanded({ ...expanded, [row.id]: true });
44
+ }
45
+ } else {
46
+ if (cascadeCollapse) {
47
+ const idsToRemove = new Set([row.id, ...getDescendantRowIds(row)]);
48
+ const nextExpanded = { ...expanded };
49
+ idsToRemove.forEach((id) => delete nextExpanded[id]);
50
+ setExpanded(nextExpanded);
51
+ } else if (!expandedControl) {
52
+ setExpanded({ ...expanded, [row.id]: false });
53
+ }
41
54
  }
42
55
  };
43
56
 
@@ -63,6 +63,7 @@ export const TableHeaderCell = ({
63
63
  stickyLeftColumn,
64
64
  inlineRowLoading,
65
65
  isActionBarVisible,
66
+ cascadeCollapse,
66
67
  } = useContext(AdvancedTableContext);
67
68
 
68
69
  type justifyTypes = "none" | "center" | "start" | "end" | "between" | "around" | "evenly"
@@ -182,7 +183,8 @@ const isToggleExpansionEnabled =
182
183
  table.getRowModel(),
183
184
  expanded,
184
185
  undefined,
185
- depth
186
+ depth,
187
+ cascadeCollapse
186
188
  )
187
189
  setExpanded(updated)
188
190
  }
@@ -12,6 +12,7 @@ interface UseTableActionsProps {
12
12
  inlineRowLoading?: boolean;
13
13
  localPagination?: { pageIndex: number; pageSize: number };
14
14
  setLocalPagination?: (pagination: { pageIndex: number; pageSize: number }) => void;
15
+ cascadeCollapse?: boolean;
15
16
  }
16
17
 
17
18
  export function useTableActions({
@@ -22,7 +23,8 @@ export function useTableActions({
22
23
  onRowSelectionChange,
23
24
  inlineRowLoading = false,
24
25
  localPagination,
25
- setLocalPagination
26
+ setLocalPagination,
27
+ cascadeCollapse = false
26
28
  }: UseTableActionsProps) {
27
29
 
28
30
  // State to achieve 1 second delay before fetching more rows
@@ -32,15 +34,25 @@ export function useTableActions({
32
34
  // Handle expand/collapse
33
35
  const handleExpandOrCollapse = useCallback(async (row: Row<GenericObject>) => {
34
36
  if (onToggleExpansionClick) onToggleExpansionClick(row)
35
- const updatedExpandedState = await updateExpandAndCollapseState(
36
- table.getRowModel(),
37
- expanded,
38
- row?.parentId,
39
- undefined
40
- )
41
37
 
42
- setExpanded(updatedExpandedState)
43
- }, [expanded, setExpanded, onToggleExpansionClick, table]);
38
+ const anyTopLevelExpanded = table.getRowModel().rows.some((r: Row<GenericObject>) => r.getIsExpanded())
39
+ const isHeaderCollapseAll = row == null && anyTopLevelExpanded
40
+
41
+ if (cascadeCollapse && isHeaderCollapseAll) {
42
+ setExpanded({})
43
+ return
44
+ }
45
+
46
+ const updatedExpandedState = await updateExpandAndCollapseState(
47
+ table.getRowModel(),
48
+ expanded,
49
+ row?.parentId,
50
+ undefined,
51
+ cascadeCollapse
52
+ )
53
+
54
+ setExpanded(updatedExpandedState)
55
+ }, [expanded, setExpanded, onToggleExpansionClick, table, cascadeCollapse]);
44
56
 
45
57
  // Handle pagination
46
58
  const onPageChange = useCallback((page: number) => {
@@ -12,6 +12,7 @@ import {
12
12
  import { GenericObject } from "../../types";
13
13
  import { createColumnHelper } from "@tanstack/react-table";
14
14
  import { createCellFunction } from "../Utilities/CellRendererUtils";
15
+ import { getParentOnlySortedRowModel } from "../Utilities/RowModelUtils";
15
16
 
16
17
  interface UseTableStateProps {
17
18
  tableData: GenericObject[];
@@ -36,6 +37,7 @@ interface UseTableStateProps {
36
37
  columnVisibilityControl?: GenericObject;
37
38
  rowStyling?: GenericObject;
38
39
  inlineRowLoading?: boolean;
40
+ sortParentOnly?: boolean;
39
41
  }
40
42
 
41
43
  export function useTableState({
@@ -55,7 +57,8 @@ export function useTableState({
55
57
  columnVisibilityControl,
56
58
  pinnedRows,
57
59
  rowStyling,
58
- inlineRowLoading = false
60
+ inlineRowLoading = false,
61
+ sortParentOnly = false
59
62
  }: UseTableStateProps) {
60
63
 
61
64
  // Create a local state for expanded and setExpanded if expandedControl not used
@@ -190,7 +193,7 @@ export function useTableState({
190
193
  getSubRows: (row: GenericObject) => row.children,
191
194
  getCoreRowModel: getCoreRowModel(),
192
195
  getExpandedRowModel: getExpandedRowModel(),
193
- getSortedRowModel: getSortedRowModel(),
196
+ getSortedRowModel: sortParentOnly ? getParentOnlySortedRowModel() : getSortedRowModel(),
194
197
  enableSortingRemoval: enableSortingRemoval,
195
198
  sortDescFirst: true,
196
199
  onRowSelectionChange: setRowSelection,
@@ -11,11 +11,21 @@ const filterExpandableRows = (expandedState: Record<string, boolean>) => {
11
11
  return expandedState
12
12
  }
13
13
 
14
+ export const getDescendantRowIds = (row: Row<GenericObject>): string[] => {
15
+ const ids: string[] = []
16
+ for (const sub of row.subRows || []) {
17
+ ids.push(sub.id)
18
+ ids.push(...getDescendantRowIds(sub))
19
+ }
20
+ return ids
21
+ }
22
+
14
23
  export const updateExpandAndCollapseState = (
15
24
  tableRows: RowModel<GenericObject>,
16
25
  expanded: Record<string, boolean>,
17
26
  targetParent?: string,
18
27
  targetDepth?: number,
28
+ cascadeCollapse?: boolean,
19
29
  ) => {
20
30
  const updateExpandedRows: Record<string, boolean> = {};
21
31
  const rows = targetDepth !== undefined ? tableRows.flatRows : tableRows.rows;
@@ -51,8 +61,22 @@ export const updateExpandAndCollapseState = (
51
61
  }
52
62
  }
53
63
 
54
- return filterExpandableRows({
64
+ const updatedExpandedState = filterExpandableRows({
55
65
  ...(expanded as ExpandedStateObject),
56
66
  ...updateExpandedRows,
57
67
  });
68
+
69
+ if (cascadeCollapse && !isExpandAction) {
70
+ const idsToRemove = new Set<string>();
71
+ for (const row of rowsToToggle) {
72
+ const shouldUpdate =
73
+ targetDepth === undefined ? true : row.depth === targetDepth;
74
+ if (shouldUpdate) {
75
+ getDescendantRowIds(row).forEach((id) => idsToRemove.add(id));
76
+ }
77
+ }
78
+ idsToRemove.forEach((id) => delete updatedExpandedState[id]);
79
+ }
80
+
81
+ return updatedExpandedState;
58
82
  };
@@ -0,0 +1,100 @@
1
+ // Returns a row model getter that sorts only depth-0 (parent) rows so children and grandchild rows keep their original order under each parent.
2
+
3
+ import type { Table, Row, RowModel, RowData } from "@tanstack/react-table";
4
+
5
+ export function getParentOnlySortedRowModel<TData extends RowData>(): (
6
+ table: Table<TData>
7
+ ) => () => RowModel<TData> {
8
+ return (table) => () => {
9
+ const sortingState = table.getState().sorting;
10
+ const rowModel = table.getPreSortedRowModel();
11
+
12
+ if (!rowModel.rows.length || !sortingState?.length) {
13
+ return rowModel;
14
+ }
15
+
16
+ const sortedFlatRows: Row<TData>[] = [];
17
+ const availableSorting = sortingState.filter((sort) =>
18
+ table.getColumn(sort.id)?.getCanSort()
19
+ );
20
+
21
+ const columnInfoById: Record<
22
+ string,
23
+ {
24
+ sortUndefined?: false | -1 | 1 | "first" | "last";
25
+ invertSorting?: boolean;
26
+ sortingFn: (rowA: Row<TData>, rowB: Row<TData>, columnId: string) => number;
27
+ }
28
+ > = {};
29
+
30
+ availableSorting.forEach((sortEntry) => {
31
+ const column = table.getColumn(sortEntry.id);
32
+ if (!column) return;
33
+ columnInfoById[sortEntry.id] = {
34
+ sortUndefined: column.columnDef.sortUndefined,
35
+ invertSorting: column.columnDef.invertSorting,
36
+ sortingFn: column.getSortingFn(),
37
+ };
38
+ });
39
+
40
+ const parentRows = rowModel.rows.map((row) => ({ ...row }));
41
+ parentRows.sort((rowA, rowB) => {
42
+ for (let i = 0; i < availableSorting.length; i += 1) {
43
+ const sortEntry = availableSorting[i]!;
44
+ const columnInfo = columnInfoById[sortEntry.id]!;
45
+ const sortUndefined = columnInfo.sortUndefined;
46
+ const isDesc = sortEntry?.desc ?? false;
47
+ let sortInt = 0;
48
+
49
+ if (sortUndefined) {
50
+ const aValue = rowA.getValue(sortEntry.id);
51
+ const bValue = rowB.getValue(sortEntry.id);
52
+ const aUndefined = aValue === undefined;
53
+ const bUndefined = bValue === undefined;
54
+ if (aUndefined || bUndefined) {
55
+ if (sortUndefined === "first") return aUndefined ? -1 : 1;
56
+ if (sortUndefined === "last") return aUndefined ? 1 : -1;
57
+ sortInt =
58
+ aUndefined && bUndefined
59
+ ? 0
60
+ : aUndefined
61
+ ? sortUndefined
62
+ : -sortUndefined;
63
+ }
64
+ }
65
+
66
+ if (sortInt === 0) {
67
+ sortInt = columnInfo.sortingFn(rowA, rowB, sortEntry.id);
68
+ }
69
+
70
+ if (sortInt !== 0) {
71
+ if (isDesc) sortInt *= -1;
72
+ if (columnInfo.invertSorting) sortInt *= -1;
73
+ return sortInt;
74
+ }
75
+ }
76
+ return rowA.index - rowB.index;
77
+ });
78
+
79
+ function flattenRowsInOrder(rows: Row<TData>[]): void {
80
+ rows.forEach((row) => {
81
+ sortedFlatRows.push(row);
82
+ if (row.subRows?.length) {
83
+ flattenRowsInOrder(row.subRows);
84
+ }
85
+ });
86
+ }
87
+ flattenRowsInOrder(parentRows);
88
+
89
+ const rowsById: Record<string, Row<TData>> = {};
90
+ sortedFlatRows.forEach((row) => {
91
+ rowsById[row.id] = row;
92
+ });
93
+
94
+ return {
95
+ rows: parentRows,
96
+ flatRows: sortedFlatRows,
97
+ rowsById,
98
+ };
99
+ };
100
+ }
@@ -31,6 +31,7 @@ type FullscreenControls = {
31
31
  type AdvancedTableProps = {
32
32
  aria?: { [key: string]: string }
33
33
  actions?: React.ReactNode[] | React.ReactNode
34
+ cascadeCollapse?: boolean
34
35
  children?: React.ReactNode | React.ReactNode[]
35
36
  className?: string
36
37
  columnDefinitions: GenericObject[]
@@ -65,6 +66,7 @@ type AdvancedTableProps = {
65
66
  showActionsBar?: boolean,
66
67
  persistToggleExpansionButton?: boolean,
67
68
  sortControl?: GenericObject
69
+ sortParentOnly?: boolean
68
70
  tableData: GenericObject[]
69
71
  tableOptions?: GenericObject
70
72
  tableProps?: GenericObject
@@ -80,6 +82,7 @@ const AdvancedTable = (props: AdvancedTableProps) => {
80
82
  const {
81
83
  aria = {},
82
84
  actions,
85
+ cascadeCollapse = false,
83
86
  children,
84
87
  className,
85
88
  columnDefinitions,
@@ -112,6 +115,7 @@ const AdvancedTable = (props: AdvancedTableProps) => {
112
115
  selectableRows,
113
116
  persistToggleExpansionButton = false,
114
117
  sortControl,
118
+ sortParentOnly = false,
115
119
  stickyLeftColumn,
116
120
  tableData,
117
121
  tableOptions,
@@ -157,7 +161,8 @@ const AdvancedTable = (props: AdvancedTableProps) => {
157
161
  columnVisibilityControl,
158
162
  pinnedRows,
159
163
  rowStyling,
160
- inlineRowLoading
164
+ inlineRowLoading,
165
+ sortParentOnly
161
166
  });
162
167
 
163
168
  // Initialize table actions
@@ -173,7 +178,8 @@ const AdvancedTable = (props: AdvancedTableProps) => {
173
178
  onRowSelectionChange,
174
179
  inlineRowLoading,
175
180
  localPagination,
176
- setLocalPagination
181
+ setLocalPagination,
182
+ cascadeCollapse
177
183
  });
178
184
 
179
185
  // Set table row count for loading state
@@ -339,6 +345,7 @@ const AdvancedTable = (props: AdvancedTableProps) => {
339
345
  >
340
346
  {renderFullscreenHeader()}
341
347
  <AdvancedTableProvider
348
+ cascadeCollapse={cascadeCollapse}
342
349
  columnDefinitions={columnDefinitions}
343
350
  columnGroupBorderColor={columnGroupBorderColor}
344
351
  columnVisibilityControl={columnVisibilityControl}
@@ -495,7 +495,41 @@ test("sort button exists and sorts column data", () => {
495
495
 
496
496
  const row2 = kit.getElementsByTagName('tr')[2]
497
497
  expect(row2.id).toBe("0-0-0-row")
498
- })
498
+ })
499
+
500
+ test("sortParentOnly sorts only parent rows and keeps children grouped under parent", () => {
501
+ render(
502
+ <AdvancedTable
503
+ columnDefinitions={columnDefinitions}
504
+ data={{ testid: testId }}
505
+ sortParentOnly
506
+ tableData={MOCK_DATA}
507
+ >
508
+ <AdvancedTable.Header enableSorting />
509
+ <AdvancedTable.Body />
510
+ </AdvancedTable>
511
+ )
512
+
513
+ const kit = screen.getByTestId(testId)
514
+ const sortButton = kit.querySelector(".header-sort-button.pb_th_link")
515
+ expect(sortButton).toBeInTheDocument()
516
+
517
+ const tbody = kit.querySelector('tbody')
518
+ const rowsBefore = tbody.getElementsByTagName('tr')
519
+ expect(rowsBefore[0]).toHaveTextContent('2021')
520
+
521
+ sortButton.click()
522
+
523
+ const rowsAfter = tbody.getElementsByTagName('tr')
524
+ expect(rowsAfter[0]).toHaveTextContent('2022')
525
+
526
+ const expandButton = kit.querySelector(".gray-icon.expand-toggle-icon")
527
+ expandButton.click()
528
+
529
+ const rowsExpanded = tbody.getElementsByTagName('tr')
530
+ expect(rowsExpanded.length).toBeGreaterThan(1)
531
+ expect(rowsExpanded[1]).toHaveTextContent('Q1')
532
+ })
499
533
 
500
534
  test("Generates Table.Header default + custom classname", () => {
501
535
  render(
@@ -1015,4 +1049,77 @@ test("columnStyling.headerFontColor works as excpected", () => {
1015
1049
 
1016
1050
  const firstEnrollmentHeader = screen.getAllByText("New Enrollments")[0].closest("th");
1017
1051
  expect(firstEnrollmentHeader).toHaveStyle({ color: colors.white });
1018
- });
1052
+ });
1053
+
1054
+ test("cascadeCollapse=false (default) preserves existing behavior when parent is re-expanded", () => {
1055
+ render(
1056
+ <AdvancedTable
1057
+ columnDefinitions={columnDefinitions}
1058
+ data={{ testid: testId }}
1059
+ tableData={MOCK_DATA}
1060
+ />
1061
+ )
1062
+
1063
+ const kit = screen.getByTestId(testId)
1064
+ const getParentExpandButton = () => kit.querySelector("tbody tr .gray-icon.expand-toggle-icon")
1065
+ const parentButton = getParentExpandButton()
1066
+ expect(parentButton).toBeInTheDocument()
1067
+ parentButton.click()
1068
+ let subRow = kit.querySelector(".pb-bg-row-white.depth-sub-row-1")
1069
+ expect(subRow).toBeInTheDocument()
1070
+ getParentExpandButton().click()
1071
+ subRow = kit.querySelector(".pb-bg-row-white.depth-sub-row-1")
1072
+ expect(subRow).not.toBeInTheDocument()
1073
+ getParentExpandButton().click()
1074
+ subRow = kit.querySelector(".pb-bg-row-white.depth-sub-row-1")
1075
+ expect(subRow).toBeInTheDocument()
1076
+ })
1077
+
1078
+ test("cascadeCollapse=true collapses all descendants when parent is collapsed", () => {
1079
+ render(
1080
+ <AdvancedTable
1081
+ cascadeCollapse
1082
+ columnDefinitions={columnDefinitions}
1083
+ data={{ testid: testId }}
1084
+ tableData={MOCK_DATA}
1085
+ />
1086
+ )
1087
+
1088
+ const kit = screen.getByTestId(testId)
1089
+ const getParentExpandButton = () => kit.querySelector("tbody tr .gray-icon.expand-toggle-icon")
1090
+ const parentButton = getParentExpandButton()
1091
+ expect(parentButton).toBeInTheDocument()
1092
+ parentButton.click()
1093
+ expect(kit.querySelector(".depth-sub-row-1")).toBeInTheDocument()
1094
+ getParentExpandButton().click()
1095
+ expect(kit.querySelector(".depth-sub-row-1")).not.toBeInTheDocument()
1096
+ getParentExpandButton().click()
1097
+ expect(kit.querySelector(".depth-sub-row-1")).toBeInTheDocument()
1098
+ })
1099
+
1100
+ test("cascadeCollapse=true with header toggle all: collapse all then expand all shows only direct children", async () => {
1101
+ render(
1102
+ <AdvancedTable
1103
+ cascadeCollapse
1104
+ columnDefinitions={columnDefinitions}
1105
+ data={{ testid: testId }}
1106
+ tableData={MOCK_DATA}
1107
+ />
1108
+ )
1109
+
1110
+ const kit = screen.getByTestId(testId)
1111
+ const toggleAllButton = kit.querySelector(".gray-icon.toggle-all-icon")
1112
+ expect(toggleAllButton).toBeInTheDocument()
1113
+ toggleAllButton.click()
1114
+ await waitFor(() => {
1115
+ expect(kit.querySelector(".depth-sub-row-1")).toBeInTheDocument()
1116
+ })
1117
+ toggleAllButton.click()
1118
+ await waitFor(() => {
1119
+ expect(kit.querySelector(".depth-sub-row-1")).not.toBeInTheDocument()
1120
+ })
1121
+ toggleAllButton.click()
1122
+ await waitFor(() => {
1123
+ expect(kit.querySelector(".depth-sub-row-1")).toBeInTheDocument()
1124
+ })
1125
+ })
@@ -0,0 +1,50 @@
1
+ import React from "react"
2
+ import AdvancedTable from '../../pb_advanced_table/_advanced_table'
3
+ import MOCK_DATA from "./advanced_table_mock_data.json"
4
+
5
+ const AdvancedTableCascadeCollapse = (props) => {
6
+ const columnDefinitions = [
7
+ {
8
+ accessor: "year",
9
+ label: "Year",
10
+ cellAccessors: ["quarter", "month", "day"],
11
+ },
12
+ {
13
+ accessor: "newEnrollments",
14
+ label: "New Enrollments",
15
+ },
16
+ {
17
+ accessor: "scheduledMeetings",
18
+ label: "Scheduled Meetings",
19
+ },
20
+ {
21
+ accessor: "attendanceRate",
22
+ label: "Attendance Rate",
23
+ },
24
+ {
25
+ accessor: "completedClasses",
26
+ label: "Completed Classes",
27
+ },
28
+ {
29
+ accessor: "classCompletionRate",
30
+ label: "Class Completion Rate",
31
+ },
32
+ {
33
+ accessor: "graduatedStudents",
34
+ label: "Graduated Students",
35
+ },
36
+ ]
37
+
38
+ return (
39
+ <div>
40
+ <AdvancedTable
41
+ cascadeCollapse
42
+ columnDefinitions={columnDefinitions}
43
+ tableData={MOCK_DATA}
44
+ {...props}
45
+ />
46
+ </div>
47
+ )
48
+ }
49
+
50
+ export default AdvancedTableCascadeCollapse
@@ -0,0 +1 @@
1
+ `cascadeCollapse` is an optional prop that is set to 'false' by default. If set to 'true', collapsing any parent row itself or by using the toggle exapansion buttons in any header or subheader row also collapses all descendants and clears their expansion state. Re-expanding then shows only direct children until the user expands deeper levels again.