playbook_ui 16.2.0.pre.alpha.PLAY2828alphafortempo14533 → 16.2.0.pre.alpha.advancedtablecascadingcollapsereact14676

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 (88) 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_body/_body.scss +1 -1
  13. data/app/pb_kits/playbook/pb_body/_body_mixins.scss +1 -1
  14. data/app/pb_kits/playbook/pb_button/_button.scss +3 -2
  15. data/app/pb_kits/playbook/pb_caption/_caption.scss +1 -1
  16. data/app/pb_kits/playbook/pb_caption/_caption_mixin.scss +1 -1
  17. data/app/pb_kits/playbook/pb_checkbox/_checkbox.scss +6 -6
  18. data/app/pb_kits/playbook/pb_collapsible/index.js +16 -4
  19. data/app/pb_kits/playbook/pb_date_picker/_date_picker.scss +2 -2
  20. data/app/pb_kits/playbook/pb_date_picker/_date_picker.tsx +4 -1
  21. data/app/pb_kits/playbook/pb_date_picker/date_picker.html.erb +2 -2
  22. data/app/pb_kits/playbook/pb_date_picker/sass_partials/_input_styles.scss +0 -1
  23. data/app/pb_kits/playbook/pb_detail/_detail.scss +2 -2
  24. data/app/pb_kits/playbook/pb_detail/_detail_mixins.scss +1 -1
  25. data/app/pb_kits/playbook/pb_dialog/index.js +45 -5
  26. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +4 -4
  27. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +2 -0
  28. data/app/pb_kits/playbook/pb_dropdown/dropdown.html.erb +2 -2
  29. data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +1 -1
  30. data/app/pb_kits/playbook/pb_dropdown/index.js +68 -13
  31. data/app/pb_kits/playbook/pb_dropdown/keyboard_accessibility.js +19 -3
  32. data/app/pb_kits/playbook/pb_enhanced_element/element_observer.ts +1 -1
  33. data/app/pb_kits/playbook/pb_enhanced_element/index.ts +2 -1
  34. data/app/pb_kits/playbook/pb_icon/icon.rb +168 -19
  35. data/app/pb_kits/playbook/pb_kit_registry/index.ts +180 -0
  36. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.scss +2 -2
  37. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +2 -0
  38. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_react_reset_key.jsx +100 -0
  39. data/app/pb_kits/playbook/pb_multi_level_select/docs/_multi_level_select_react_reset_key.md +1 -0
  40. data/app/pb_kits/playbook/pb_multi_level_select/docs/example.yml +1 -0
  41. data/app/pb_kits/playbook/pb_multi_level_select/docs/index.js +1 -0
  42. data/app/pb_kits/playbook/pb_pagination/_pagination.scss +101 -1
  43. data/app/pb_kits/playbook/pb_pagination/_pagination.test.jsx +172 -1
  44. data/app/pb_kits/playbook/pb_pagination/_pagination.tsx +178 -15
  45. data/app/pb_kits/playbook/pb_pagination/docs/_pagination_default.jsx +1 -1
  46. data/app/pb_kits/playbook/pb_passphrase/_passphrase.scss +2 -2
  47. data/app/pb_kits/playbook/pb_passphrase/_passphrase.tsx +4 -0
  48. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.scss +5 -5
  49. data/app/pb_kits/playbook/pb_radio/_radio.scss +3 -3
  50. data/app/pb_kits/playbook/pb_rich_text_editor/_previewer_mixin.scss +2 -2
  51. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.scss +4 -4
  52. data/app/pb_kits/playbook/pb_rich_text_editor/_rich_text_editor.tsx +2 -0
  53. data/app/pb_kits/playbook/pb_rich_text_editor/_tiptap_styles.scss +1 -1
  54. data/app/pb_kits/playbook/pb_select/_select.scss +6 -6
  55. data/app/pb_kits/playbook/pb_select/_select.tsx +2 -0
  56. data/app/pb_kits/playbook/pb_select/select.html.erb +2 -2
  57. data/app/pb_kits/playbook/pb_star_rating/star_rating.html.erb +1 -1
  58. data/app/pb_kits/playbook/pb_star_rating/subcomponents/_star_rating_interactive.tsx +1 -0
  59. data/app/pb_kits/playbook/pb_text_input/_text_input.scss +0 -1
  60. data/app/pb_kits/playbook/pb_text_input/_text_input.tsx +2 -0
  61. data/app/pb_kits/playbook/pb_text_input/text_input.html.erb +2 -2
  62. data/app/pb_kits/playbook/pb_textarea/_textarea.scss +1 -2
  63. data/app/pb_kits/playbook/pb_textarea/_textarea.tsx +43 -21
  64. data/app/pb_kits/playbook/pb_textarea/docs/_textarea_input_options.jsx +68 -0
  65. data/app/pb_kits/playbook/pb_textarea/docs/example.yml +1 -0
  66. data/app/pb_kits/playbook/pb_textarea/docs/index.js +9 -8
  67. data/app/pb_kits/playbook/pb_textarea/textarea.html.erb +4 -4
  68. data/app/pb_kits/playbook/pb_textarea/textarea.rb +6 -2
  69. data/app/pb_kits/playbook/pb_textarea/textarea.test.js +134 -1
  70. data/app/pb_kits/playbook/pb_time_picker/_time_picker.scss +4 -4
  71. data/app/pb_kits/playbook/pb_time_picker/_time_picker.tsx +6 -0
  72. data/app/pb_kits/playbook/pb_time_picker/time_picker.test.jsx +2 -2
  73. data/app/pb_kits/playbook/pb_title/_title_mixin.scss +1 -1
  74. data/app/pb_kits/playbook/pb_tooltip/index.js +60 -15
  75. data/app/pb_kits/playbook/pb_typeahead/_typeahead.scss +2 -2
  76. data/app/pb_kits/playbook/pb_typeahead/_typeahead.test.jsx +1 -1
  77. data/dist/chunks/{_pb_line_graph-BSLb5VXP.js → _pb_line_graph-BGY7jEks.js} +1 -1
  78. data/dist/chunks/{_typeahead-DXIBDeMj.js → _typeahead-QhswHQnq.js} +1 -1
  79. data/dist/chunks/{globalProps-DyTB8IdV.js → globalProps-CK2YuA9O.js} +1 -1
  80. data/dist/chunks/{lib-9wz3x5jl.js → lib-DspaUdlc.js} +1 -1
  81. data/dist/chunks/vendor.js +5 -5
  82. data/dist/menu.yml +1 -1
  83. data/dist/playbook-rails-react-bindings.js +1 -1
  84. data/dist/playbook-rails.js +1 -1
  85. data/dist/playbook.css +1 -1
  86. data/lib/playbook/forms/builder/checkbox_field.rb +1 -1
  87. data/lib/playbook/version.rb +1 -1
  88. metadata +12 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 28cd9a824fc08c1c05900604d456aa161b4ea960f839abd9d7d656d40aa4bd69
4
- data.tar.gz: ef72ce4efcf711790c2db9b3bdb5e975341b4de9c7ce0ab0463fb22cf0d1981b
3
+ metadata.gz: 3310889318a881050c07f029fb5257de930342ee8eef612020cab8aae49ad888
4
+ data.tar.gz: b45cc744fe4dfccfab3660baef4909a91278e7551c7de2b6cae8d4abf98849b5
5
5
  SHA512:
6
- metadata.gz: 267fefb43c858e4925b6547c8b1a0418e377cbece041024ad67ee1c01781844614aa784cbc20882d695090614af4cd644e8040c602ccff99f196943d558fd960
7
- data.tar.gz: 5f57f8d288e6790d48837373d7da228726ec934c3ba701cac34dfdda1c1095b3a0430485504150f274b1b25b22b9fd570609843b45c7f5eaba6584cb2e13275f
6
+ metadata.gz: 29ba51ada748179cd73e7227ea9981f7fc252cc36e644b2e99447e86092f846ec0ca34c5f633070832c861a58f55b5339c8315757e1db2bacf3a61d5301843d4
7
+ data.tar.gz: d033429369a41a1c3a0785f96b16c45b49378118dfb37d10937a0766803b141ad41e67800d2ba58c1ffd2a2c3927ee7749bacc70062ca5747af694accd8565e6
@@ -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'
@@ -38,7 +38,7 @@
38
38
  }
39
39
 
40
40
  a {
41
- color: $primary;
41
+ color: $text_link;
42
42
  &:hover {
43
43
  color: $text_default_color;
44
44
  }
@@ -6,7 +6,7 @@ $pb_body_colors: (
6
6
  default: $text_default_color,
7
7
  light: $text_light,
8
8
  lighter: $text_lighter,
9
- link: $primary,
9
+ link: $text_link,
10
10
  error: $text_error,
11
11
  success: $text_success,
12
12
  );
@@ -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;
@@ -88,7 +88,7 @@
88
88
  .pb_caption_kit_base_link,
89
89
  .pb_caption_kit_lg_link,
90
90
  .pb_caption_kit_xl_link {
91
- color: $primary;
91
+ color: $text_link;
92
92
  }
93
93
 
94
94
  // Dark theme variants
@@ -4,7 +4,7 @@
4
4
  $pb_caption_colors: (
5
5
  default: $text_default_color,
6
6
  light: $text_light,
7
- link: $primary,
7
+ link: $text_link,
8
8
  lighter: $text_lighter,
9
9
  success: $text_success,
10
10
  error: $text_error,
@@ -62,16 +62,16 @@ $transition: $transition_cubic;
62
62
  left: $offscreen;
63
63
 
64
64
  &:focus ~ .pb_checkbox_checkmark {
65
- box-shadow: 0px 0px 0px 2px $white, 0px 0px 0px 4px $primary;
65
+ box-shadow: 0px 0px 0px 2px $white, 0px 0px 0px 4px $input_border_state;
66
66
  }
67
67
  &:checked ~ .pb_checkbox_checkmark,
68
68
  & ~ .pb_checkbox_indeterminate {
69
69
  background-color: $primary_action;
70
- border-color: $primary_action;
70
+ border-color: $input_border_state;
71
71
 
72
72
  &:hover {
73
73
  background-color: $primary_action;
74
- border-color: $primary_action;
74
+ border-color: $input_border_state;
75
75
  }
76
76
  }
77
77
  &:checked ~ .pb_checkbox_checkmark,
@@ -112,7 +112,7 @@ $transition: $transition_cubic;
112
112
  &.dark {
113
113
  input {
114
114
  &:focus ~ .pb_checkbox_checkmark {
115
- box-shadow: 0px 0px 0px 2px $bg_dark_card, 0px 0px 0px 4px $primary;
115
+ box-shadow: 0px 0px 0px 2px $bg_dark_card, 0px 0px 0px 4px $input_border_state_dark;
116
116
  }
117
117
 
118
118
  &:disabled ~ .pb_checkbox_checkmark {
@@ -154,9 +154,9 @@ $transition: $transition_cubic;
154
154
  input {
155
155
  &:checked ~ .pb_checkbox_checkmark,
156
156
  & ~ .pb_checkbox_indeterminate {
157
- border-color: $primary;
157
+ border-color: $input_border_state_dark;
158
158
  &:hover {
159
- border-color: $primary;
159
+ border-color: $input_border_state_dark;
160
160
  }
161
161
  }
162
162
  }
@@ -11,9 +11,11 @@ export default class PbCollapsible extends PbEnhancedElement {
11
11
  }
12
12
 
13
13
  connect() {
14
- this.element.addEventListener('click', () => {
14
+ this.clickHandler = () => {
15
15
  this.toggleElement(this.target)
16
- })
16
+ }
17
+ this.element.addEventListener('click', this.clickHandler)
18
+
17
19
  // Check the initial state of the collapsible content and set the arrow accordingly
18
20
  if (this.target.classList.contains('is-visible')) {
19
21
  this.displayUpArrow()
@@ -21,9 +23,19 @@ export default class PbCollapsible extends PbEnhancedElement {
21
23
  this.displayDownArrow()
22
24
  }
23
25
  // Listen for a custom event to toggle the collapsible
24
- document.addEventListener(`${this.target.id}`, () => {
26
+ this.customEventHandler = () => {
25
27
  this.toggleElement(this.target)
26
- })
28
+ }
29
+ document.addEventListener(`${this.target.id}`, this.customEventHandler)
30
+ }
31
+
32
+ disconnect() {
33
+ if (this.clickHandler) {
34
+ this.element.removeEventListener('click', this.clickHandler)
35
+ }
36
+ if (this.customEventHandler && this.target) {
37
+ document.removeEventListener(`${this.target.id}`, this.customEventHandler)
38
+ }
27
39
  }
28
40
 
29
41
  get target() {
@@ -28,10 +28,10 @@
28
28
  div.cal_icon_wrapper,
29
29
  input.date_picker_input {
30
30
  @include transition_default;
31
- border-color: $primary;
31
+ border-color: $status_border_primary;
32
32
  }
33
33
  .add-on-card {
34
- border-color: $primary;
34
+ border-color: $status_border_primary;
35
35
  }
36
36
  }
37
37
 
@@ -221,11 +221,14 @@ const DatePicker = (props: DatePickerProps): React.ReactElement => {
221
221
  {!hideLabel && (
222
222
  <label htmlFor={pickerId}>
223
223
  {requiredIndicator ? (
224
- <Caption className="pb_date_picker_kit_label">
224
+ <Caption className="pb_date_picker_kit_label"
225
+ color="lighter"
226
+ >
225
227
  {label} <span style={{ color: `${colors.error}` }}>*</span>
226
228
  </Caption>
227
229
  ) : (
228
230
  <Caption className="pb_date_picker_kit_label"
231
+ color="lighter"
229
232
  text={label}
230
233
  />
231
234
  )}
@@ -7,11 +7,11 @@
7
7
  <% if !object.hide_label && object.label %>
8
8
  <label for="<%= object.picker_id %>">
9
9
  <% if object.required_indicator %>
10
- <%= pb_rails("caption", props: { dark: object.dark, classname: "pb_date_picker_kit_label" }) do %>
10
+ <%= pb_rails("caption", props: { dark: object.dark, classname: "pb_date_picker_kit_label", color: "lighter" }) do %>
11
11
  <%= object.label %><span style="color: #DA0014;"> *</span>
12
12
  <% end %>
13
13
  <% else %>
14
- <%= pb_rails("caption", props: { text: object.label, dark: object.dark, classname: "pb_date_picker_kit_label" }) %>
14
+ <%= pb_rails("caption", props: { text: object.label, dark: object.dark, classname: "pb_date_picker_kit_label", color: "lighter" }) %>
15
15
  <% end %>
16
16
  </label>
17
17
  <% end %>
@@ -35,7 +35,6 @@
35
35
  .date_picker_input:-webkit-autofill:focus {
36
36
  @include pb_textarea_focus_light;
37
37
  @include transition_default;
38
- border-color: $primary;
39
38
  }
40
39
 
41
40
  input:disabled,
@@ -13,7 +13,7 @@
13
13
 
14
14
  // Base styles
15
15
  a {
16
- color: $primary;
16
+ color: $text_link;
17
17
  &:hover {
18
18
  cursor: pointer;
19
19
  color: $text_default_color;
@@ -44,7 +44,7 @@
44
44
  }
45
45
 
46
46
  .pb_detail_kit_color_link {
47
- @include pb_detail($primary);
47
+ @include pb_detail($text_link);
48
48
  }
49
49
 
50
50
  .pb_detail_kit_color_error {
@@ -6,7 +6,7 @@ $pb_detail_colors: (
6
6
  light: $text_light,
7
7
  default: $text_default_color,
8
8
  lighter: $text_lighter,
9
- link: $primary,
9
+ link: $text_link,
10
10
  error: $text_error,
11
11
  success: $text_success,
12
12
  );
@@ -8,8 +8,16 @@ export default class PbDialog extends PbEnhancedElement {
8
8
  }
9
9
 
10
10
  connect() {
11
- window.addEventListener("DOMContentLoaded", () => this.setupDialog())
12
- window.addEventListener("turbo:frame-load", () => this.setupDialog())
11
+ // Store references to this instance's specific elements
12
+ this.dialogElement = this.element.querySelector(".pb_dialog_rails")
13
+ this.dialogId = this.dialogElement?.id
14
+ this.managedTriggers = new Set()
15
+
16
+ this.domContentLoadedHandler = () => this.setupDialog()
17
+ this.turboFrameLoadHandler = () => this.setupDialog()
18
+
19
+ window.addEventListener("DOMContentLoaded", this.domContentLoadedHandler)
20
+ window.addEventListener("turbo:frame-load", this.turboFrameLoadHandler)
13
21
 
14
22
  // Code for custom_event_type setup (can take multiple events in a string separated by commas)
15
23
  const customEventTypeString = this.element.dataset.customEventType
@@ -24,11 +32,38 @@ export default class PbDialog extends PbEnhancedElement {
24
32
  }
25
33
 
26
34
  disconnect() {
35
+ // Clean up window event listeners
36
+ if (this.domContentLoadedHandler) {
37
+ window.removeEventListener("DOMContentLoaded", this.domContentLoadedHandler)
38
+ }
39
+ if (this.turboFrameLoadHandler) {
40
+ window.removeEventListener("turbo:frame-load", this.turboFrameLoadHandler)
41
+ }
42
+
43
+ // Clean up custom event listeners
27
44
  if (this.customEventTypes && Array.isArray(this.customEventTypes)) {
28
45
  this.customEventTypes.forEach(eventType => {
29
46
  window.removeEventListener(eventType, this.handleCustomEvent)
30
47
  })
31
48
  }
49
+
50
+ // Clean up only the triggers that this instance managed
51
+ this.managedTriggers.forEach((trigger) => {
52
+ if (trigger._openDialogClickHandler) {
53
+ trigger.removeEventListener("click", trigger._openDialogClickHandler)
54
+ delete trigger._openDialogClickHandler
55
+ }
56
+ if (trigger._closeDialogClickHandler) {
57
+ trigger.removeEventListener("click", trigger._closeDialogClickHandler)
58
+ delete trigger._closeDialogClickHandler
59
+ }
60
+ })
61
+
62
+ // Clean up this dialog's outside click handler
63
+ if (this.dialogElement && this.dialogElement._outsideClickHandler) {
64
+ this.dialogElement.removeEventListener("mousedown", this.dialogElement._outsideClickHandler)
65
+ delete this.dialogElement._outsideClickHandler
66
+ }
32
67
  }
33
68
 
34
69
  handleCustomEvent = (event) => {
@@ -88,9 +123,12 @@ export default class PbDialog extends PbEnhancedElement {
88
123
  }
89
124
 
90
125
  setupDialog() {
91
- const openTrigger = document.querySelectorAll("[data-open-dialog]");
92
- const closeTrigger = document.querySelectorAll("[data-close-dialog]");
93
- const dialogs = document.querySelectorAll(".pb_dialog_rails")
126
+ // Only set up triggers and dialogs that belong to this instance
127
+ if (!this.dialogId) return
128
+
129
+ const openTrigger = document.querySelectorAll(`[data-open-dialog="${this.dialogId}"]`);
130
+ const closeTrigger = document.querySelectorAll(`[data-close-dialog="${this.dialogId}"]`);
131
+ const dialogs = this.dialogElement ? [this.dialogElement] : []
94
132
 
95
133
  const loadingButton = document.querySelector('[data-disable-with="Loading"]');
96
134
  if (loadingButton && !loadingButton.dataset.listenerAttached) {
@@ -126,6 +164,7 @@ export default class PbDialog extends PbEnhancedElement {
126
164
  };
127
165
 
128
166
  open.addEventListener("click", open._openDialogClickHandler)
167
+ this.managedTriggers.add(open)
129
168
  });
130
169
 
131
170
  closeTrigger.forEach((close) => {
@@ -139,6 +178,7 @@ export default class PbDialog extends PbEnhancedElement {
139
178
  };
140
179
 
141
180
  close.addEventListener("click", close._closeDialogClickHandler)
181
+ this.managedTriggers.add(close)
142
182
  });
143
183
 
144
184
  // Close dialog box on outside click