playbook_ui 16.8.0.pre.alpha.PLAY2979dropdownmlsportaling16471 → 16.8.0.pre.alpha.tablewidths16466

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/app/pb_kits/playbook/pb_advanced_table/Components/RegularTableView.tsx +2 -0
  3. data/app/pb_kits/playbook/pb_advanced_table/Components/TableHeaderCell.tsx +2 -0
  4. data/app/pb_kits/playbook/pb_advanced_table/Components/VirtualizedTableView.tsx +5 -1
  5. data/app/pb_kits/playbook/pb_advanced_table/Hooks/useTableState.ts +24 -0
  6. data/app/pb_kits/playbook/pb_advanced_table/Utilities/ColumnLayoutHelper.ts +138 -0
  7. data/app/pb_kits/playbook/pb_advanced_table/advanced_table.test.jsx +144 -0
  8. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling.jsx +1 -0
  9. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling.md +6 -0
  10. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_width.jsx +57 -0
  11. data/app/pb_kits/playbook/pb_advanced_table/docs/_advanced_table_column_styling_width.md +66 -0
  12. data/app/pb_kits/playbook/pb_advanced_table/docs/_playground.json +5 -2
  13. data/app/pb_kits/playbook/pb_advanced_table/docs/_playground.overrides.json +1 -1
  14. data/app/pb_kits/playbook/pb_advanced_table/docs/advanced_table_column_definitions_styling.json +4 -1
  15. data/app/pb_kits/playbook/pb_advanced_table/docs/example.yml +1 -0
  16. data/app/pb_kits/playbook/pb_advanced_table/docs/index.js +1 -0
  17. data/app/pb_kits/playbook/pb_dialog/_dialog.scss +1 -54
  18. data/app/pb_kits/playbook/pb_dialog/_dialog.tsx +5 -21
  19. data/app/pb_kits/playbook/pb_dialog/_dialog_context.tsx +2 -8
  20. data/app/pb_kits/playbook/pb_dialog/child_kits/_dialog_header.tsx +1 -1
  21. data/app/pb_kits/playbook/pb_dialog/dialog.html.erb +1 -5
  22. data/app/pb_kits/playbook/pb_dialog/dialog.test.jsx +0 -90
  23. data/app/pb_kits/playbook/pb_dropdown/_dropdown.scss +0 -8
  24. data/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx +31 -101
  25. data/app/pb_kits/playbook/pb_dropdown/index.js +25 -246
  26. data/app/pb_kits/playbook/pb_dropdown/keyboard_accessibility.js +10 -9
  27. data/app/pb_kits/playbook/pb_dropdown/subcomponents/DropdownContainer.tsx +2 -39
  28. data/app/pb_kits/playbook/pb_multi_level_select/_multi_level_select.tsx +10 -125
  29. data/app/pb_kits/playbook/pb_popover/_popover.scss +0 -24
  30. data/app/pb_kits/playbook/pb_popover/_popover.tsx +14 -46
  31. data/app/pb_kits/playbook/pb_popover/index.ts +3 -25
  32. data/app/pb_kits/playbook/pb_popover/popover.html.erb +2 -5
  33. data/app/pb_kits/playbook/pb_popover/popover.test.js +5 -66
  34. data/app/pb_kits/playbook/pb_typeahead/_typeahead.tsx +3 -111
  35. data/app/pb_kits/playbook/pb_typeahead/kit.schema.json +0 -6
  36. data/dist/chunks/{_pb_line_graph-_PxeurZY.js → _pb_line_graph-BgsTI0CL.js} +1 -1
  37. data/dist/chunks/_typeahead-D2TWdJTn.js +5 -0
  38. data/dist/chunks/{globalProps-Dyav4mmr.js → globalProps-DOB47YGB.js} +1 -1
  39. data/dist/chunks/lib-BzglXly2.js +29 -0
  40. data/dist/chunks/vendor.js +5 -5
  41. data/dist/playbook-rails-react-bindings.js +1 -1
  42. data/dist/playbook-rails.js +1 -1
  43. data/dist/playbook.css +1 -1
  44. data/lib/playbook/version.rb +1 -1
  45. metadata +8 -6
  46. data/app/pb_kits/playbook/utilities/floatingPortalHosts.ts +0 -261
  47. data/dist/chunks/_typeahead-Bxdz6EPm.js +0 -5
  48. data/dist/chunks/lib-BqvWeZrf.js +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d6ce1fe6e000fa868604503688310c380206939ba88ae622ff423439f2fa7906
4
- data.tar.gz: 991ea29b625d9279a9f1937220eca3348d5e1958e1ce53ac717d1b59939eb880
3
+ metadata.gz: 0f46fd7de41487efa37647c36cc3d1d55e2260ccda34ff0f094dc4f3c21608c4
4
+ data.tar.gz: 60e1b0388423c2a75088ba9210b0e9456c27fb7a7b5822b5753f1c46f1abfa53
5
5
  SHA512:
6
- metadata.gz: d9228c85c7869b892ab963d59da2aef498490a8f35a2008ced11aa0aae33bcf33662e70573c0df03aa2eef74635a082b09335bec485dc18f27d4bb5963062268
7
- data.tar.gz: 01bbdeb46628b17c036ee32f7ec2a1358a193029f6a0c670a53d21160ab2204525a517042ed172b59b89bc72cfbdf078b2fbe38dd84fecb065bf8f234d5f91bc
6
+ metadata.gz: 236bb6ec1f254c88d37bfa99aa2bb530aba234550a5635bfd19c9bb76fe0240bf818ccd4f2b859424662c2e15d1f31cae47d0831e172a9c2926e1eaa4cf3ade7
7
+ data.tar.gz: 63776f5d3b77b41da7a95ad4d67e89d244a0981fb8cc2220c3fc8996f5506eb2b4f94cf30989d848afc83d2104dbe17092e00851e4b4cd46ebd5a63985ddb543
@@ -5,6 +5,7 @@ import { flexRender, Row, Cell } from "@tanstack/react-table"
5
5
  import { GenericObject } from "../../types"
6
6
  import { isChrome } from "../Utilities/BrowserCheck"
7
7
  import { findColumnDefByAccessor } from "../Utilities/ColumnStylingHelper"
8
+ import { playbookColumnLayoutStylesFromMeta } from "../Utilities/ColumnLayoutHelper"
8
9
  import { getRowColorClass, shouldShowLoadingIndicator } from "../Utilities/RowUtils"
9
10
 
10
11
  import LoadingInline from "../../pb_loading_inline/_loading_inline"
@@ -94,6 +95,7 @@ const TableCellRenderer = ({
94
95
  )}
95
96
  key={`${cell.id}-data`}
96
97
  style={{
98
+ ...playbookColumnLayoutStylesFromMeta(column.columnDef),
97
99
  left: isPinnedLeft
98
100
  ? i === 1 // Accounting for set min-width for first column
99
101
  ? '180px'
@@ -16,6 +16,7 @@ import { SortIconButton } from "./SortIconButton"
16
16
  import { ToggleIconButton } from "./ToggleIconButton"
17
17
  import { displayIcon } from "../Utilities/IconHelpers"
18
18
  import { findColumnDefByAccessor } from "../Utilities/ColumnStylingHelper"
19
+ import { playbookColumnLayoutStylesFromMeta } from "../Utilities/ColumnLayoutHelper"
19
20
  import { updateExpandAndCollapseState } from "../Utilities/ExpansionControlHelpers"
20
21
 
21
22
  import { isChrome } from "../Utilities/BrowserCheck"
@@ -198,6 +199,7 @@ const isToggleExpansionEnabled =
198
199
  id={cellId}
199
200
  key={`${header?.id}-header`}
200
201
  style={{
202
+ ...playbookColumnLayoutStylesFromMeta(header?.column?.columnDef),
201
203
  backgroundColor: headerBackgroundColor,
202
204
  color: headerFontColor,
203
205
  left: isPinnedLeft
@@ -19,6 +19,7 @@ import { LoadingCell } from "../Components/LoadingCell"
19
19
  import { renderCollapsibleTrail } from "../Components/CollapsibleTrail"
20
20
 
21
21
  import AdvancedTableContext from "../Context/AdvancedTableContext"
22
+ import { playbookColumnLayoutStylesFromMeta } from "../Utilities/ColumnLayoutHelper"
22
23
 
23
24
  type VirtualizedTableViewProps = {
24
25
  collapsibleTrail?: boolean
@@ -224,7 +225,10 @@ export const VirtualizedTableView = ({
224
225
  isLastCell && 'last-cell',
225
226
  )}
226
227
  key={`${cell.id}-data`}
227
- style={{ width: cellWidth }}
228
+ style={{
229
+ ...playbookColumnLayoutStylesFromMeta(cell.column.columnDef),
230
+ width: cellWidth,
231
+ }}
228
232
  >
229
233
  {collapsibleTrail && i === 0 && row.depth > 0 && renderCollapsibleTrail(row.depth)}
230
234
  <span id={`${cell.id}-span`}>
@@ -12,6 +12,10 @@ import {
12
12
  import { GenericObject } from "../../types";
13
13
  import { createColumnHelper } from "@tanstack/react-table";
14
14
  import { createCellFunction } from "../Utilities/CellRendererUtils";
15
+ import {
16
+ buildPlaybookColumnLayoutStyles,
17
+ buildTanStackSizingFromColumn,
18
+ } from "../Utilities/ColumnLayoutHelper";
15
19
  import { getParentOnlySortedRowModel } from "../Utilities/RowModelUtils";
16
20
 
17
21
  interface UseTableStateProps {
@@ -105,11 +109,31 @@ export function useTableState({
105
109
  columns: buildColumns(column.columns, false),
106
110
  };
107
111
  }
112
+ const tanStackSizing = buildTanStackSizingFromColumn(column);
113
+ const layoutStyles = buildPlaybookColumnLayoutStyles(column, tanStackSizing);
114
+ const userMeta =
115
+ column.meta &&
116
+ typeof column.meta === "object" &&
117
+ !Array.isArray(column.meta)
118
+ ? column.meta
119
+ : {};
120
+ const hasLayoutStyles =
121
+ layoutStyles.width !== undefined ||
122
+ layoutStyles.minWidth !== undefined ||
123
+ layoutStyles.maxWidth !== undefined;
124
+
108
125
  // Define the base column structure
109
126
  const columnStructure = {
110
127
  ...columnHelper.accessor(column.accessor, {
111
128
  header: column.header ?? column.label ?? "",
112
129
  enableSorting: isFirstColumn || column.enableSort === true,
130
+ ...tanStackSizing,
131
+ meta: {
132
+ ...userMeta,
133
+ ...(hasLayoutStyles
134
+ ? { playbookColumnLayoutStyles: layoutStyles }
135
+ : {}),
136
+ },
113
137
  }),
114
138
  };
115
139
 
@@ -0,0 +1,138 @@
1
+ import type { CSSProperties } from "react"
2
+
3
+ import { GenericObject } from "../../types"
4
+
5
+ /**
6
+ * Converts a Playbook column width value to a CSS length string.
7
+ * Numbers are treated as pixels; strings are passed through (e.g. `"12rem"`, `"200px"`).
8
+ */
9
+ export function cssLength(
10
+ value: number | string | undefined | null
11
+ ): string | undefined {
12
+ if (value === undefined || value === null || value === "") return undefined
13
+ if (typeof value === "number" && Number.isFinite(value)) return `${value}px`
14
+ return String(value)
15
+ }
16
+
17
+ const columnStylingKeys = (column: GenericObject) =>
18
+ (column.columnStyling || column.column_styling || {}) as GenericObject
19
+
20
+ function readStylingLength(
21
+ styling: GenericObject,
22
+ camel: string,
23
+ snake?: string
24
+ ): string | number | undefined {
25
+ if (snake) return styling[camel] ?? styling[snake]
26
+ return styling[camel] as string | number | undefined
27
+ }
28
+
29
+ /**
30
+ * TanStack Table v8 `ColumnDef` sizing fields we forward from each `columnDefinitions` entry.
31
+ * See AdvancedTable kit docs for precedence with `columnStyling` width keys.
32
+ */
33
+ export const PLAYBOOK_FORWARDED_COLUMN_DEF_SIZING_KEYS = [
34
+ "size",
35
+ "minSize",
36
+ "maxSize",
37
+ ] as const
38
+
39
+ export type PlaybookForwardedColumnSizing = Partial<
40
+ Record<(typeof PLAYBOOK_FORWARDED_COLUMN_DEF_SIZING_KEYS)[number], number>
41
+ >
42
+
43
+ export function buildTanStackSizingFromColumn(
44
+ column: GenericObject
45
+ ): PlaybookForwardedColumnSizing {
46
+ const out: PlaybookForwardedColumnSizing = {}
47
+ PLAYBOOK_FORWARDED_COLUMN_DEF_SIZING_KEYS.forEach((key) => {
48
+ const v = column[key]
49
+ if (typeof v === "number" && Number.isFinite(v)) out[key] = v
50
+ })
51
+
52
+ const styling = columnStylingKeys(column)
53
+
54
+ if (out.size === undefined && readStylingLength(styling, "width") !== undefined) {
55
+ const w = readStylingLength(styling, "width")
56
+ if (typeof w === "number" && Number.isFinite(w)) out.size = w
57
+ }
58
+ if (out.minSize === undefined && readStylingLength(styling, "minWidth", "min_width") !== undefined) {
59
+ const w = readStylingLength(styling, "minWidth", "min_width")
60
+ if (typeof w === "number" && Number.isFinite(w)) out.minSize = w
61
+ }
62
+ if (out.maxSize === undefined && readStylingLength(styling, "maxWidth", "max_width") !== undefined) {
63
+ const w = readStylingLength(styling, "maxWidth", "max_width")
64
+ if (typeof w === "number" && Number.isFinite(w)) out.maxSize = w
65
+ }
66
+
67
+ // width/size only → fixed column (min and max match preferred)
68
+ if (out.size !== undefined && out.minSize === undefined && out.maxSize === undefined) {
69
+ out.minSize = out.size
70
+ out.maxSize = out.size
71
+ }
72
+
73
+ return out
74
+ }
75
+
76
+ /**
77
+ * Inline width styles for `<th>` / `<td>` so `table-layout: fixed` tables keep stable
78
+ * columns when row content changes (e.g. expand/collapse). Values mirror TanStack sizing
79
+ * and/or `columnStyling` width keys; string values in `columnStyling` win over numeric
80
+ * TanStack fields when both describe the same axis.
81
+ */
82
+ export function buildPlaybookColumnLayoutStyles(
83
+ column: GenericObject,
84
+ tanStackSizing: PlaybookForwardedColumnSizing
85
+ ): CSSProperties {
86
+ const styling = columnStylingKeys(column)
87
+ const styles: CSSProperties = {}
88
+
89
+ const stylingWidth = readStylingLength(styling, "width")
90
+ const stylingMin = readStylingLength(styling, "minWidth", "min_width")
91
+ const stylingMax = readStylingLength(styling, "maxWidth", "max_width")
92
+
93
+ const hasStylingWidth = stylingWidth !== undefined && stylingWidth !== ""
94
+ const hasStylingMin = stylingMin !== undefined && stylingMin !== ""
95
+ const hasStylingMax = stylingMax !== undefined && stylingMax !== ""
96
+
97
+ let widthValue: string | number | undefined = hasStylingWidth
98
+ ? stylingWidth
99
+ : tanStackSizing.size
100
+ let minValue: string | number | undefined = hasStylingMin
101
+ ? stylingMin
102
+ : tanStackSizing.minSize
103
+ let maxValue: string | number | undefined = hasStylingMax
104
+ ? stylingMax
105
+ : tanStackSizing.maxSize
106
+
107
+ const preferredForLock = hasStylingWidth ? stylingWidth : tanStackSizing.size
108
+ const explicitMin = hasStylingMin || tanStackSizing.minSize !== undefined
109
+ const explicitMax = hasStylingMax || tanStackSizing.maxSize !== undefined
110
+
111
+ if (
112
+ preferredForLock !== undefined &&
113
+ preferredForLock !== "" &&
114
+ !explicitMin &&
115
+ !explicitMax
116
+ ) {
117
+ minValue = preferredForLock
118
+ maxValue = preferredForLock
119
+ widthValue = preferredForLock
120
+ }
121
+
122
+ const widthCss = cssLength(widthValue)
123
+ const minCss = cssLength(minValue)
124
+ const maxCss = cssLength(maxValue)
125
+
126
+ if (widthCss !== undefined) styles.width = widthCss
127
+ if (minCss !== undefined) styles.minWidth = minCss
128
+ if (maxCss !== undefined) styles.maxWidth = maxCss
129
+
130
+ return styles
131
+ }
132
+
133
+ export function playbookColumnLayoutStylesFromMeta(
134
+ columnDef: GenericObject | undefined
135
+ ): CSSProperties {
136
+ const meta = columnDef?.meta as GenericObject | undefined
137
+ return (meta?.playbookColumnLayoutStyles || {}) as CSSProperties
138
+ }
@@ -774,6 +774,150 @@ test("columnStyling.cellPadding sets cell padding", () => {
774
774
  expect(firstEnrollmentCell).toHaveClass('p_none')
775
775
  });
776
776
 
777
+ test("columnStyling minWidth, width, and maxWidth apply to header and body cells", () => {
778
+ const styledColumnDefs = [
779
+ {
780
+ accessor: "year",
781
+ label: "Year",
782
+ cellAccessors: ["quarter", "month", "day"],
783
+ columnStyling: { minWidth: 240, width: 260, maxWidth: 400 },
784
+ },
785
+ {
786
+ accessor: "newEnrollments",
787
+ label: "New Enrollments",
788
+ },
789
+ {
790
+ accessor: "scheduledMeetings",
791
+ label: "Scheduled Meetings",
792
+ },
793
+ ];
794
+
795
+ render(
796
+ <AdvancedTable
797
+ columnDefinitions={styledColumnDefs}
798
+ data={{ testid: testId }}
799
+ tableData={MOCK_DATA}
800
+ />
801
+ );
802
+
803
+ const yearHeader = screen.getByText("Year").closest("th");
804
+ expect(yearHeader).toHaveStyle({ minWidth: "240px", width: "260px", maxWidth: "400px" });
805
+
806
+ const yearCell = screen.getAllByText("2021")[0].closest("td");
807
+ expect(yearCell).toHaveStyle({ minWidth: "240px", width: "260px", maxWidth: "400px" });
808
+ });
809
+
810
+ test("columnStyling width only locks minWidth and maxWidth to the same value", () => {
811
+ const styledColumnDefs = [
812
+ {
813
+ accessor: "year",
814
+ label: "Year",
815
+ cellAccessors: ["quarter", "month", "day"],
816
+ columnStyling: { width: 128 },
817
+ },
818
+ {
819
+ accessor: "newEnrollments",
820
+ label: "New Enrollments",
821
+ },
822
+ {
823
+ accessor: "scheduledMeetings",
824
+ label: "Scheduled Meetings",
825
+ },
826
+ ];
827
+
828
+ render(
829
+ <AdvancedTable
830
+ columnDefinitions={styledColumnDefs}
831
+ data={{ testid: testId }}
832
+ tableData={MOCK_DATA}
833
+ />
834
+ );
835
+
836
+ const yearHeader = screen.getByText("Year").closest("th");
837
+ expect(yearHeader).toHaveStyle({
838
+ width: "128px",
839
+ minWidth: "128px",
840
+ maxWidth: "128px",
841
+ });
842
+ });
843
+
844
+ test("columnDefinitions size only locks minSize and maxSize to the same value", () => {
845
+ const styledColumnDefs = [
846
+ {
847
+ accessor: "year",
848
+ label: "Year",
849
+ cellAccessors: ["quarter", "month", "day"],
850
+ },
851
+ {
852
+ accessor: "newEnrollments",
853
+ label: "New Enrollments",
854
+ size: 200,
855
+ },
856
+ {
857
+ accessor: "scheduledMeetings",
858
+ label: "Scheduled Meetings",
859
+ },
860
+ ];
861
+
862
+ render(
863
+ <AdvancedTable
864
+ columnDefinitions={styledColumnDefs}
865
+ data={{ testid: testId }}
866
+ tableData={MOCK_DATA}
867
+ />
868
+ );
869
+
870
+ const enrollmentsHeader = screen.getByText("New Enrollments").closest("th");
871
+ expect(enrollmentsHeader).toHaveStyle({
872
+ width: "200px",
873
+ minWidth: "200px",
874
+ maxWidth: "200px",
875
+ });
876
+ });
877
+
878
+ test("columnDefinitions size, minSize, and maxSize apply layout styles", () => {
879
+ const styledColumnDefs = [
880
+ {
881
+ accessor: "year",
882
+ label: "Year",
883
+ cellAccessors: ["quarter", "month", "day"],
884
+ },
885
+ {
886
+ accessor: "newEnrollments",
887
+ label: "New Enrollments",
888
+ size: 180,
889
+ minSize: 160,
890
+ maxSize: 320,
891
+ },
892
+ {
893
+ accessor: "scheduledMeetings",
894
+ label: "Scheduled Meetings",
895
+ },
896
+ ];
897
+
898
+ render(
899
+ <AdvancedTable
900
+ columnDefinitions={styledColumnDefs}
901
+ data={{ testid: testId }}
902
+ tableData={MOCK_DATA}
903
+ />
904
+ );
905
+
906
+ const enrollmentsHeader = screen.getByText("New Enrollments").closest("th");
907
+ expect(enrollmentsHeader).toHaveStyle({
908
+ width: "180px",
909
+ minWidth: "160px",
910
+ maxWidth: "320px",
911
+ });
912
+
913
+ const enrollmentsCell = screen.getAllByText("20")[0].closest("td");
914
+ expect(enrollmentsCell).toHaveStyle({
915
+ width: "180px",
916
+ minWidth: "160px",
917
+ maxWidth: "320px",
918
+ });
919
+ });
920
+
777
921
  test("columnStyling.fontColor sets cell font color", () => {
778
922
  const styledColumnDefs = [
779
923
  {
@@ -10,6 +10,7 @@ const AdvancedTableColumnStyling = (props) => {
10
10
  accessor: "year",
11
11
  label: "Year",
12
12
  cellAccessors: ["quarter", "month", "day"],
13
+ columnStyling: { width: 124 },
13
14
  },
14
15
  {
15
16
  accessor: "newEnrollments",
@@ -6,4 +6,10 @@ The `columnStyling` prop is an optional item that can be used within `columnDefi
6
6
 
7
7
  3) `fontColor`: This will allow you to control the font color for a given column.
8
8
 
9
+ 4) Column width: optional keys on `columnStyling` are `minWidth`, `width`, and `maxWidth` (numbers = pixels; CSS strings allowed). This example sets `width` on Year for a fixed hierarchy column (see the width doc for `minWidth` and bands).
10
+
11
+ Fixed width: pass `width` only (or TanStack `size` only) and Playbook sets min and max to the same value automatically — you do not need all three for an exact width.
12
+
13
+ See [Column Styling: Width](https://playbook.powerapp.cloud/kits/advanced_table/react#column-styling-width) for the full guide (Playbook ↔ TanStack mapping, floor-only vs bands, and when to use one vs three values).
14
+
9
15
  `columnStyling` can be used within the columnDefinition of all the columns or some of them, as shown. Each column has its own individual control in this way.
@@ -0,0 +1,57 @@
1
+ import React from "react"
2
+ import AdvancedTable from "../_advanced_table"
3
+ import MOCK_DATA from "./advanced_table_mock_data.json"
4
+
5
+ /**
6
+ * Kit doc: compares fixed width, floor-only, TanStack band, and min/preferred/max band.
7
+ */
8
+ const AdvancedTableColumnStylingWidth = (props) => {
9
+ const columnDefinitions = [
10
+ {
11
+ accessor: "year",
12
+ label: "Year (fixed)",
13
+ cellAccessors: ["quarter", "month", "day"],
14
+ // width alone locks min + max to the same value (128px).
15
+ columnStyling: { width: 128 },
16
+ },
17
+ {
18
+ accessor: "newEnrollments",
19
+ label: "Enrollments (floor)",
20
+ columnStyling: { minWidth: 160 },
21
+ },
22
+ {
23
+ accessor: "scheduledMeetings",
24
+ label: "Meetings (TanStack band)",
25
+ size: 200,
26
+ minSize: 160,
27
+ maxSize: 240,
28
+ },
29
+ {
30
+ accessor: "attendanceRate",
31
+ label: "Attendance (min / pref / max)",
32
+ columnStyling: { minWidth: 108, width: 124, maxWidth: 168 },
33
+ },
34
+ {
35
+ accessor: "completedClasses",
36
+ label: "Completed",
37
+ },
38
+ {
39
+ accessor: "classCompletionRate",
40
+ label: "Completion %",
41
+ },
42
+ {
43
+ accessor: "graduatedStudents",
44
+ label: "Graduated",
45
+ },
46
+ ]
47
+
48
+ return (
49
+ <AdvancedTable
50
+ columnDefinitions={columnDefinitions}
51
+ tableData={MOCK_DATA}
52
+ {...props}
53
+ />
54
+ )
55
+ }
56
+
57
+ export default AdvancedTableColumnStylingWidth
@@ -0,0 +1,66 @@
1
+ AdvancedTable column width is controlled in two equivalent places on each leaf `columnDefinitions` entry. Playbook maps them to inline styles on header and body cells (and forwards numeric values into TanStack Table’s column model).
2
+
3
+ 1) Playbook `columnStyling` and TanStack `ColumnDef` use the same three ideas:
4
+
5
+ - Preferred / target width: `columnStyling` `width` maps to TanStack `size` on the same column object.
6
+ - Minimum width (floor): `columnStyling` `minWidth` maps to TanStack `minSize`.
7
+ - Maximum width (ceiling): `columnStyling` `maxWidth` maps to TanStack `maxSize`.
8
+
9
+ Numbers are pixels. You can also pass CSS length strings on `columnStyling` (e.g. `"12rem"`, `"200px"`). TanStack fields accept numbers only.
10
+
11
+ If both APIs set the same axis, `columnStyling` wins for that axis when Playbook builds cell styles.
12
+
13
+ 2) Fixed width: set `width` only
14
+
15
+ If you pass only `width` (or only `size`) and do not set `minWidth` / `maxWidth` (or `minSize` / `maxSize`), Playbook treats that as a fixed column: it sets all three to the same value under the hood so you do not have to repeat yourself.
16
+
17
+ ```jsx
18
+ columnStyling: { width: 128 }
19
+ // Applied as width, minWidth, and maxWidth: 128px
20
+ ```
21
+
22
+ ```jsx
23
+ size: 200
24
+ // Forwarded as size, minSize, and maxSize: 200
25
+ ```
26
+
27
+ Use this when the column should stay one width (e.g. a hierarchy column with expand controls).
28
+
29
+ 3) Floor only: `minWidth` / `minSize`
30
+
31
+ Set only a minimum when the column may grow with the table or content but must not shrink below a baseline (common fix for horizontal “jump” when rows expand):
32
+
33
+ ```jsx
34
+ columnStyling: { minWidth: 160 }
35
+ ```
36
+
37
+ 4) Flexible band: min + preferred + max
38
+
39
+ Set two or three values when you want a range. CSS uses preferred `width` clamped between `minWidth` and `maxWidth`:
40
+
41
+ - `minWidth`: won’t shrink below this.
42
+ - `width`: preferred size when space allows.
43
+ - `maxWidth`: won’t grow above this.
44
+
45
+ Example from the table below (Attendance): `minWidth: 108`, `width: 124`, `maxWidth: 168` → preferred 124px, allowed between 108 and 168.
46
+
47
+ You only need all three when you want that band. If min and max are omitted, `width` alone is enough for a fixed column.
48
+
49
+ 5) TanStack band without `columnStyling`
50
+
51
+ The Meetings column uses only TanStack fields:
52
+
53
+ ```jsx
54
+ { accessor: "scheduledMeetings", size: 200, minSize: 160, maxSize: 240 }
55
+ ```
56
+
57
+ Playbook applies the same min / preferred / max idea to cell styles. Setting only `size: 200` would lock all three to 200, same as `width: 200` in `columnStyling`.
58
+
59
+ 6) What the example table shows
60
+
61
+ - Year (fixed): `columnStyling: { width: 128 }` — locked to 128px.
62
+ - Enrollments (floor): `columnStyling: { minWidth: 160 }` — at least 160px; can grow.
63
+ - Meetings (TanStack band): `size` / `minSize` / `maxSize` — preferred 200px, between 160–240.
64
+ - Attendance (min / pref / max): all three in `columnStyling` — preferred 124px, between 108–168.
65
+
66
+ Grouped columns: put width options on leaf definitions (columns with an `accessor`), not on parent group headers.
@@ -313,7 +313,7 @@
313
313
  },
314
314
  "column_styling_info": {
315
315
  "presetName": "Column Styling",
316
- "message": "This sample uses columnStyling on column definitions. You can style headers and cells for the whole table or per column.",
316
+ "message": "This sample uses columnStyling on column definitions. Year uses width for a fixed hierarchy column; other columns show background colors. See the Column Styling Width doc for minWidth and min/pref/max bands.",
317
317
  "type": "info"
318
318
  }
319
319
  },
@@ -2628,7 +2628,10 @@
2628
2628
  "quarter",
2629
2629
  "month",
2630
2630
  "day"
2631
- ]
2631
+ ],
2632
+ "columnStyling": {
2633
+ "width": 124
2634
+ }
2632
2635
  },
2633
2636
  {
2634
2637
  "accessor": "newEnrollments",
@@ -371,7 +371,7 @@
371
371
  },
372
372
  "column_styling_info": {
373
373
  "presetName": "Column Styling",
374
- "message": "This sample uses columnStyling on column definitions. You can style headers and cells for the whole table or per column.",
374
+ "message": "This sample uses columnStyling on column definitions. Year uses width for a fixed hierarchy column; other columns show background colors. See the Column Styling Width doc for minWidth and min/pref/max bands.",
375
375
  "type": "info"
376
376
  }
377
377
  }
@@ -2,7 +2,10 @@
2
2
  {
3
3
  "accessor": "year",
4
4
  "label": "Year",
5
- "cellAccessors": ["quarter", "month", "day"]
5
+ "cellAccessors": ["quarter", "month", "day"],
6
+ "columnStyling": {
7
+ "width": 124
8
+ }
6
9
  },
7
10
  {
8
11
  "accessor": "newEnrollments",
@@ -80,6 +80,7 @@ examples:
80
80
  - advanced_table_row_styling: Row Styling
81
81
  - advanced_table_padding_control_per_row: Padding Control using Row Styling
82
82
  - advanced_table_column_styling: Column Styling
83
+ - advanced_table_column_styling_width: Column Styling Width
83
84
  - advanced_table_column_styling_column_headers: Column Styling with Multiple Headers
84
85
  - advanced_table_column_styling_background: Column Styling Background Color
85
86
  - advanced_table_column_styling_background_custom: Column Styling Background Color (Custom)
@@ -37,6 +37,7 @@ export { default as AdvancedTablePinnedRows } from './_advanced_table_pinned_row
37
37
  export { default as AdvancedTableScrollbarNone} from './_advanced_table_scrollbar_none.jsx'
38
38
  export { default as AdvancedTableRowStyling } from './_advanced_table_row_styling.jsx'
39
39
  export { default as AdvancedTableColumnStyling } from './_advanced_table_column_styling.jsx'
40
+ export { default as AdvancedTableColumnStylingWidth } from './_advanced_table_column_styling_width.jsx'
40
41
  export { default as AdvancedTableColumnStylingColumnHeaders } from './_advanced_table_column_styling_column_headers.jsx'
41
42
  export { default as AdvancedTableInfiniteScroll} from './_advanced_table_infinite_scroll.jsx'
42
43
  export {default as AdvancedTableWithCustomHeader} from './_advanced_table_with_custom_header.jsx'
@@ -151,33 +151,11 @@
151
151
  border-radius: $border_radius_md;
152
152
  max-height: calc(100vh - #{$gutter * 2});
153
153
  max-width: calc(100vw - #{$gutter * 2});
154
- display: flex;
155
- flex-direction: column;
156
- min-height: 0;
157
- overflow: visible;
154
+ overflow: auto;
158
155
  animation-name: modalFadeIn;
159
156
  animation-duration: $animation-duration;
160
157
  outline: none;
161
158
  animation-timing-function: $easeInOutQuint;
162
-
163
- .pb_dialog_scroll_region {
164
- flex: 1 1 auto;
165
- min-height: 0;
166
- overflow: auto;
167
- -webkit-overflow-scrolling: touch;
168
- }
169
-
170
- .pb_dialog_floating_root {
171
- flex: 0 0 auto;
172
- min-height: 0;
173
- pointer-events: none;
174
- position: relative;
175
- z-index: 1;
176
- }
177
-
178
- .pb_dialog_floating_root > * {
179
- pointer-events: auto;
180
- }
181
159
  }
182
160
 
183
161
  // Left placement animations
@@ -606,35 +584,4 @@
606
584
  border: none;
607
585
  cursor: pointer;
608
586
  }
609
-
610
- // Rails native dialog: scroll region + floating portal host for portaled form inputs
611
- dialog.pb_dialog_rails:not([open]) {
612
- display: none !important;
613
- }
614
-
615
- .pb_dialog_rails[open] {
616
- display: flex;
617
- flex-direction: column;
618
- min-height: 0;
619
- overflow: visible;
620
- }
621
-
622
- .pb_dialog_scroll_region {
623
- flex: 1 1 auto;
624
- min-height: 0;
625
- overflow: auto;
626
- -webkit-overflow-scrolling: touch;
627
- }
628
-
629
- .pb_dialog_floating_root {
630
- flex: 0 0 auto;
631
- min-height: 0;
632
- pointer-events: none;
633
- position: relative;
634
- z-index: 1;
635
- }
636
-
637
- .pb_dialog_floating_root > * {
638
- pointer-events: auto;
639
- }
640
587
  }