playbook_ui 16.3.0.pre.alpha.PLAY2810toastaddmargin14803 → 16.3.0.pre.alpha.PLAY2814advancedtablesortparentonlyreact14813

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 (28) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableState.ts +5 -2
  3. data/app/pb_kits/playbook/pb_advanced_table/Utilities/RowModelUtils.ts +100 -0
  4. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +4 -1
  5. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +35 -1
  6. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_sort_parent_only.jsx +175 -0
  7. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_sort_parent_only.md +5 -0
  8. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +1 -0
  9. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +2 -1
  10. data/app/pb_kits/playbook/pb_advanced_table/index.js +48 -29
  11. data/app/pb_kits/playbook/pb_collapsible/index.js +15 -26
  12. data/app/pb_kits/playbook/pb_dropdown/index.js +67 -37
  13. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/_fixed_confirmation_toast.scss +0 -4
  14. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/_fixed_confirmation_toast.tsx +0 -3
  15. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/example.yml +0 -2
  16. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/index.js +1 -2
  17. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/fixed_confirmation_toast.rb +1 -7
  18. data/app/pb_kits/playbook/utilities/domHelpers.ts +50 -0
  19. data/dist/chunks/vendor.js +2 -2
  20. data/dist/menu.yml +1 -1
  21. data/dist/playbook-rails.js +1 -1
  22. data/dist/playbook.css +1 -1
  23. data/lib/playbook/version.rb +1 -1
  24. metadata +6 -6
  25. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_fixed_confirmation_toast_nav_margin.html.erb +0 -46
  26. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_fixed_confirmation_toast_nav_margin.jsx +0 -42
  27. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_fixed_confirmation_toast_nav_margin_rails.md +0 -1
  28. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/docs/_fixed_confirmation_toast_nav_margin_react.md +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6408ec28821af982194a7cc3dfc5455782498b851a798534bb31e6691a5c53a8
4
- data.tar.gz: cbf3ea0631fcbac27eba9a7d536bd4fac3a018f4e2e006a6d6bb96e177169733
3
+ metadata.gz: '08fb76d06568a9cfa7e70127d6025444f9f52640848d94b246e43f1d9a4e40bd'
4
+ data.tar.gz: 981afae84f0710df2dbb89c76fa81f0676f2d9bc795f034bc2cf07fe45b20226
5
5
  SHA512:
6
- metadata.gz: 89c5c9ae942801eba0b4f985a07e074e6016bb8c7b9ecb698552ca898c1f56748b00dbf2d6e05533b2098f74e95296e87b396b7387dcd1760fe2fc918dd79fe1
7
- data.tar.gz: fe873426e671dd5b3d6a825a58f4cf4ec9b77a208d91a916e822d82b28a8e6bb8462ba4d247c233a6c53d91e4c2715e8f349d3f4c5242bd2eb1bf9583b08678b
6
+ metadata.gz: e588d74894b397d7fe857d87534df43f41330dc7cb223a30aacd2289abb505cd135e099ad4802290590dc7397ba81b9e34bc2ac390aff54b52bf0f069a684680
7
+ data.tar.gz: 917c919b3f66125c7fb7eeaf41f38d8912cadc0039653e8634fb780f2b171a1dd4f86208c9897b7e904abaf78f084a2208df591251a5479888065ae7a8fc9320
@@ -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,
@@ -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
+ }
@@ -66,6 +66,7 @@ type AdvancedTableProps = {
66
66
  showActionsBar?: boolean,
67
67
  persistToggleExpansionButton?: boolean,
68
68
  sortControl?: GenericObject
69
+ sortParentOnly?: boolean
69
70
  tableData: GenericObject[]
70
71
  tableOptions?: GenericObject
71
72
  tableProps?: GenericObject
@@ -114,6 +115,7 @@ const AdvancedTable = (props: AdvancedTableProps) => {
114
115
  selectableRows,
115
116
  persistToggleExpansionButton = false,
116
117
  sortControl,
118
+ sortParentOnly = false,
117
119
  stickyLeftColumn,
118
120
  tableData,
119
121
  tableOptions,
@@ -159,7 +161,8 @@ const AdvancedTable = (props: AdvancedTableProps) => {
159
161
  columnVisibilityControl,
160
162
  pinnedRows,
161
163
  rowStyling,
162
- inlineRowLoading
164
+ inlineRowLoading,
165
+ sortParentOnly
163
166
  });
164
167
 
165
168
  // Initialize table actions
@@ -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(
@@ -0,0 +1,175 @@
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
+ import Caption from "../../pb_caption/_caption"
6
+
7
+ const sharedColumnDefinitions = [
8
+ {
9
+ accessor: "year",
10
+ label: "Year",
11
+ cellAccessors: ["quarter", "month", "day"],
12
+ },
13
+ {
14
+ accessor: "newEnrollments",
15
+ label: "New Enrollments",
16
+ },
17
+ {
18
+ accessor: "scheduledMeetings",
19
+ label: "Scheduled Meetings",
20
+ },
21
+ {
22
+ accessor: "attendanceRate",
23
+ label: "Attendance Rate",
24
+ },
25
+ {
26
+ accessor: "completedClasses",
27
+ label: "Completed Classes",
28
+ },
29
+ {
30
+ accessor: "classCompletionRate",
31
+ label: "Class Completion Rate",
32
+ },
33
+ {
34
+ accessor: "graduatedStudents",
35
+ label: "Graduated Students",
36
+ },
37
+ ]
38
+
39
+ const sortByColumnDefinitions = [
40
+ {
41
+ accessor: "year",
42
+ label: "Year",
43
+ cellAccessors: ["quarter", "month", "day"],
44
+ },
45
+ {
46
+ accessor: "newEnrollments",
47
+ label: "New Enrollments",
48
+ enableSort: true,
49
+ },
50
+ {
51
+ accessor: "scheduledMeetings",
52
+ label: "Scheduled Meetings",
53
+ },
54
+ {
55
+ accessor: "attendanceRate",
56
+ label: "Attendance Rate",
57
+ enableSort: true,
58
+ },
59
+ {
60
+ accessor: "completedClasses",
61
+ label: "Completed Classes",
62
+ },
63
+ {
64
+ accessor: "classCompletionRate",
65
+ label: "Class Completion Rate",
66
+ },
67
+ {
68
+ accessor: "graduatedStudents",
69
+ label: "Graduated Students",
70
+ },
71
+ ]
72
+
73
+ const sortByColumnMultiDefinitions = [
74
+ {
75
+ accessor: "year",
76
+ label: "Year",
77
+ cellAccessors: ["quarter", "month", "day"],
78
+ },
79
+ {
80
+ label: "Enrollment Data",
81
+ columns: [
82
+ {
83
+ label: "Enrollment Stats",
84
+ columns: [
85
+ {
86
+ accessor: "newEnrollments",
87
+ label: "New Enrollments",
88
+ enableSort: true,
89
+ },
90
+ {
91
+ accessor: "scheduledMeetings",
92
+ label: "Scheduled Meetings",
93
+ },
94
+ ],
95
+ },
96
+ ],
97
+ },
98
+ {
99
+ label: "Performance Data",
100
+ columns: [
101
+ {
102
+ label: "Completion Metrics",
103
+ columns: [
104
+ {
105
+ accessor: "completedClasses",
106
+ label: "Completed Classes",
107
+ enableSort: true,
108
+ },
109
+ {
110
+ accessor: "classCompletionRate",
111
+ label: "Class Completion Rate",
112
+ },
113
+ ],
114
+ },
115
+ {
116
+ label: "Attendance",
117
+ columns: [
118
+ {
119
+ accessor: "attendanceRate",
120
+ label: "Attendance Rate",
121
+ },
122
+ {
123
+ accessor: "scheduledMeetings",
124
+ label: "Scheduled Meetings",
125
+ },
126
+ ],
127
+ },
128
+ ],
129
+ },
130
+ ]
131
+
132
+ const AdvancedTableSortParentOnly = (props) => {
133
+ return (
134
+ <div>
135
+ <Caption text="Enable Sorting (first column) + sortParentOnly" />
136
+ <AdvancedTable
137
+ columnDefinitions={sharedColumnDefinitions}
138
+ sortParentOnly
139
+ tableData={MOCK_DATA}
140
+ {...props}
141
+ >
142
+ <AdvancedTable.Header enableSorting />
143
+ <AdvancedTable.Body />
144
+ </AdvancedTable>
145
+ <Caption marginTop="md"
146
+ text="Sort by column + sortParentOnly"
147
+ />
148
+ <AdvancedTable
149
+ columnDefinitions={sortByColumnDefinitions}
150
+ enableSortingRemoval
151
+ sortParentOnly
152
+ tableData={MOCK_DATA}
153
+ {...props}
154
+ >
155
+ <AdvancedTable.Header />
156
+ <AdvancedTable.Body />
157
+ </AdvancedTable>
158
+ <Caption marginTop="md"
159
+ text="Sort by column (multi-column) + sortParentOnly"
160
+ />
161
+ <AdvancedTable
162
+ columnDefinitions={sortByColumnMultiDefinitions}
163
+ enableSortingRemoval
164
+ sortParentOnly
165
+ tableData={MOCK_DATA}
166
+ {...props}
167
+ >
168
+ <AdvancedTable.Header enableSorting />
169
+ <AdvancedTable.Body />
170
+ </AdvancedTable>
171
+ </div>
172
+ )
173
+ }
174
+
175
+ export default AdvancedTableSortParentOnly
@@ -0,0 +1,5 @@
1
+ The `sortParentOnly` prop is a boolean set to `false` by default. When set to `true`, only parent (depth-0) rows are re-ordered when sorting; children and grandchildren stay grouped under their parent and keep their original order.
2
+
3
+ `sortParentOnly` works with every sorting mode: `enableSorting` on the header, per-column `enableSort: true`, and sortable leaf columns in the multi-header variant. Sort indicators behave as usual.
4
+
5
+ When omitted or `false`, sorting applies to all levels.
@@ -39,6 +39,7 @@ examples:
39
39
  - advanced_table_sort_per_column: Enable Sort By Column
40
40
  - advanced_table_sort_per_column_for_multi_column: Enable Sort By Column (Multi-Column)
41
41
  - advanced_table_custom_sort: Custom Sort
42
+ - advanced_table_sort_parent_only: Sort Parent Only
42
43
  - advanced_table_expanded_control: Expanded Control
43
44
  - advanced_table_expand_by_depth: Expand by Depth
44
45
  - advanced_table_subrow_headers: SubRow Headers
@@ -49,4 +49,5 @@ export { default as AdvancedTablePaddingControlPerRow } from './_advanced_table_
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
51
  export { default as AdvancedTableColumnStylingBackgroundCustom } from './_advanced_table_column_styling_background_custom.jsx'
52
- export { default as AdvancedTableCascadeCollapse } from './_advanced_table_cascade_collapse.jsx'
52
+ export { default as AdvancedTableCascadeCollapse } from './_advanced_table_cascade_collapse.jsx'
53
+ export { default as AdvancedTableSortParentOnly } from './_advanced_table_sort_parent_only.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) {
@@ -1,4 +1,5 @@
1
1
  import PbEnhancedElement from '../pb_enhanced_element'
2
+ import { getElementHeight, setArrowVisibility, toggleVisibility } from '../utilities/domHelpers'
2
3
 
3
4
  const MAIN_SELECTOR = '[data-collapsible-main]'
4
5
  const CONTENT_SELECTOR = '[data-collapsible-content]'
@@ -43,15 +44,7 @@ export default class PbCollapsible extends PbEnhancedElement {
43
44
  }
44
45
 
45
46
  showElement(elem) {
46
- // Get the natural height of the element
47
- const getHeight = () => {
48
- elem.style.display = 'block'
49
- const height = elem.scrollHeight + 'px' // Get it's height
50
- elem.style.display = '' // Hide it again
51
- return height
52
- }
53
-
54
- const height = getHeight()
47
+ const height = getElementHeight(elem)
55
48
  elem.classList.add('is-visible')
56
49
  elem.style.height = height // Update the max-height
57
50
  elem.style.overflow = "hidden"
@@ -82,26 +75,22 @@ export default class PbCollapsible extends PbEnhancedElement {
82
75
  }
83
76
 
84
77
  toggleElement(elem) {
85
- if (elem.classList.contains('is-visible')) {
86
- this.hideElement(elem)
87
- this.displayDownArrow()
88
- return
89
- }
90
- // Otherwise, show it
91
- this.showElement(elem)
92
- this.displayUpArrow()
78
+ const isExpanded = toggleVisibility({
79
+ isVisible: elem.classList.contains('is-visible'),
80
+ onHide: () => this.hideElement(elem),
81
+ onShow: () => this.showElement(elem),
82
+ })
83
+
84
+ isExpanded ? this.displayUpArrow() : this.displayDownArrow()
93
85
  }
94
86
 
95
87
  toggleArrows(showDownArrow) {
96
- const downArrow = this.element.querySelector(DOWN_ARROW_SELECTOR);
97
- const upArrow = this.element.querySelector(UP_ARROW_SELECTOR);
98
-
99
- if (downArrow) {
100
- downArrow.style.display = showDownArrow ? 'inline-block' : 'none';
101
- }
102
- if (upArrow) {
103
- upArrow.style.display = showDownArrow ? 'none' : 'inline-block';
104
- }
88
+ setArrowVisibility({
89
+ rootElement: this.element,
90
+ downSelector: DOWN_ARROW_SELECTOR,
91
+ upSelector: UP_ARROW_SELECTOR,
92
+ showDownArrow,
93
+ })
105
94
  }
106
95
 
107
96
  displayDownArrow() {