playbook_ui 18.3.0 → 18.4.0.pre.rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/Components/CollapsibleTrail.tsx +8 -1
  3. data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +39 -8
  4. data/app/pb_kits/playbook/pb_advanced_table/Components/VirtualizedTableView.tsx +24 -4
  5. data/app/pb_kits/playbook/pb_advanced_table/Utilities/RowUtils.ts +29 -0
  6. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.scss +72 -5
  7. data/app/pb_kits/playbook/pb_advanced_table/_advanced_table.tsx +3 -0
  8. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.html.erb +1 -1
  9. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.rb +8 -0
  10. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +467 -0
  11. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_group_separators.jsx +67 -0
  12. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_group_separators.md +7 -0
  13. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_group_separators_rails.html.erb +78 -0
  14. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_group_separators_rails.md +3 -0
  15. data/app/pb_kits/playbook/pb_advanced_table/docs/_playground.json +10 -1
  16. data/app/pb_kits/playbook/pb_advanced_table/docs/_playground.overrides.json +8 -1
  17. data/app/pb_kits/playbook/pb_advanced_table/docs/advanced_table_mock_data_group_separators.json +328 -0
  18. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +2 -0
  19. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +1 -0
  20. data/app/pb_kits/playbook/pb_advanced_table/index.js +39 -2
  21. data/app/pb_kits/playbook/pb_advanced_table/kit.schema.json +8 -0
  22. data/app/pb_kits/playbook/pb_advanced_table/table_body.html.erb +2 -2
  23. data/app/pb_kits/playbook/pb_advanced_table/table_body.rb +53 -23
  24. data/app/pb_kits/playbook/pb_advanced_table/table_row.html.erb +1 -1
  25. data/app/pb_kits/playbook/pb_advanced_table/table_row.rb +2 -0
  26. data/app/pb_kits/playbook/pb_collapsible/child_kits/CollapsibleMain.tsx +4 -4
  27. data/app/pb_kits/playbook/pb_collapsible/collapsible.test.js +81 -2
  28. data/app/pb_kits/playbook/pb_date/date.test.js +1 -1
  29. data/app/pb_kits/playbook/pb_date_picker/date_picker.test.js +4 -0
  30. data/app/pb_kits/playbook/pb_date_stacked/date_stacked.test.js +1 -0
  31. data/app/pb_kits/playbook/pb_date_time/dateTime.test.js +1 -0
  32. data/app/pb_kits/playbook/pb_date_year_stacked/date_year_stacked.test.js +1 -0
  33. data/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +2 -1
  34. data/app/pb_kits/playbook/pb_fixed_confirmation_toast/fixed_confirmation_toast.test.js +1 -2
  35. data/app/pb_kits/playbook/pb_icon/_icon.tsx +6 -1
  36. data/app/pb_kits/playbook/pb_icon/kit.schema.json +1 -0
  37. data/app/pb_kits/playbook/pb_icon_circle/_icon_circle.tsx +3 -1
  38. data/app/pb_kits/playbook/pb_icon_circle/kit.schema.json +1 -0
  39. data/app/pb_kits/playbook/pb_nav/_nav_item.test.js +17 -0
  40. data/app/pb_kits/playbook/pb_pb_bar_graph/pbbargraph.test.jsx +1 -2
  41. data/app/pb_kits/playbook/pb_pb_circle_chart/pb_circle_chart.test.jsx +1 -2
  42. data/app/pb_kits/playbook/pb_pb_gauge_chart/pb_gauge_chart.test.jsx +1 -2
  43. data/app/pb_kits/playbook/pb_pb_line_graph/pb_line_graph.test.jsx +1 -2
  44. data/app/pb_kits/playbook/pb_phone_number_input/_phone_number_input.scss +3 -7
  45. data/app/pb_kits/playbook/pb_phone_number_input/img/flags.webp +0 -0
  46. data/app/pb_kits/playbook/pb_phone_number_input/img/flags@2x.webp +0 -0
  47. data/app/pb_kits/playbook/pb_phone_number_input/img/globe.webp +0 -0
  48. data/app/pb_kits/playbook/pb_phone_number_input/img/globe@2x.webp +0 -0
  49. data/app/pb_kits/playbook/pb_phone_number_input/intlTelInput.scss +13 -6
  50. data/app/pb_kits/playbook/pb_phone_number_input/phone_number_input_assets.test.js +61 -0
  51. data/app/pb_kits/playbook/pb_popover/index.ts +47 -8
  52. data/app/pb_kits/playbook/pb_popover/popover_index.test.js +456 -0
  53. data/app/pb_kits/playbook/pb_timestamp/timestamp.test.js +1 -0
  54. data/app/pb_kits/playbook/pb_weekday_stacked/weekday_stacked.test.jsx +1 -0
  55. data/app/pb_kits/playbook/utilities/test/globalProps/onClick.test.js +169 -0
  56. data/app/pb_kits/playbook/utilities/test-utils.js +0 -1
  57. data/dist/ai/all-schemas.json +10 -0
  58. data/dist/ai/forms.json +1 -1
  59. data/dist/ai/index.json +2 -2
  60. data/dist/ai/kits/advanced_table.schema.json +8 -0
  61. data/dist/ai/kits/icon.schema.json +1 -0
  62. data/dist/ai/kits/icon_circle.schema.json +1 -0
  63. data/dist/ai/playgrounds/advanced_table.json +1 -1
  64. data/dist/ai/playgrounds/index.json +1 -1
  65. data/dist/chunks/_pagination-DZ0eQuH2.js +1 -0
  66. data/dist/chunks/{_pb_line_graph-BKwbOeqo.js → _pb_line_graph-DyvH5Zj1.js} +1 -1
  67. data/dist/chunks/{_tooltip-UkcgcThO.js → _tooltip-CFbtqu-E.js} +1 -1
  68. data/dist/chunks/{_typeahead-NYVJDWmJ.js → _typeahead-C3MN3apM.js} +1 -1
  69. data/dist/chunks/{globalProps-BivjwRew.js → globalProps-xfGTpdLP.js} +1 -1
  70. data/dist/chunks/{lib-C50eCpoN.js → lib-Cu_i0y0k.js} +1 -1
  71. data/dist/chunks/{time_picker_helper-DfKvHCJu.js → time_picker_helper-B82MORju.js} +1 -1
  72. data/dist/chunks/vendor.js +1 -1
  73. data/dist/menu.yml +1 -1
  74. data/dist/playbook-rails-react-bindings.js +1 -1
  75. data/dist/playbook-rails.js +1 -1
  76. data/dist/playbook.css +1 -1
  77. data/lib/playbook/version.rb +2 -2
  78. metadata +19 -8
  79. data/dist/chunks/_pagination-CARbC_IN.js +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ed0bccebc1ce9ad6fd37beef926cdbc7a6144dc721e7b465018ddfcfa0567308
4
- data.tar.gz: edd9e231226e1c85b0a19bfd39a63fa5b8689c0a3c798c1dc8937c9008e3024b
3
+ metadata.gz: 7857c81c8777f126825841968493b921ed0152d13d2adf30b09c631a21b5227b
4
+ data.tar.gz: 607cf7d2df99e50c7cfaac3e19c3f184abe226586c27fa7775e6a019904d12a6
5
5
  SHA512:
6
- metadata.gz: d0ebc657f6d8ffa1853c109c366750e77193c31db71635296c3d9000613d00fdf213cc508149805300d86a17a27700a6539788315c4e99fe44842cf6d57152a7
7
- data.tar.gz: 1b6a91e7784fc21f0586107ffcd6dd1262c469e893df73e88d2e441f84270a70c177e8d57f11ea960659c91b66d477ebb7f8f4e8bc13a1d8f0765e2de3db0b4b
6
+ metadata.gz: 063a9838e72ac864c7b13359ce1f04a1c9b046ce1a44221aece7fafa2578e9a6a8c34757337020521f7a243865f014c841e19659616c0623ebcbed92fcb116c5
7
+ data.tar.gz: fe9d2025dd4b44a4e09695cf6b96075dcfcfb566236076446499f89bf7e8b2d6c12d1bfca2acce53510fccb4b1c8c6d73c39c7a33b3d3ab1296ec6ffa4575a77
@@ -1,10 +1,17 @@
1
- import React from "react"
1
+ import React, { useContext } from "react"
2
+
3
+ import AdvancedTableContext from "../Context/AdvancedTableContext"
2
4
 
3
5
  //CollapsibleTrail component
4
6
  const CollapsibleTrail = ({ leftOffset }: { leftOffset: number }) => {
7
+ const { groupSeparators } = useContext(AdvancedTableContext)
5
8
  const style: { [key: string]: string | number } = {
6
9
  left: `${leftOffset}em`,
7
10
  }
11
+ // Read by groupSeparators CSS to decide which trails stop at the stroke
12
+ if (groupSeparators) {
13
+ style["--collapsible-trail-left"] = `${leftOffset}em`
14
+ }
8
15
 
9
16
  return (
10
17
  <div
@@ -1,4 +1,4 @@
1
- import React, { useContext, useEffect } from "react"
1
+ import React, { useContext, useEffect, useMemo } from "react"
2
2
  import classnames from "classnames"
3
3
  import { flexRender, Row, Cell } from "@tanstack/react-table"
4
4
 
@@ -6,7 +6,7 @@ import { GenericObject } from "../../types"
6
6
  import { isChrome } from "../Utilities/BrowserCheck"
7
7
  import { findColumnDefByAccessor } from "../Utilities/ColumnStylingHelper"
8
8
  import { playbookColumnLayoutStylesFromMeta } from "../Utilities/ColumnLayoutHelper"
9
- import { getFontWeight, getRowColorClass, getRowStyle, shouldShowLoadingIndicator } from "../Utilities/RowUtils"
9
+ import { buildGroupSeparatorInsets, getFontWeight, getRowColorClass, getRowStyle, shouldShowLoadingIndicator } from "../Utilities/RowUtils"
10
10
 
11
11
  import LoadingInline from "../../pb_loading_inline/_loading_inline"
12
12
  import Checkbox from "../../pb_checkbox/_checkbox"
@@ -125,6 +125,7 @@ export const RegularTableView = ({
125
125
  }: RegularTableViewProps) => {
126
126
  const {
127
127
  enableToggleExpansion,
128
+ groupSeparators,
128
129
  handleExpandOrCollapse,
129
130
  inlineRowLoading,
130
131
  loading,
@@ -158,14 +159,30 @@ export const RegularTableView = ({
158
159
  (obj: Record<string, unknown>) => "columns" in obj
159
160
  );
160
161
 
161
- // Row pinning
162
- function PinnedRow({ row, position }: { row: Row<any>; position: 'top' | 'bottom' }) {
162
+ // One map from the full row model so pinning a non-last sibling cannot
163
+ // invent a second separator just because that section only sees that row.
164
+ const rowModelRows = table.getRowModel().rows
165
+ const groupSeparatorInsets = useMemo(
166
+ () => (groupSeparators ? buildGroupSeparatorInsets(rowModelRows) : new Map<string, number>()),
167
+ [groupSeparators, rowModelRows]
168
+ )
169
+
170
+ function PinnedRow({
171
+ position,
172
+ row,
173
+ }: {
174
+ position: 'top' | 'bottom'
175
+ row: Row<any>
176
+ }) {
163
177
  const customRowStyle = getRowStyle(rowStyling, row);
164
178
  const bottomRowsCount = table.getBottomRows().length;
179
+ const groupSeparatorInsetEm = groupSeparatorInsets.get(row.id)
180
+ const showGroupSeparator = groupSeparatorInsetEm !== undefined
165
181
 
166
182
  return (
167
183
  <tr
168
184
  className={classnames('pinned-row', {
185
+ 'group-separator-row': showGroupSeparator,
169
186
  'pinned-row-bottom': position === 'bottom',
170
187
  })}
171
188
  style={{
@@ -182,6 +199,9 @@ export const RegularTableView = ({
182
199
  : undefined,
183
200
  zIndex: '3',
184
201
  fontWeight: getFontWeight(customRowStyle),
202
+ ...(showGroupSeparator && {
203
+ ["--group-separator-inset" as string]: `${groupSeparatorInsetEm}em`,
204
+ }),
185
205
  }}
186
206
  >
187
207
  <TableCellRenderer
@@ -198,11 +218,13 @@ export const RegularTableView = ({
198
218
  )
199
219
  }
200
220
 
201
- const totalRows = pinnedRows ? table.getCenterRows() : table.getRowModel().rows
221
+ const totalRows = pinnedRows ? table.getCenterRows() : rowModelRows
222
+ const topRows = pinnedRows ? table.getTopRows() : []
223
+ const bottomRows = pinnedRows ? table.getBottomRows() : []
202
224
 
203
225
  return (
204
226
  <>
205
- {pinnedRows && table.getTopRows().map((row: Row<GenericObject>) => (
227
+ {topRows.map((row: Row<GenericObject>) => (
206
228
  <PinnedRow key={row.id}
207
229
  position="top"
208
230
  row={row}
@@ -219,6 +241,8 @@ export const RegularTableView = ({
219
241
  // Use functions from RowUtils for consistent cell coloring
220
242
  const rowColor = getRowColorClass(row, inlineRowLoading || false);
221
243
  const isDataLoading = shouldShowLoadingIndicator(row, inlineRowLoading || false, columnDefinitions[0]?.cellAccessors?.length || 0);
244
+ const groupSeparatorInsetEm = groupSeparatorInsets.get(row.id)
245
+ const showGroupSeparator = groupSeparatorInsetEm !== undefined
222
246
 
223
247
  return (
224
248
  <React.Fragment key={`${row.index}-${row.id}-${row.depth}-row`}>
@@ -234,13 +258,20 @@ export const RegularTableView = ({
234
258
  )}
235
259
 
236
260
  <tr
237
- className={`${rowColor} ${row.depth > 0 ? `depth-sub-row-${row.depth}` : ""}`}
261
+ className={classnames(
262
+ rowColor,
263
+ row.depth > 0 && `depth-sub-row-${row.depth}`,
264
+ showGroupSeparator && "group-separator-row"
265
+ )}
238
266
  id={`${row.index}-${row.id}-${row.depth}-row`}
239
267
  ref={isFirstRegularRow ? sampleRowRef : null}
240
268
  style={{
241
269
  backgroundColor: customRowStyle?.backgroundColor,
242
270
  color: customRowStyle?.fontColor,
243
271
  fontWeight: getFontWeight(customRowStyle),
272
+ ...(showGroupSeparator && {
273
+ ["--group-separator-inset" as string]: `${groupSeparatorInsetEm}em`,
274
+ }),
244
275
  }}
245
276
  >
246
277
  {/* Render custom checkbox column when we want selectableRows for non-expanding tables */}
@@ -280,7 +311,7 @@ export const RegularTableView = ({
280
311
  </React.Fragment>
281
312
  );
282
313
  })}
283
- {pinnedRows && table.getBottomRows().map((row: Row<GenericObject>) => (
314
+ {bottomRows.map((row: Row<GenericObject>) => (
284
315
  <PinnedRow key={row.id}
285
316
  position="bottom"
286
317
  row={row}
@@ -1,4 +1,4 @@
1
- import React, { useContext, useLayoutEffect, useState, useEffect, useRef } from "react"
1
+ import React, { useContext, useEffect, useLayoutEffect, useMemo, useState } from "react"
2
2
  import classnames from "classnames"
3
3
  import { flexRender, Cell, Row } from "@tanstack/react-table"
4
4
  import { VirtualItem } from "@tanstack/react-virtual"
@@ -7,7 +7,7 @@ import { GenericObject } from "../../types"
7
7
 
8
8
  import { isChrome } from "../Utilities/BrowserCheck"
9
9
  import { getVirtualizedRowStyle } from "../Utilities/TableContainerStyles"
10
- import { getRowColorClass } from "../Utilities/RowUtils"
10
+ import { buildGroupSeparatorInsets, getRowColorClass } from "../Utilities/RowUtils"
11
11
 
12
12
  import LoadingInline from "../../pb_loading_inline/_loading_inline"
13
13
  import Checkbox from "../../pb_checkbox/_checkbox"
@@ -34,6 +34,7 @@ export const VirtualizedTableView = ({
34
34
  }: VirtualizedTableViewProps) => {
35
35
  const {
36
36
  enableToggleExpansion,
37
+ groupSeparators,
37
38
  handleExpandOrCollapse,
38
39
  inlineRowLoading,
39
40
  loading,
@@ -119,6 +120,13 @@ export const VirtualizedTableView = ({
119
120
  };
120
121
  }, [table, selectableRows, hasAnySubRows]);
121
122
 
123
+ // Memoize against scroll-driven re-renders; only rebuild when the row model changes.
124
+ const rowModelRows = table.getRowModel().rows
125
+ const groupSeparatorInsets = useMemo(
126
+ () => (groupSeparators ? buildGroupSeparatorInsets(rowModelRows) : new Map<string, number>()),
127
+ [groupSeparators, rowModelRows]
128
+ )
129
+
122
130
  // Safety check
123
131
  if (!virtualizer || !flattenedItems) {
124
132
  return (
@@ -180,10 +188,17 @@ export const VirtualizedTableView = ({
180
188
 
181
189
  // Use the utility function to get consistent row color
182
190
  const rowColor = getRowColorClass(row, inlineRowLoading || false);
191
+ const groupSeparatorInsetEm = groupSeparatorInsets.get(row.id)
192
+ const showGroupSeparator = groupSeparatorInsetEm !== undefined
183
193
 
184
194
  return (
185
195
  <tr
186
- className={`virtualized-table-row ${rowColor} ${row.depth > 0 ? `depth-sub-row-${row.depth}` : ""}`}
196
+ className={classnames(
197
+ "virtualized-table-row",
198
+ rowColor,
199
+ row.depth > 0 && `depth-sub-row-${row.depth}`,
200
+ showGroupSeparator && "group-separator-row"
201
+ )}
187
202
  data-index={virtualRow.index}
188
203
  key={`row-${item.id}-sort-${sortingState}`}
189
204
  ref={(node) => {
@@ -195,7 +210,12 @@ export const VirtualizedTableView = ({
195
210
  }
196
211
  }
197
212
  }}
198
- style={virtualItemStyle}
213
+ style={{
214
+ ...virtualItemStyle,
215
+ ...(showGroupSeparator && {
216
+ ["--group-separator-inset" as string]: `${groupSeparatorInsetEm}em`,
217
+ }),
218
+ }}
199
219
  >
200
220
  {/* Render custom checkbox column when we want selectableRows for non-expanding tables */}
201
221
  {selectableRows && !hasAnySubRows && (
@@ -53,6 +53,35 @@ export const getFontWeight = (rowStyle?: GenericObject) => {
53
53
  if (rowStyle?.fontWeight === "regular") return 400
54
54
  }
55
55
 
56
+ // Matches the `left` of the nth collapsible-trail (see CollapsibleTrail.tsx).
57
+ const getCollapsibleTrailLeftEm = (trailIndex: number): number =>
58
+ trailIndex * 1.0 + (trailIndex > 1 ? (trailIndex - 1) * 0.25 : 0)
59
+
60
+ /**
61
+ * One O(n) pass over the full visible row model → Map of row id to its
62
+ * `--group-separator-inset` (em). Call once (and memoize) for the whole table —
63
+ * do not rebuild per pinned section, or a pinned non-last sibling can look like
64
+ * a last sibling and invent a second separator.
65
+ *
66
+ * A row closes every group between its depth and the next visible row's depth
67
+ * (so expanded parents defer to their last visible descendant, and sort/filter
68
+ * is respected). One stroke is drawn, inset to the outermost closed group:
69
+ * year-level → 0 / full-bleed, otherwise that group's collapsible-trail.
70
+ */
71
+ export const buildGroupSeparatorInsets = (
72
+ visibleRows: Row<GenericObject>[]
73
+ ): Map<string, number> => {
74
+ const insets = new Map<string, number>()
75
+
76
+ visibleRows.forEach((row, index) => {
77
+ if (row.depth === 0) return
78
+ const nextDepth = visibleRows[index + 1]?.depth ?? 0
79
+ if (nextDepth >= row.depth) return
80
+ insets.set(row.id, nextDepth === 0 ? 0 : getCollapsibleTrailLeftEm(nextDepth + 1))
81
+ })
82
+ return insets
83
+ }
84
+
56
85
  /**
57
86
  * Creates a virtual item style object for virtualized rows
58
87
  */
@@ -15,6 +15,8 @@
15
15
 
16
16
  // Base vertical border color variable setup
17
17
  --column-border-color: #{$border_light};
18
+ --group-separator-color: #{$input_border_default};
19
+ --group-separator-row-border-color: #{$border_light};
18
20
 
19
21
  // Define color tokens once
20
22
  $border_color_options: (
@@ -524,6 +526,48 @@
524
526
  border-bottom-color: $border_light !important;
525
527
  }
526
528
 
529
+ // 2px group separator on last sibling of each nested group. It replaces the
530
+ // default border-bottom: the border goes transparent and the stroke paints
531
+ // over its spot, with a 1px default line underneath for any part left of
532
+ // --group-separator-inset (0 = year-level full-bleed). Rails:
533
+ // .group-separator-expanded defers to the leaf.
534
+ tr.group-separator-row:not(.group-separator-expanded) {
535
+ > td,
536
+ > .pb_table_td {
537
+ border-bottom-color: transparent !important;
538
+ background-image:
539
+ linear-gradient(var(--group-separator-color), var(--group-separator-color)),
540
+ linear-gradient(var(--group-separator-row-border-color), var(--group-separator-row-border-color));
541
+ // Padding-box origin so the inset matches the trails' `left` (a first-cell
542
+ // border-left would otherwise shift the stroke 1px past its trail); the
543
+ // +1px pushes both layers down over the 1px bottom border.
544
+ background-origin: padding-box;
545
+ background-position: 0 calc(100% + 1px), 0 calc(100% + 1px);
546
+ background-repeat: no-repeat;
547
+ background-size: 100% 2px, 100% 1px;
548
+ // Table hover sets `transition: all` on cells, which slides the stroke in
549
+ // from the top when a row becomes a separator. Keep only the hover border fade.
550
+ transition-property: border-color !important;
551
+ }
552
+
553
+ > td:first-child:not(.checkbox-cell),
554
+ > .pb_table_td:first-child:not(.checkbox-cell),
555
+ > td.checkbox-cell + td,
556
+ > td.checkbox-cell + .pb_table_td {
557
+ background-position: var(--group-separator-inset, 0em) calc(100% + 1px), 0 calc(100% + 1px);
558
+ background-size: calc(100% - var(--group-separator-inset, 0em)) 2px, 100% 1px;
559
+
560
+ // Trails left of the inset belong to groups that stay open, so they run
561
+ // through; trails at or past it stop on top of the stroke, which rises
562
+ // 1px above the (now transparent) border (0 or 1px).
563
+ // Avoid `* 1000` inside this clamp: dart-sass strips the calc() and leaves
564
+ // `(var() - var()) * 1000`, which SassC rejects when nitro recompiles playbook.css.
565
+ .collapsible-trail {
566
+ bottom: clamp(0px, calc(var(--collapsible-trail-left, 0em) - var(--group-separator-inset, 0em) + 1px), 1px);
567
+ }
568
+ }
569
+ }
570
+
527
571
  // Apply border colors when columnGroupBorderColor is set
528
572
  &[class*="column-group-border-"] {
529
573
  // For top-level column groups
@@ -789,6 +833,8 @@
789
833
  &.dark {
790
834
  // Override default border color for dark mode
791
835
  --column-border-color: #{$border_dark};
836
+ --group-separator-color: #{$input_border_default_dark};
837
+ --group-separator-row-border-color: #{$border_dark};
792
838
 
793
839
  // Dark mode default border colors for trails and horizontal borders
794
840
  .collapsible-trail {
@@ -800,6 +846,26 @@
800
846
  border-bottom-color: $border_dark !important;
801
847
  }
802
848
 
849
+ // table-dark sets `border-color: … !important` (rest + hover), and
850
+ // column-group-border sets `.toggle-content td { border-bottom: … !important }`.
851
+ // Those beat the shared separator rule, so the opaque border paints over the
852
+ // 2px stroke. Reassert transparent bottom with higher specificity.
853
+ .pb_table.dark,
854
+ .pb_table.table-dark {
855
+ tbody > tr.group-separator-row:not(.group-separator-expanded),
856
+ .pb_table_tbody > .pb_table_tr.group-separator-row:not(.group-separator-expanded) {
857
+ > td,
858
+ > .pb_table_td {
859
+ border-bottom-color: transparent !important;
860
+ }
861
+
862
+ &:hover > td,
863
+ &:hover > .pb_table_td {
864
+ border-bottom-color: transparent !important;
865
+ }
866
+ }
867
+ }
868
+
803
869
  .pb-bg-row-white {
804
870
  background: $bg_dark_card;
805
871
  }
@@ -808,21 +874,22 @@
808
874
  background: $bg_dark;
809
875
  }
810
876
 
811
- // Dark mode virtualized rows
877
+ // Dark mode virtualized rows — use background-color (not the background
878
+ // shorthand) so group-separator background-image is not cleared.
812
879
  .virtualized-table-row {
813
880
  &.pb-bg-row-white {
814
- background: $bg_dark_card !important;
881
+ background-color: $bg_dark_card !important;
815
882
 
816
883
  td:first-child {
817
- background: $bg_dark_card !important;
884
+ background-color: $bg_dark_card !important;
818
885
  }
819
886
  }
820
887
 
821
888
  &.bg-silver {
822
- background: $bg_dark;
889
+ background-color: $bg_dark;
823
890
 
824
891
  td:first-child {
825
- background: $bg_dark;
892
+ background-color: $bg_dark;
826
893
  }
827
894
  }
828
895
  }
@@ -37,6 +37,7 @@ type AdvancedTableProps = {
37
37
  expandByDepth?: GenericObject[]
38
38
  onExpandByDepthClick?: (arg: number, arg1: any) => void
39
39
  fullWidthCell?: boolean
40
+ groupSeparators?: boolean
40
41
  htmlOptions?: {[key: string]: string | number | boolean | (() => void)},
41
42
  id?: string
42
43
  initialLoadingRowsCount?: number
@@ -100,6 +101,7 @@ const AdvancedTable = (props: AdvancedTableProps) => {
100
101
  expandByDepth,
101
102
  onExpandByDepthClick,
102
103
  fullWidthCell = false,
104
+ groupSeparators = false,
103
105
  htmlOptions = {},
104
106
  id,
105
107
  initialLoadingRowsCount = 10,
@@ -326,6 +328,7 @@ const AdvancedTable = (props: AdvancedTableProps) => {
326
328
  expandByDepth={expandByDepth}
327
329
  expanded={expanded}
328
330
  expandedControl={expandedControl}
331
+ groupSeparators={groupSeparators}
329
332
  handleExpandOrCollapse={handleExpandOrCollapse}
330
333
  hasAnySubRows={hasAnySubRows}
331
334
  inlineRowLoading={inlineRowLoading}
@@ -12,7 +12,7 @@
12
12
  <% content.presence %>
13
13
  <% else %>
14
14
  <%= pb_rails("advanced_table/table_header", props: { table_id: object.id, column_definitions: object.column_definitions, enable_toggle_expansion: object.enable_toggle_expansion, responsive: object.responsive, loading: object.loading, selectable_rows: object.selectable_rows, show_actions_bar: object.show_actions_bar, inline_row_loading: object.inline_row_loading, persist_toggle_expansion_button: object.persist_toggle_expansion_button, table_data: object.table_data }) %>
15
- <%= pb_rails("advanced_table/table_body", props: { table_id: object.id, table_data: object.table_data, column_definitions: object.column_definitions, responsive: object.responsive, loading: object.loading, selectable_rows: object.selectable_rows, enable_toggle_expansion: object.enable_toggle_expansion, row_styling: object.row_styling, inline_row_loading: object.inline_row_loading, full_width_cell: object.full_width_cell, pinned_rows: object.pinned_rows }) %>
15
+ <%= pb_rails("advanced_table/table_body", props: { table_id: object.id, table_data: object.table_data, column_definitions: object.column_definitions, responsive: object.responsive, loading: object.loading, selectable_rows: object.selectable_rows, enable_toggle_expansion: object.enable_toggle_expansion, row_styling: object.row_styling, inline_row_loading: object.inline_row_loading, full_width_cell: object.full_width_cell, group_separators: object.group_separators, pinned_rows: object.pinned_rows }) %>
16
16
  <% end %>
17
17
  <% end %>
18
18
  <% end %>
@@ -37,11 +37,19 @@ module Playbook
37
37
  default: false
38
38
  prop :full_width_cell, type: Playbook::Props::Boolean,
39
39
  default: false
40
+ prop :group_separators, type: Playbook::Props::Boolean,
41
+ default: false
40
42
  prop :persist_toggle_expansion_button, type: Playbook::Props::Boolean,
41
43
  default: false
42
44
  prop :pinned_rows, type: Playbook::Props::HashProp,
43
45
  default: {}
44
46
 
47
+ # Marker for Rails JS: only toggle group-separator-expanded when this is set.
48
+ # Use merge (not tap/mutate) so we don't write onto the shared default {} or a caller-owned data hash.
49
+ def data
50
+ Hash(prop(:data)).merge(group_separators ? { group_separators: true } : {})
51
+ end
52
+
45
53
  def classname
46
54
  additional_classes = [
47
55
  responsive_classname,