Skip to content

Commit 7616241

Browse files
feat(tables): add isSticky prop to Head component (#1482)
Co-authored-by: Jonathan Zempel <jzempel@gmail.com>
1 parent 5157937 commit 7616241

File tree

8 files changed

+93
-16
lines changed

8 files changed

+93
-16
lines changed
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
{
22
"index.esm.js": {
3-
"bundled": 24206,
4-
"minified": 17389,
5-
"gzipped": 4200,
3+
"bundled": 24607,
4+
"minified": 17705,
5+
"gzipped": 4285,
66
"treeshaked": {
77
"rollup": {
8-
"code": 13412,
8+
"code": 13681,
99
"import_statements": 384
1010
},
1111
"webpack": {
12-
"code": 14947
12+
"code": 15238
1313
}
1414
}
1515
},
1616
"index.cjs.js": {
17-
"bundled": 27224,
18-
"minified": 19966,
19-
"gzipped": 4518
17+
"bundled": 27645,
18+
"minified": 20302,
19+
"gzipped": 4604
2020
}
2121
}

packages/tables/demo/stories/TableStory.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
Row,
2222
SortableCell,
2323
ITableProps,
24+
IHeadProps,
2425
ISortableCellProps,
2526
IRowProps
2627
} from '@zendeskgarden/react-tables';
@@ -36,6 +37,7 @@ interface IArgs extends ITableProps {
3637
isSortable: boolean;
3738
isSelected?: IRowProps['isSelected'];
3839
isStriped?: IRowProps['isStriped'];
40+
isSticky?: IHeadProps['isSticky'];
3941
isTruncated?: boolean;
4042
}
4143

@@ -49,6 +51,7 @@ export const TableStory: Story<IArgs> = ({
4951
isSortable,
5052
isSelected,
5153
isStriped,
54+
isSticky,
5255
isTruncated,
5356
...args
5457
}) => {
@@ -71,7 +74,7 @@ export const TableStory: Story<IArgs> = ({
7174
return (
7275
<Table {...args}>
7376
<Caption>{caption}</Caption>
74-
<Head>
77+
<Head isSticky={isSticky}>
7578
<HeaderRow>
7679
{hasSelection && (
7780
<HeaderCell isMinimum>

packages/tables/demo/table.stories.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import { TABLE_DATA as DATA } from './stories/data';
4848
isTruncated: { control: 'boolean', table: { category: 'Cell' } },
4949
caption: { name: 'children', table: { category: 'Caption' } },
5050
isBold: { control: 'boolean', table: { category: 'GroupRow' } },
51+
isSticky: { control: 'boolean', table: { category: 'Head' } },
5152
isStriped: { control: 'boolean', table: { category: 'Row' } },
5253
isSelected: { control: 'boolean', table: { category: 'Row' } },
5354
data: { name: 'Row[]', table: { category: 'Story' } },

packages/tables/src/elements/Head.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55
* found at http://www.apache.org/licenses/LICENSE-2.0.
66
*/
77

8-
import React, { HTMLAttributes, forwardRef } from 'react';
8+
import React, { forwardRef } from 'react';
99
import { StyledHead } from '../styled';
10+
import { IHeadProps } from '../types';
1011

1112
/**
1213
* @extends HTMLAttributes<HTMLTableSectionElement>
1314
*/
14-
export const Head = forwardRef<HTMLTableSectionElement, HTMLAttributes<HTMLTableSectionElement>>(
15-
(props, ref) => <StyledHead ref={ref} {...props} />
16-
);
15+
export const Head = forwardRef<HTMLTableSectionElement, IHeadProps>((props, ref) => (
16+
<StyledHead ref={ref} {...props} />
17+
));
1718

1819
Head.displayName = 'Head';

packages/tables/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export type {
2121
ITableProps,
2222
IRowProps,
2323
ICellProps,
24+
IHeadProps,
2425
IHeaderCellProps,
2526
ISortableCellProps
2627
} from './types';
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Copyright Zendesk, Inc.
3+
*
4+
* Use of this source code is governed under the Apache License, Version 2.0
5+
* found at http://www.apache.org/licenses/LICENSE-2.0.
6+
*/
7+
8+
import React from 'react';
9+
import { render } from 'garden-test-utils';
10+
11+
import { StyledHead } from './StyledHead';
12+
import { StyledHeaderRow } from './StyledHeaderRow';
13+
14+
describe('StyledHead', () => {
15+
it('renders the expected element', () => {
16+
const { container } = render(
17+
<table>
18+
<StyledHead />
19+
</table>
20+
);
21+
22+
expect(container.firstChild!.childNodes[0].nodeName).toBe('THEAD');
23+
});
24+
25+
it('renders sticky head styles', () => {
26+
const { getByTestId } = render(
27+
<table>
28+
<StyledHead isSticky data-test-id="head">
29+
<StyledHeaderRow data-test-id="header" />
30+
</StyledHead>
31+
</table>
32+
);
33+
34+
expect(getByTestId('head')).toHaveStyleRule('position', 'sticky');
35+
expect(getByTestId('head')).toHaveStyleRule('border-bottom-color', 'transparent', {
36+
modifier: `& > ${StyledHeaderRow}:last-child`
37+
});
38+
});
39+
});

packages/tables/src/styled/StyledHead.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,42 @@
55
* found at http://www.apache.org/licenses/LICENSE-2.0.
66
*/
77

8-
import styled from 'styled-components';
9-
import { retrieveComponentStyles, DEFAULT_THEME } from '@zendeskgarden/react-theming';
8+
import styled, { css, DefaultTheme, ThemeProps } from 'styled-components';
9+
import { retrieveComponentStyles, DEFAULT_THEME, getColor } from '@zendeskgarden/react-theming';
10+
import { StyledHeaderRow } from './StyledHeaderRow';
1011

1112
const COMPONENT_ID = 'tables.head';
1213

14+
interface IStyledHeadProps {
15+
isSticky?: boolean;
16+
}
17+
18+
/*
19+
* 1. Prevent <Checkbox> or <OverflowButton> from leaking over the sticky header
20+
* 2. Replace header row border with a box-shadow that maintains position
21+
*/
22+
const stickyStyles = (props: ThemeProps<DefaultTheme>) => {
23+
const borderColor = getColor('neutralHue', 300, props.theme);
24+
25+
return css`
26+
position: sticky;
27+
top: 0;
28+
z-index: 1; /* [1] */
29+
box-shadow: inset 0 -${props.theme.borderWidths.sm} 0 ${borderColor}; /* [2] */
30+
background-color: ${props.theme.colors.background};
31+
32+
& > ${StyledHeaderRow}:last-child {
33+
border-bottom-color: transparent; /* [2] */
34+
}
35+
`;
36+
};
37+
1338
export const StyledHead = styled.thead.attrs({
1439
'data-garden-id': COMPONENT_ID,
1540
'data-garden-version': PACKAGE_VERSION
16-
})`
41+
})<IStyledHeadProps>`
42+
${props => props.isSticky && stickyStyles(props)}
43+
1744
${props => retrieveComponentStyles(COMPONENT_ID, props)};
1845
`;
1946

packages/tables/src/types/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ export interface ITableProps extends TableHTMLAttributes<HTMLTableElement> {
2424
isReadOnly?: boolean;
2525
}
2626

27+
export interface IHeadProps extends HTMLAttributes<HTMLTableSectionElement> {
28+
/** Applies sticky header styling */
29+
isSticky?: boolean;
30+
}
31+
2732
export interface IRowProps extends HTMLAttributes<HTMLTableRowElement> {
2833
/** Applies striped styling */
2934
isStriped?: boolean;

0 commit comments

Comments
 (0)