playbook_ui 16.2.0 → 16.3.0.pre.alpha.PLAY2779dropdowncustomeventtype14814

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 (161) 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/Utilities/ExpansionControlHelpers.tsx +25 -1
  6. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +5 -1
  7. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +74 -1
  8. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_cascade_collapse.jsx +50 -0
  9. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_cascade_collapse.md +1 -0
  10. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +1 -0
  11. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +2 -1
  12. data/app/pb_kits/playbook/pb_advanced_table/index.js +48 -29
  13. data/app/pb_kits/playbook/pb_body/_body.scss +4 -4
  14. data/app/pb_kits/playbook/pb_body/_body_mixins.scss +28 -28
  15. data/app/pb_kits/playbook/pb_button/_button.scss +3 -2
  16. data/app/pb_kits/playbook/pb_button/_button_mixins.scss +6 -1
  17. data/app/pb_kits/playbook/pb_caption/_caption.scss +1 -1
  18. data/app/pb_kits/playbook/pb_caption/_caption_mixin.scss +15 -15
  19. data/app/pb_kits/playbook/pb_caption/docs/_caption_colors.html.erb +6 -4
  20. data/app/pb_kits/playbook/pb_caption/docs/_caption_colors.jsx +14 -4
  21. data/app/pb_kits/playbook/pb_checkbox/_checkbox.scss +18 -18
  22. data/app/pb_kits/playbook/pb_collapsible/index.js +31 -30
  23. data/app/pb_kits/playbook/pb_date_picker/_date_picker.scss +7 -7
  24. data/app/pb_kits/playbook/pb_date_picker/_date_picker.tsx +7 -1
  25. data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +2 -2
  26. data/app/pb_kits/playbook/pb_date_picker/date_picker.rb +1 -0
  27. data/app/pb_kits/playbook/pb_date_picker/docs/_date_picker_positions.md +1 -0
  28. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_calendar_input_icon.scss +14 -6
  29. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_input_styles.scss +80 -5
  30. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_time_selection_styles.scss +8 -1
  31. data/app/pb_kits/playbook/pb_detail/_detail.scss +14 -14
  32. data/app/pb_kits/playbook/pb_detail/_detail_mixins.scss +13 -13
  33. data/app/pb_kits/playbook/pb_detail/docs/_detail_colors.html.erb +6 -24
  34. data/app/pb_kits/playbook/pb_detail/docs/_detail_colors.jsx +35 -33
  35. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_compound_components.md +3 -1
  36. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_default.html.erb +18 -9
  37. data/app/pb_kits/playbook/pb_dialog/docs/_dialog_default.jsx +24 -5
  38. data/app/pb_kits/playbook/pb_dialog/index.js +45 -5
  39. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +34 -25
  40. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +15 -3
  41. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_closing_options.jsx +63 -0
  42. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_closing_options.md +1 -0
  43. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_closing_options_rails.html.erb +16 -0
  44. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_closing_options_rails.md +1 -0
  45. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_custom_event_type.html.erb +224 -0
  46. data/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_custom_event_type.md +7 -0
  47. data/app/pb_kits/playbook/pb_dropdown/docs/example.yml +3 -0
  48. data/app/pb_kits/playbook/pb_dropdown/docs/index.js +1 -0
  49. data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +2 -2
  50. data/app/pb_kits/playbook/pb_dropdown/dropdown.rb +8 -1
  51. data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +1 -1
  52. data/app/pb_kits/playbook/pb_dropdown/index.js +320 -56
  53. data/app/pb_kits/playbook/pb_dropdown/keyboard_accessibility.js +19 -3
  54. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownTrigger.tsx +19 -14
  55. data/app/pb_kits/playbook/pb_dropdown/utilities/clickOutsideHelper.tsx +7 -1
  56. data/app/pb_kits/playbook/pb_enhanced_element/element_observer.ts +1 -1
  57. data/app/pb_kits/playbook/pb_enhanced_element/index.ts +2 -1
  58. data/app/pb_kits/playbook/pb_form/docs/_form_form_with.html.erb +2 -1
  59. data/app/pb_kits/playbook/pb_form/docs/_form_with_required_indicator.html.erb +2 -0
  60. data/app/pb_kits/playbook/pb_icon/_icon.scss +5 -1
  61. data/app/pb_kits/playbook/pb_icon/icon.rb +168 -19
  62. data/app/pb_kits/playbook/pb_kit_registry/index.ts +180 -0
  63. data/app/pb_kits/playbook/pb_link/_link.scss +16 -1
  64. data/app/pb_kits/playbook/pb_link/docs/_link_underline.jsx +31 -1
  65. data/app/pb_kits/playbook/pb_list/_list_mixin.scss +4 -4
  66. data/app/pb_kits/playbook/pb_multi_level_select/_helper_functions.tsx +1 -1
  67. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +28 -10
  68. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +27 -17
  69. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_react_reset_key.jsx +100 -0
  70. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_react_reset_key.md +1 -0
  71. data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +1 -0
  72. data/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +1 -0
  73. data/app/pb_kits/playbook/pb_pagination/_pagination.scss +101 -1
  74. data/app/pb_kits/playbook/pb_pagination/_pagination.test.jsx +172 -1
  75. data/app/pb_kits/playbook/pb_pagination/_pagination.tsx +178 -15
  76. data/app/pb_kits/playbook/pb_pagination/docs/_pagination_default.jsx +1 -1
  77. data/app/pb_kits/playbook/pb_passphrase/_passphrase.scss +14 -1
  78. data/app/pb_kits/playbook/pb_passphrase/_passphrase.tsx +5 -2
  79. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.scss +11 -11
  80. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input.rb +2 -0
  81. data/app/pb_kits/playbook/pb_radio/_radio.scss +17 -17
  82. data/app/pb_kits/playbook/pb_rich_text_editor/_previewer_mixin.scss +6 -6
  83. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.scss +31 -32
  84. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.tsx +2 -0
  85. data/app/pb_kits/playbook/pb_rich_text_editor/_tiptap_styles.scss +5 -5
  86. data/app/pb_kits/playbook/pb_rich_text_editor/rich_text_editor.html.erb +1 -0
  87. data/app/pb_kits/playbook/pb_select/_select.scss +61 -51
  88. data/app/pb_kits/playbook/pb_select/_select.tsx +28 -18
  89. data/app/pb_kits/playbook/pb_select/docs/_select_required_indicator.html.erb +24 -0
  90. data/app/pb_kits/playbook/pb_select/docs/_select_required_indicator.jsx +33 -0
  91. data/app/pb_kits/playbook/pb_select/docs/_select_required_indicator.md +3 -0
  92. data/app/pb_kits/playbook/pb_select/docs/example.yml +3 -2
  93. data/app/pb_kits/playbook/pb_select/docs/index.js +1 -0
  94. data/app/pb_kits/playbook/pb_select/select.html.erb +7 -1
  95. data/app/pb_kits/playbook/pb_select/select.rb +3 -0
  96. data/app/pb_kits/playbook/pb_selectable_card/_selectable_card.scss +69 -20
  97. data/app/pb_kits/playbook/pb_selectable_icon/_selectable_icon.scss +6 -5
  98. data/app/pb_kits/playbook/pb_star_rating/star_rating.html.erb +1 -1
  99. data/app/pb_kits/playbook/pb_star_rating/subcomponents/_star_rating_interactive.tsx +1 -0
  100. data/app/pb_kits/playbook/pb_table/_table.tsx +24 -21
  101. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_with_card_title_props.jsx +152 -0
  102. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_with_card_title_props.md +17 -0
  103. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_with_card_title_props_rails.html.erb +121 -0
  104. data/app/pb_kits/playbook/pb_table/docs/_table_with_filter_with_card_title_props_rails.md +17 -0
  105. data/app/pb_kits/playbook/pb_table/docs/example.yml +2 -0
  106. data/app/pb_kits/playbook/pb_table/docs/index.js +1 -0
  107. data/app/pb_kits/playbook/pb_table/table.html.erb +12 -11
  108. data/app/pb_kits/playbook/pb_table/table.rb +4 -0
  109. data/app/pb_kits/playbook/pb_table/table.test.js +33 -0
  110. data/app/pb_kits/playbook/pb_text_input/_text_input.scss +51 -20
  111. data/app/pb_kits/playbook/pb_text_input/_text_input.tsx +11 -4
  112. data/app/pb_kits/playbook/pb_text_input/docs/_text_input_disabled.jsx +11 -13
  113. data/app/pb_kits/playbook/pb_text_input/text_input.html.erb +2 -2
  114. data/app/pb_kits/playbook/pb_textarea/_textarea.scss +59 -13
  115. data/app/pb_kits/playbook/pb_textarea/_textarea.tsx +43 -21
  116. data/app/pb_kits/playbook/pb_textarea/_textarea_mixin.scss +31 -10
  117. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_disabled.html.erb +10 -0
  118. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_disabled.jsx +27 -0
  119. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_input_options.jsx +68 -0
  120. data/app/pb_kits/playbook/pb_textarea/docs/example.yml +3 -0
  121. data/app/pb_kits/playbook/pb_textarea/docs/index.js +10 -8
  122. data/app/pb_kits/playbook/pb_textarea/textarea.html.erb +4 -4
  123. data/app/pb_kits/playbook/pb_textarea/textarea.rb +9 -2
  124. data/app/pb_kits/playbook/pb_textarea/textarea.test.js +134 -1
  125. data/app/pb_kits/playbook/pb_time_picker/_time_picker.scss +186 -25
  126. data/app/pb_kits/playbook/pb_time_picker/_time_picker.tsx +22 -1
  127. data/app/pb_kits/playbook/pb_time_picker/time_picker.rb +8 -1
  128. data/app/pb_kits/playbook/pb_time_picker/time_picker.test.jsx +2 -2
  129. data/app/pb_kits/playbook/pb_title/_title_mixin.scss +13 -13
  130. data/app/pb_kits/playbook/pb_title/docs/_title_colors.html.erb +2 -0
  131. data/app/pb_kits/playbook/pb_title/docs/_title_colors.jsx +14 -0
  132. data/app/pb_kits/playbook/pb_tooltip/index.js +60 -15
  133. data/app/pb_kits/playbook/pb_typeahead/_typeahead.scss +66 -23
  134. data/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx +1 -1
  135. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +105 -3
  136. data/app/pb_kits/playbook/tokens/_colors_accessible.scss +5 -1
  137. data/app/pb_kits/playbook/tokens/_titles.scss +1 -1
  138. data/app/pb_kits/playbook/utilities/domHelpers.ts +50 -0
  139. data/dist/chunks/{_pb_line_graph-CsDClWC8.js → _pb_line_graph-BGY7jEks.js} +1 -1
  140. data/dist/chunks/_typeahead-DdGKR1rQ.js +1 -0
  141. data/dist/chunks/componentRegistry-DRSp5D_e.js +1 -0
  142. data/dist/chunks/{globalProps-CW_a2wOO.js → globalProps-CK2YuA9O.js} +1 -1
  143. data/dist/chunks/lib-DspaUdlc.js +29 -0
  144. data/dist/chunks/vendor.js +5 -5
  145. data/dist/menu.yml +2 -1
  146. data/dist/playbook-rails-react-bindings.js +1 -1
  147. data/dist/playbook-rails.js +1 -1
  148. data/dist/playbook.css +1 -1
  149. data/lib/playbook/forms/builder/checkbox_field.rb +1 -1
  150. data/lib/playbook/forms/builder/collection_select_field.rb +7 -1
  151. data/lib/playbook/forms/builder/intl_telephone_field.rb +11 -1
  152. data/lib/playbook/forms/builder/phone_number_field.rb +3 -2
  153. data/lib/playbook/forms/builder/select_field.rb +7 -1
  154. data/lib/playbook/forms/builder/time_zone_select_field.rb +7 -1
  155. data/lib/playbook/pb_forms_helper.rb +3 -0
  156. data/lib/playbook/tokens/colors.json +2 -0
  157. data/lib/playbook/version.rb +2 -2
  158. metadata +29 -7
  159. data/dist/chunks/_typeahead-E5Ps8hIY.js +0 -1
  160. data/dist/chunks/componentRegistry-DzmmLR2x.js +0 -1
  161. data/dist/chunks/lib-DHuT-Q9y.js +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 30bb9f2e6d1d3ac26193857757c26d6e8cdcdd594907f1cb00f66a44d01b06fd
4
- data.tar.gz: f3e563f423c8667d56863435a56d6628f2f74c5d1bb36685c77758583feff8d1
3
+ metadata.gz: 35ea148133e067dd35dfe27d5f662f9e55dd94a8ce5e8413efd8234405e7f6ec
4
+ data.tar.gz: dd608024f4e264422496a04d6f6946a67b240117359e1f852926f9af7223ecaf
5
5
  SHA512:
6
- metadata.gz: f3399019b7aea00c3793f9609e6223164845a79400635013acc22826b79e2150be832fe121472cc18d6974d9bb9cb7f03aa3d1ebf73cc7aae79ca7d6fcd4552d
7
- data.tar.gz: 758485999cd223ddcf82d780c8684663b32b2a27c90a436ffc71acc416a81ae0a4ae2438b2e47b3f9a3a7dadacdb71ed3296cac0abb1c860aed7175d4b6d7fa2
6
+ metadata.gz: d5ba6c0803ad1c8e0332b8c639c50eaece52996d7a482754f630a7473ee3b9c1c6d2c3feb5984ab4fe9c96ef6e8afff7d799cfab494bb1f4f340ecebf29fa677
7
+ data.tar.gz: 49744400d43e8113480de8ca7977b479f1cf59dcda6443546a1535c46ff3577f47e017364571ff7f76cce70b489fbfa44fa84b7f25e2e6446071919fd1d9f15e
@@ -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) => {
@@ -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
  };
@@ -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[]
@@ -80,6 +81,7 @@ const AdvancedTable = (props: AdvancedTableProps) => {
80
81
  const {
81
82
  aria = {},
82
83
  actions,
84
+ cascadeCollapse = false,
83
85
  children,
84
86
  className,
85
87
  columnDefinitions,
@@ -173,7 +175,8 @@ const AdvancedTable = (props: AdvancedTableProps) => {
173
175
  onRowSelectionChange,
174
176
  inlineRowLoading,
175
177
  localPagination,
176
- setLocalPagination
178
+ setLocalPagination,
179
+ cascadeCollapse
177
180
  });
178
181
 
179
182
  // Set table row count for loading state
@@ -339,6 +342,7 @@ const AdvancedTable = (props: AdvancedTableProps) => {
339
342
  >
340
343
  {renderFullscreenHeader()}
341
344
  <AdvancedTableProvider
345
+ cascadeCollapse={cascadeCollapse}
342
346
  columnDefinitions={columnDefinitions}
343
347
  columnGroupBorderColor={columnGroupBorderColor}
344
348
  columnVisibilityControl={columnVisibilityControl}
@@ -1015,4 +1015,77 @@ test("columnStyling.headerFontColor works as excpected", () => {
1015
1015
 
1016
1016
  const firstEnrollmentHeader = screen.getAllByText("New Enrollments")[0].closest("th");
1017
1017
  expect(firstEnrollmentHeader).toHaveStyle({ color: colors.white });
1018
- });
1018
+ });
1019
+
1020
+ test("cascadeCollapse=false (default) preserves existing behavior when parent is re-expanded", () => {
1021
+ render(
1022
+ <AdvancedTable
1023
+ columnDefinitions={columnDefinitions}
1024
+ data={{ testid: testId }}
1025
+ tableData={MOCK_DATA}
1026
+ />
1027
+ )
1028
+
1029
+ const kit = screen.getByTestId(testId)
1030
+ const getParentExpandButton = () => kit.querySelector("tbody tr .gray-icon.expand-toggle-icon")
1031
+ const parentButton = getParentExpandButton()
1032
+ expect(parentButton).toBeInTheDocument()
1033
+ parentButton.click()
1034
+ let subRow = kit.querySelector(".pb-bg-row-white.depth-sub-row-1")
1035
+ expect(subRow).toBeInTheDocument()
1036
+ getParentExpandButton().click()
1037
+ subRow = kit.querySelector(".pb-bg-row-white.depth-sub-row-1")
1038
+ expect(subRow).not.toBeInTheDocument()
1039
+ getParentExpandButton().click()
1040
+ subRow = kit.querySelector(".pb-bg-row-white.depth-sub-row-1")
1041
+ expect(subRow).toBeInTheDocument()
1042
+ })
1043
+
1044
+ test("cascadeCollapse=true collapses all descendants when parent is collapsed", () => {
1045
+ render(
1046
+ <AdvancedTable
1047
+ cascadeCollapse
1048
+ columnDefinitions={columnDefinitions}
1049
+ data={{ testid: testId }}
1050
+ tableData={MOCK_DATA}
1051
+ />
1052
+ )
1053
+
1054
+ const kit = screen.getByTestId(testId)
1055
+ const getParentExpandButton = () => kit.querySelector("tbody tr .gray-icon.expand-toggle-icon")
1056
+ const parentButton = getParentExpandButton()
1057
+ expect(parentButton).toBeInTheDocument()
1058
+ parentButton.click()
1059
+ expect(kit.querySelector(".depth-sub-row-1")).toBeInTheDocument()
1060
+ getParentExpandButton().click()
1061
+ expect(kit.querySelector(".depth-sub-row-1")).not.toBeInTheDocument()
1062
+ getParentExpandButton().click()
1063
+ expect(kit.querySelector(".depth-sub-row-1")).toBeInTheDocument()
1064
+ })
1065
+
1066
+ test("cascadeCollapse=true with header toggle all: collapse all then expand all shows only direct children", async () => {
1067
+ render(
1068
+ <AdvancedTable
1069
+ cascadeCollapse
1070
+ columnDefinitions={columnDefinitions}
1071
+ data={{ testid: testId }}
1072
+ tableData={MOCK_DATA}
1073
+ />
1074
+ )
1075
+
1076
+ const kit = screen.getByTestId(testId)
1077
+ const toggleAllButton = kit.querySelector(".gray-icon.toggle-all-icon")
1078
+ expect(toggleAllButton).toBeInTheDocument()
1079
+ toggleAllButton.click()
1080
+ await waitFor(() => {
1081
+ expect(kit.querySelector(".depth-sub-row-1")).toBeInTheDocument()
1082
+ })
1083
+ toggleAllButton.click()
1084
+ await waitFor(() => {
1085
+ expect(kit.querySelector(".depth-sub-row-1")).not.toBeInTheDocument()
1086
+ })
1087
+ toggleAllButton.click()
1088
+ await waitFor(() => {
1089
+ expect(kit.querySelector(".depth-sub-row-1")).toBeInTheDocument()
1090
+ })
1091
+ })
@@ -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.
@@ -42,6 +42,7 @@ examples:
42
42
  - advanced_table_expanded_control: Expanded Control
43
43
  - advanced_table_expand_by_depth: Expand by Depth
44
44
  - advanced_table_subrow_headers: SubRow Headers
45
+ - advanced_table_cascade_collapse: Cascade Collapse
45
46
  - advanced_table_collapsible_trail: Collapsible Trail
46
47
  - advanced_table_table_options: Table Options
47
48
  - advanced_table_table_props: Table Props
@@ -48,4 +48,5 @@ export { default as AdvancedTablePaddingControl } from './_advanced_table_paddin
48
48
  export { default as AdvancedTablePaddingControlPerRow } from './_advanced_table_padding_control_per_row.jsx'
49
49
  export { default as AdvancedTableColumnStylingBackground } from './_advanced_table_column_styling_background.jsx'
50
50
  export { default as AdvancedTableColumnStylingBackgroundMulti } from './_advanced_table_column_styling_background_multi.jsx'
51
- export { default as AdvancedTableColumnStylingBackgroundCustom } from './_advanced_table_column_styling_background_custom.jsx'
51
+ export { default as AdvancedTableColumnStylingBackgroundCustom } from './_advanced_table_column_styling_background_custom.jsx'
52
+ export { default as AdvancedTableCascadeCollapse } from './_advanced_table_cascade_collapse.jsx'
@@ -1,5 +1,6 @@
1
1
  import PbEnhancedElement from "../pb_enhanced_element";
2
2
  import { updateSelectionActionBar } from "./advanced_table_action_bar";
3
+ import { setArrowVisibility, toggleVisibility } from "../utilities/domHelpers";
3
4
 
4
5
  const ADVANCED_TABLE_SELECTOR = "[data-advanced-table]";
5
6
  const DOWN_ARROW_SELECTOR = "#advanced-table_open_icon";
@@ -20,10 +21,18 @@ export default class PbAdvancedTable extends PbEnhancedElement {
20
21
  this.childRowsMap = new Map();
21
22
  }
22
23
 
24
+ get table() {
25
+ return this.cachedTable || (this.cachedTable = this.element.closest("table"));
26
+ }
27
+
28
+ get mainTable() {
29
+ return this.cachedMainTable || (this.cachedMainTable = this.element.closest(".pb_advanced_table"));
30
+ }
31
+
23
32
  // Fetch and cache child rows for a given parent row ID
24
33
  childRowsFor(parentId) {
25
34
  if (!this.childRowsMap.has(parentId)) {
26
- const table = this.element.closest("table");
35
+ const table = this.table;
27
36
  const rows = Array.from(
28
37
  table.querySelectorAll(`tr[data-row-parent="${parentId}"]`)
29
38
  );
@@ -33,7 +42,8 @@ export default class PbAdvancedTable extends PbEnhancedElement {
33
42
  }
34
43
 
35
44
  updateTableSelectedRowsAttribute() {
36
- const mainTable = this.element.closest(".pb_advanced_table");
45
+ const mainTable = this.mainTable;
46
+ if (!mainTable) return;
37
47
  mainTable.dataset.selectedRows = JSON.stringify(
38
48
  Array.from(PbAdvancedTable.selectedRows)
39
49
  );
@@ -41,7 +51,8 @@ export default class PbAdvancedTable extends PbEnhancedElement {
41
51
 
42
52
  // Recalculate selected count based on all checked checkboxes
43
53
  recalculateSelectedCount() {
44
- const table = this.element.closest("table");
54
+ const table = this.table;
55
+ if (!table) return;
45
56
 
46
57
  // Get all checkboxes that could be part of the selection
47
58
  // This includes row checkboxes and any parent checkboxes that might be programmatically checked
@@ -95,7 +106,7 @@ export default class PbAdvancedTable extends PbEnhancedElement {
95
106
  });
96
107
 
97
108
  this.updateTableSelectedRowsAttribute();
98
- updateSelectionActionBar(table.closest(".pb_advanced_table"), PbAdvancedTable.selectedRows.size);
109
+ updateSelectionActionBar(this.mainTable, PbAdvancedTable.selectedRows.size);
99
110
 
100
111
  // Sync header select-all state
101
112
  if (selectAllCheckbox) {
@@ -139,7 +150,7 @@ export default class PbAdvancedTable extends PbEnhancedElement {
139
150
 
140
151
  this.updateTableSelectedRowsAttribute();
141
152
 
142
- const table = checkbox.closest("table");
153
+ const table = this.table;
143
154
  const selectAllCheckbox = table.querySelector("#select-all-rows");
144
155
 
145
156
  if (selectAllCheckbox) {
@@ -153,7 +164,7 @@ export default class PbAdvancedTable extends PbEnhancedElement {
153
164
  );
154
165
  selectAllInput.checked = allChecked;
155
166
  }
156
- updateSelectionActionBar(table.closest(".pb_advanced_table"), PbAdvancedTable.selectedRows.size);
167
+ updateSelectionActionBar(this.mainTable, PbAdvancedTable.selectedRows.size);
157
168
  }
158
169
 
159
170
  get target() {
@@ -161,10 +172,11 @@ export default class PbAdvancedTable extends PbEnhancedElement {
161
172
  }
162
173
 
163
174
  connect() {
164
- const table = this.element.closest("table");
175
+ const table = this.table;
176
+ if (!table) return;
165
177
 
166
178
  this.hideCloseIcon();
167
- const mainTable = this.element.closest(".pb_advanced_table");
179
+ const mainTable = this.mainTable;
168
180
 
169
181
  // This so it is hidden on first render
170
182
  if (mainTable) {
@@ -271,9 +283,7 @@ export default class PbAdvancedTable extends PbEnhancedElement {
271
283
  }
272
284
 
273
285
  // Find direct child rows
274
- const childRows = Array.from(
275
- table.querySelectorAll(`[data-row-parent="${toggleBtn.id}"]`)
276
- );
286
+ const childRows = this.childRowsFor(toggleBtn.id);
277
287
  this.toggleElement(childRows);
278
288
 
279
289
  // Restore original element context
@@ -284,7 +294,8 @@ export default class PbAdvancedTable extends PbEnhancedElement {
284
294
  }
285
295
 
286
296
  addBorderRadiusOnLastVisibleRow() {
287
- const parentElement = this.element.closest(".pb_advanced_table");
297
+ const parentElement = this.mainTable;
298
+ if (!parentElement) return;
288
299
 
289
300
  const table = document.getElementById(parentElement.id);
290
301
 
@@ -316,11 +327,9 @@ export default class PbAdvancedTable extends PbEnhancedElement {
316
327
  elements.forEach((elem) => {
317
328
  elem.style.display = "table-row";
318
329
  elem.classList.add("is-visible");
319
- const childRowsAll = this.element
320
- .closest("table")
321
- .querySelectorAll(
322
- `[data-advanced-table-content^="${elem.dataset.advancedTableContent}-"]`
323
- );
330
+ const childRowsAll = this.table.querySelectorAll(
331
+ `[data-advanced-table-content^="${elem.dataset.advancedTableContent}-"]`
332
+ );
324
333
 
325
334
  childRowsAll.forEach((childRow) => {
326
335
  const dataContent = childRow.dataset.advancedTableContent;
@@ -382,8 +391,7 @@ export default class PbAdvancedTable extends PbEnhancedElement {
382
391
  const currentDepth = parseInt(elem.dataset.rowDepth);
383
392
  if (childrenArray.length > currentDepth) {
384
393
  // Find the child rows corresponding to this parent row
385
- const childRows = this.element
386
- .closest("table")
394
+ const childRows = this.table
387
395
  .querySelectorAll(
388
396
  `[data-advanced-table-content^="${elem.dataset.advancedTableContent}-"]`
389
397
  );
@@ -401,28 +409,39 @@ export default class PbAdvancedTable extends PbEnhancedElement {
401
409
 
402
410
  const isVisible = elements[0].classList.contains("is-visible");
403
411
 
404
- isVisible ? this.hideElement(elements) : this.showElement(elements);
405
- isVisible ? this.displayDownArrow() : this.displayUpArrow();
412
+ const isExpanded = toggleVisibility({
413
+ isVisible,
414
+ onHide: () => this.hideElement(elements),
415
+ onShow: () => this.showElement(elements),
416
+ });
417
+
418
+ isExpanded ? this.displayUpArrow() : this.displayDownArrow();
406
419
 
407
420
  const row = this.element.closest("tr");
408
421
  if (row) {
409
- row.classList.toggle("bg-silver", !isVisible);
410
- row.classList.toggle("pb-bg-row-white", isVisible);
422
+ row.classList.toggle("bg-silver", isExpanded);
423
+ row.classList.toggle("pb-bg-row-white", !isExpanded);
411
424
  }
412
425
 
413
426
  this.addBorderRadiusOnLastVisibleRow();
414
427
  }
415
428
 
416
429
  displayDownArrow() {
417
- this.element.querySelector(DOWN_ARROW_SELECTOR).style.display =
418
- "inline-block";
419
- this.element.querySelector(UP_ARROW_SELECTOR).style.display = "none";
430
+ setArrowVisibility({
431
+ rootElement: this.element,
432
+ downSelector: DOWN_ARROW_SELECTOR,
433
+ upSelector: UP_ARROW_SELECTOR,
434
+ showDownArrow: true,
435
+ });
420
436
  }
421
437
 
422
438
  displayUpArrow() {
423
- this.element.querySelector(UP_ARROW_SELECTOR).style.display =
424
- "inline-block";
425
- this.element.querySelector(DOWN_ARROW_SELECTOR).style.display = "none";
439
+ setArrowVisibility({
440
+ rootElement: this.element,
441
+ downSelector: DOWN_ARROW_SELECTOR,
442
+ upSelector: UP_ARROW_SELECTOR,
443
+ showDownArrow: false,
444
+ });
426
445
  }
427
446
 
428
447
  static handleToggleAllHeaders(element) {
@@ -30,7 +30,7 @@
30
30
  .pb_body_kit_error_neutral,
31
31
  .pb_body_kit_error_positive,
32
32
  .pb_body_kit_error_negative {
33
- @include pb_body($text_lt_default);
33
+ @include pb_body($text_default_color);
34
34
 
35
35
  b,
36
36
  strong {
@@ -38,9 +38,9 @@
38
38
  }
39
39
 
40
40
  a {
41
- color: $primary;
41
+ color: $text_link;
42
42
  &:hover {
43
- color: $text_lt_default;
43
+ color: $text_default_color;
44
44
  }
45
45
  }
46
46
 
@@ -110,7 +110,7 @@
110
110
  @include pb_body_dark();
111
111
 
112
112
  a {
113
- color: $active_dark;
113
+ color: $text_link_dark;
114
114
  }
115
115
  }
116
116
 
@@ -3,38 +3,38 @@
3
3
  @import "../tokens/typography";
4
4
 
5
5
  $pb_body_colors: (
6
- default: $text_lt_default,
7
- light: $text_lt_light,
8
- lighter: $text_lt_lighter,
9
- link: $primary,
10
- error: $error,
11
- success: $text_lt_success_sm,
6
+ default: $text_default_color,
7
+ light: $text_light,
8
+ lighter: $text_lighter,
9
+ link: $text_link,
10
+ error: $text_error,
11
+ success: $text_success,
12
12
  );
13
13
 
14
14
  $pb_dark_body_colors: (
15
- default: $text_dk_default,
16
- light: $text_dk_light,
17
- lighter: $text_dk_lighter,
18
- link: $active_dark,
19
- error: $error,
20
- success: $text_dk_success_sm,
15
+ default: $text_default_dark,
16
+ light: $text_light_dark,
17
+ lighter: $text_lighter_dark,
18
+ link: $text_link_dark,
19
+ error: $text_error_dark,
20
+ success: $text_success_dark,
21
21
  );
22
22
 
23
23
  // Order is important here!
24
24
  $pb_body_status: (
25
- default: $text_lt_default,
26
- negative: $error,
27
- dark_error: $error_dark_body,
28
- positive: $text_lt_success_sm,
25
+ default: $text_default_color,
26
+ negative: $text_error,
27
+ dark_error: $text_error_dark,
28
+ positive: $text_success,
29
29
  );
30
30
 
31
31
  $pb_dark_body_status: (
32
- default: $text_dk_default,
33
- negative: $error_dark,
34
- positive: $text_dk_success_sm,
32
+ default: $text_default_dark,
33
+ negative: $text_error_dark,
34
+ positive: $text_success_dark,
35
35
  );
36
36
 
37
- @mixin pb_body($color: $text_lt_default) {
37
+ @mixin pb_body($color: $text_default_color) {
38
38
  line-height: $lh_tight;
39
39
  color: $color;
40
40
  font-size: $text_default;
@@ -44,34 +44,34 @@ $pb_dark_body_status: (
44
44
 
45
45
  // Colors ======================
46
46
  @mixin pb_body_light {
47
- @include pb_body($text_lt_light);
47
+ @include pb_body($text_light);
48
48
  }
49
49
 
50
50
  @mixin pb_body_lighter {
51
- @include pb_body($text_lt_lighter);
51
+ @include pb_body($text_lighter);
52
52
  }
53
53
 
54
54
  @mixin pb_body_dark {
55
- @include pb_body($text_dk_default);
55
+ @include pb_body($text_default_dark);
56
56
  }
57
57
 
58
58
  @mixin pb_body_light_dark {
59
- @include pb_body($text_dk_light);
59
+ @include pb_body($text_light_dark);
60
60
  }
61
61
 
62
62
  @mixin pb_body_lighter_dark {
63
- @include pb_body($text_dk_lighter);
63
+ @include pb_body($text_lighter_dark);
64
64
  }
65
65
 
66
66
  // Statuses =====================
67
67
  @mixin pb_body_negative {
68
- @include pb_body($error);
68
+ @include pb_body($text_error);
69
69
  }
70
70
 
71
71
  @mixin pb_body_dark_error {
72
- @include pb_body($error_dark_body);
72
+ @include pb_body($text_error_dark);
73
73
  }
74
74
 
75
75
  @mixin pb_body_positive {
76
- @include pb_body($success);
76
+ @include pb_body($text_success);
77
77
  }
@@ -115,9 +115,10 @@ $pb_button_sizes: (
115
115
 
116
116
  // Icon-only button (icon prop set, no text) - square with equal padding
117
117
  // Rails: uses .pb_button_icon_only class
118
- // React: detects when pb_button_content has an empty text span
118
+ // React: when pb_button_content is empty (no text). Do not match when content has
119
+ // text + icon (e.g. "Exit Fullscreen" + FA icon) which can include empty spans.
119
120
  &.pb_button_icon_only,
120
- &:has(.pb_button_content > span:empty) {
121
+ &:has(.pb_button_content:empty) {
121
122
  aspect-ratio: 1;
122
123
  min-width: auto;
123
124
  width: auto;