Skip to content

Commit 254837e

Browse files
authored
[PBNTR-924] Table kit: Custom headers - React Only (#4413)
**What does this PR do?** A clear and concise description with your runway ticket url. [PBNTR-924](https://runway.powerhrg.com/backlog_items/PBNTR-924) adds the headerStyle prop to the React Table kit which allows for header customization. Two doc examples showcasing different custom headers - a borderless table on a generic background and a floating table where the header's backgroundColor can be altered to match the container in which the table is nested have been added in accordance with the [investigation/POC in PBNTR-786](https://huddle.powerapp.cloud/projects/647/artifacts/7904), as have tests. **Screenshots:** Screenshots to visualize your addition/change <img width="1344" alt="both doc examples" src="https://github.com/user-attachments/assets/0878a81d-2098-4c20-90d3-75dfc4923c05" /> **How to test?** Steps to confirm the desired behavior: 1. Go to the two header style doc examples in the review environment ([borderless](https://pr4413.playbook.beta.px.powerapp.cloud/kits/table/react#header-style-borderless) and [floating](https://pr4413.playbook.beta.px.powerapp.cloud/kits/table/react#header-style-floating)) and see how the Table without a border appears in both scenarios. #### Checklist: - [x] **LABELS** Add a label: `enhancement`, `bug`, `improvement`, `new kit`, `deprecated`, or `breaking`. See [Changelog & Labels](https://github.com/powerhome/playbook/wiki/Changelog-&-Labels) for details. - [x] **DEPLOY** I have added the `milano` label to show I'm ready for a review. - [x] **TESTS** I have added test coverage to my code.
1 parent d0f0298 commit 254837e

12 files changed

+239
-2
lines changed

Diff for: playbook/app/pb_kits/playbook/pb_table/_table.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type TableProps = {
2121
data?: { [key: string]: string },
2222
dataTable: boolean,
2323
disableHover?: boolean,
24+
headerStyle?: "default" | "borderless" | "floating"
2425
htmlOptions?: { [key: string]: string | number | boolean | (() => void) },
2526
id?: string,
2627
outerPadding?: "none" | "xxs" | "xs" | "sm" | "md" | "lg" | "xl",
@@ -46,6 +47,7 @@ const Table = (props: TableProps): React.ReactElement => {
4647
data = {},
4748
dataTable = false,
4849
disableHover = false,
50+
headerStyle = "default",
4951
htmlOptions = {},
5052
id,
5153
outerPadding = '',
@@ -85,6 +87,8 @@ const Table = (props: TableProps): React.ReactElement => {
8587
'sticky-left-column': stickyLeftColumn,
8688
'sticky-right-column': stickyRightColumn,
8789
'striped': striped,
90+
'header-borderless': headerStyle === 'borderless',
91+
'header-floating': headerStyle === 'floating',
8892
[outerPaddingCss]: outerPadding !== '',
8993
},
9094
globalProps(props),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React from 'react'
2+
3+
import Table from '../../pb_table/_table'
4+
5+
const TableWithHeaderStyleBorderless = (props) => {
6+
return (
7+
<>
8+
<Table
9+
headerStyle="borderless"
10+
size="sm"
11+
{...props}
12+
>
13+
<thead>
14+
<tr>
15+
<th>{'Column 1'}</th>
16+
<th>{'Column 2'}</th>
17+
<th>{'Column 3'}</th>
18+
<th>{'Column 4'}</th>
19+
<th>{'Column 5'}</th>
20+
</tr>
21+
</thead>
22+
<tbody>
23+
<tr>
24+
<td>{'Value 1'}</td>
25+
<td>{'Value 2'}</td>
26+
<td>{'Value 3'}</td>
27+
<td>{'Value 4'}</td>
28+
<td>{'Value 5'}</td>
29+
</tr>
30+
<tr>
31+
<td>{'Value 1'}</td>
32+
<td>{'Value 2'}</td>
33+
<td>{'Value 3'}</td>
34+
<td>{'Value 4'}</td>
35+
<td>{'Value 5'}</td>
36+
</tr>
37+
<tr>
38+
<td>{'Value 1'}</td>
39+
<td>{'Value 2'}</td>
40+
<td>{'Value 3'}</td>
41+
<td>{'Value 4'}</td>
42+
<td>{'Value 5'}</td>
43+
</tr>
44+
</tbody>
45+
</Table>
46+
</>
47+
)
48+
}
49+
50+
export default TableWithHeaderStyleBorderless
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Customize your header by removing the header borders with the `headerStyle="borderless"` prop.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import React from 'react'
2+
3+
import Background from '../../pb_background/_background'
4+
import Card from '../../pb_card/_card'
5+
import Table from '../../pb_table/_table'
6+
7+
const TableWithHeaderStyleFloating = (props) => {
8+
return (
9+
<>
10+
<Card background="light"
11+
{...props}
12+
>
13+
<Table
14+
headerStyle="floating"
15+
size="sm"
16+
{...props}
17+
>
18+
<Table.Head>
19+
<Background backgroundColor="light"
20+
tag="tr"
21+
{...props}
22+
>
23+
<Table.Header>{'Column 1'}</Table.Header>
24+
<Table.Header>{'Column 2'}</Table.Header>
25+
<Table.Header>{'Column 3'}</Table.Header>
26+
<Table.Header>{'Column 4'}</Table.Header>
27+
<Table.Header>{'Column 5'}</Table.Header>
28+
</Background>
29+
</Table.Head>
30+
<Table.Body>
31+
<Table.Row>
32+
<Table.Cell>{'Value 1'}</Table.Cell>
33+
<Table.Cell>{'Value 2'}</Table.Cell>
34+
<Table.Cell>{'Value 3'}</Table.Cell>
35+
<Table.Cell>{'Value 4'}</Table.Cell>
36+
<Table.Cell>{'Value 5'}</Table.Cell>
37+
</Table.Row>
38+
<Table.Row>
39+
<Table.Cell>{'Value 1'}</Table.Cell>
40+
<Table.Cell>{'Value 2'}</Table.Cell>
41+
<Table.Cell>{'Value 3'}</Table.Cell>
42+
<Table.Cell>{'Value 4'}</Table.Cell>
43+
<Table.Cell>{'Value 5'}</Table.Cell>
44+
</Table.Row>
45+
<Table.Row>
46+
<Table.Cell>{'Value 1'}</Table.Cell>
47+
<Table.Cell>{'Value 2'}</Table.Cell>
48+
<Table.Cell>{'Value 3'}</Table.Cell>
49+
<Table.Cell>{'Value 4'}</Table.Cell>
50+
<Table.Cell>{'Value 5'}</Table.Cell>
51+
</Table.Row>
52+
</Table.Body>
53+
</Table>
54+
</Card>
55+
</>
56+
)
57+
}
58+
59+
export default TableWithHeaderStyleFloating
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Further customize your header by using the [table with background kit](https://playbook.powerapp.cloud/kits/table/react#table-with-background-kit) logic to give your table header a custom background color. Use the `headerStyle="floating"` prop to visually nest the borderless table within a card or collapsible with a matching background color (the `backgroundColor` passed to Background kit should match the `backgroundColor` for the element in which it is nested).

Diff for: playbook/app/pb_kits/playbook/pb_table/docs/example.yml

+2
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,5 @@ examples:
7575
- table_with_collapsible_with_nested_table: Table with Collapsible with Nested Table
7676
- table_with_clickable_rows: Table with Clickable Rows
7777
- table_with_selectable_rows: Table with Selectable Rows
78+
- table_with_header_style_borderless: Header Style Borderless
79+
- table_with_header_style_floating: Header Style Floating

Diff for: playbook/app/pb_kits/playbook/pb_table/docs/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,5 @@ export { default as TableWithCollapsibleWithNestedRows } from './_table_with_col
3535
export { default as TableWithCollapsibleWithCustomClick } from './_table_with_collapsible_with_custom_click.jsx'
3636
export { default as TableWithSelectableRows } from './_table_with_selectable_rows.jsx'
3737
export { default as TableWithClickableRows } from './_table_with_clickable_rows.jsx'
38+
export { default as TableWithHeaderStyleBorderless } from './_table_with_header_style_borderless.jsx'
39+
export { default as TableWithHeaderStyleFloating } from './_table_with_header_style_floating.jsx'

Diff for: playbook/app/pb_kits/playbook/pb_table/styles/_headers.scss

+76
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,79 @@
1414
}
1515
}
1616
}
17+
// remove all outward facing borders from header
18+
.header-borderless > thead,
19+
.header-borderless > thead > tr,
20+
.header-borderless > thead > tr > th,
21+
.header-borderless > .pb_table_thead,
22+
.header-borderless > .pb_table_thead > .pb_table_tr,
23+
.header-borderless > .pb_table_thead .pb_table_th {
24+
border-top: none !important;
25+
border-left: none !important;
26+
border-right: none !important;
27+
}
28+
29+
// remove bottom border (internal to table) on header cells - only light mode needs this redundancy handled likely due to a difference in the base scss and table-dark.scss
30+
.header-borderless:not(.table-dark) > thead > tr:last-child > th,
31+
.header-borderless:not(.table-dark) > .pb_table_thead > .pb_table_tr:last-child > .pb_table_th {
32+
border-bottom: none !important;
33+
}
34+
35+
// preserves top rounded corners to header at top of table-card for unnested card
36+
.header-borderless > thead > tr:first-child > th:first-child,
37+
.header-borderless > .pb_table_thead > .pb_table_tr:first-child > .pb_table_th:first-child {
38+
border-top-left-radius: $border_radius_md !important;
39+
}
40+
41+
.header-borderless > thead > tr:first-child > th:last-child,
42+
.header-borderless > .pb_table_thead > .pb_table_tr:first-child > .pb_table_th:last-child {
43+
border-top-right-radius: $border_radius_md !important;
44+
}
45+
46+
// ensures top border is from first row of body to prevent double borders
47+
.header-borderless > tbody > tr:first-child > td,
48+
.header-borderless > .pb_table_tbody > .pb_table_tr:first-child > .pb_table_td {
49+
border-top: 1px solid $border_light !important;
50+
}
51+
52+
// floating code - this carries over everything for header-borderless
53+
.header-floating > thead,
54+
.header-floating > thead > tr,
55+
.header-floating > thead > tr > th,
56+
.header-floating > .pb_table_thead,
57+
.header-floating > .pb_table_thead > .pb_table_tr,
58+
.header-floating > .pb_table_thead .pb_table_th {
59+
border-top: none !important;
60+
border-left: none !important;
61+
border-right: none !important;
62+
}
63+
64+
.header-floating:not(.table-dark) > thead > tr:last-child > th,
65+
.header-floating:not(.table-dark) > .pb_table_thead > .pb_table_tr:last-child > .pb_table_th {
66+
border-bottom: none !important;
67+
}
68+
69+
.header-floating > thead > tr:first-child > th:first-child,
70+
.header-floating > .pb_table_thead > .pb_table_tr:first-child > .pb_table_th:first-child {
71+
border-top-left-radius: $border_radius_md !important;
72+
}
73+
74+
.header-floating > thead > tr:first-child > th:last-child,
75+
.header-floating > .pb_table_thead > .pb_table_tr:first-child > .pb_table_th:last-child {
76+
border-top-right-radius: $border_radius_md !important;
77+
}
78+
79+
.header-floating > tbody > tr:first-child > td,
80+
.header-floating > .pb_table_tbody > .pb_table_tr:first-child > .pb_table_td {
81+
border-top: 1px solid $border_light !important;
82+
}
83+
84+
// flatten out corners for floating headerstyle variant to avoid small triangle of white/empty space
85+
.header-floating > thead > tr:first-child > th:first-child,
86+
.header-floating > .pb_table_thead > .pb_table_tr:first-child > .pb_table_th:first-child {
87+
border-top-left-radius: 0 !important;
88+
}
89+
.header-floating > thead > tr:first-child > th:last-child,
90+
.header-floating > .pb_table_thead > .pb_table_tr:first-child > .pb_table_th:last-child {
91+
border-top-right-radius: 0 !important;
92+
}

Diff for: playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_head.tsx

+11-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ type TableHeadPropTypes = {
1212
children: React.ReactNode[] | React.ReactNode;
1313
className: string;
1414
data?: { [key: string]: string };
15+
headerStyle?: "default" | "borderless" | "floating";
1516
htmlOptions?: { [key: string]: string | number | boolean | (() => void) };
1617
id?: string;
1718
tag?: "table" | "div";
@@ -23,6 +24,7 @@ const TableHead = (props: TableHeadPropTypes): React.ReactElement => {
2324
children,
2425
className,
2526
data = {},
27+
headerStyle = "default",
2628
htmlOptions = {},
2729
id,
2830
tag = "table",
@@ -31,7 +33,15 @@ const TableHead = (props: TableHeadPropTypes): React.ReactElement => {
3133
const ariaProps = buildAriaProps(aria);
3234
const dataProps = buildDataProps(data);
3335
const htmlProps = buildHtmlProps(htmlOptions);
34-
const classes = classnames("pb_table_thead", globalProps(props), className);
36+
const classes = classnames(
37+
"pb_table_thead",
38+
{
39+
"pb_table_thead_borderless": headerStyle === "borderless" || headerStyle === "floating",
40+
"pb_table_thead_floating": headerStyle === "floating",
41+
},
42+
globalProps(props),
43+
className
44+
);
3545
const isTableTag = tag === "table";
3646

3747
return (

Diff for: playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_header.tsx

+11-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ type TableHeaderPropTypes = {
1212
children: React.ReactNode[] | React.ReactNode;
1313
className: string;
1414
data?: { [key: string]: string };
15+
headerStyle?: "default" | "borderless" | "floating";
1516
htmlOptions?: { [key: string]: string | number | boolean | (() => void) };
1617
id?: string;
1718
tag?: "table" | "div";
@@ -24,6 +25,7 @@ const TableHeader = (props: TableHeaderPropTypes): React.ReactElement => {
2425
children,
2526
className,
2627
data = {},
28+
headerStyle = "default",
2729
htmlOptions = {},
2830
id,
2931
tag = "table",
@@ -33,7 +35,15 @@ const TableHeader = (props: TableHeaderPropTypes): React.ReactElement => {
3335
const ariaProps = buildAriaProps(aria);
3436
const dataProps = buildDataProps(data);
3537
const htmlProps = buildHtmlProps(htmlOptions);
36-
const classes = classnames("pb_table_th", globalProps(props), className);
38+
const classes = classnames(
39+
"pb_table_th",
40+
{
41+
"pb_table_thead_borderless": headerStyle === "borderless" || headerStyle === "floating",
42+
"pb_table_thead_floating": headerStyle === "floating",
43+
},
44+
globalProps(props),
45+
className
46+
);
3747
const isTableTag = tag === "table";
3848

3949
return (

Diff for: playbook/app/pb_kits/playbook/pb_table/subcomponents/_table_row.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type TableRowPropTypes = {
2222
dark?: boolean;
2323
dragId?: string;
2424
draggableItem?: boolean;
25+
headerStyle?: "default" | "borderless" | "floating";
2526
htmlOptions?: { [key: string]: string | number | boolean | (() => void) };
2627
id?: string;
2728
toggleCellId?: string;
@@ -41,6 +42,7 @@ const TableRow = (props: TableRowPropTypes): React.ReactElement => {
4142
dark = false,
4243
dragId,
4344
draggableItem = false,
45+
headerStyle = "default",
4446
htmlOptions = {},
4547
id,
4648
toggleCellId,
@@ -60,6 +62,9 @@ const TableRow = (props: TableRowPropTypes): React.ReactElement => {
6062
const classes = classnames(
6163
buildCss("pb_table_row_kit", sideHighlightClass),
6264
"pb_table_tr",
65+
{
66+
"pb_table_tr_borderless_header": headerStyle === "borderless",
67+
},
6368
collapsibleRow,
6469
globalProps(props),
6570
className

Diff for: playbook/app/pb_kits/playbook/pb_table/table.test.js

+17
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,20 @@ test("Should have outerPadding class", () => {
167167
const kit = renderKit(Table, props, { outerPadding: "sm" })
168168
expect(kit).toHaveClass("pb_table table-sm table-responsive-collapse table-card outer_padding_space_sm table-collapse-sm")
169169
})
170+
171+
test("when headerStyle is default", () => {
172+
const kit = renderKit(Table, props)
173+
expect(kit).toHaveClass("pb_table table-sm table-responsive-collapse table-card table-collapse-sm")
174+
expect(kit).not.toHaveClass("header-borderless")
175+
expect(kit).not.toHaveClass("header-floating")
176+
})
177+
178+
test("when headerStyle is borderless", () => {
179+
const kit = renderKit(Table, props, { headerStyle: "borderless" })
180+
expect(kit).toHaveClass("pb_table table-sm table-responsive-collapse table-card header-borderless table-collapse-sm")
181+
})
182+
183+
test("when headerStyle is floating", () => {
184+
const kit = renderKit(Table, props, { headerStyle: "floating" })
185+
expect(kit).toHaveClass("pb_table table-sm table-responsive-collapse table-card header-floating table-collapse-sm")
186+
})

0 commit comments

Comments
 (0)