Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Table): Change column width to match style guidelines #850

Merged
merged 3 commits into from
Nov 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 81 additions & 31 deletions src/Table/elements.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import styled, { css } from 'styled-components';
import { transparentize } from 'polished';
import breakpoint from 'styled-components-breakpoint';

const borderRight = height => css`
&:after {
Expand All @@ -18,10 +20,8 @@ const borderRight = height => css`

// Table
export const TableElement = styled.table`
min-width: 100%;

border-collapse: collapse;
table-layout: fixed;
width: 100%;
border-spacing: 0;
position: relative;
`;

Expand Down Expand Up @@ -59,32 +59,25 @@ export const TableHeader = styled.thead`
export const TableHeaderCell = styled.th`
position: relative;
height: 4.4rem;
padding: 0 0.5rem;

padding: 0 1.2rem;
box-sizing: border-box;
vertical-align: middle;
border-bottom: 1px solid ${({ theme: { palette } }) => palette.veryLightBlue};
background-color: ${({ theme: { palette } }) => palette.white};

white-space: nowrap;
text-align: ${({ align }) => align || 'left'};

&:before {
content: '';
position: absolute;
right: 0;
bottom: 0;
width: 100%;
border-bottom: 1px solid ${({ theme: { palette } }) => palette.veryLightBlue};
}

&:first-child {
padding-left: 2rem;
padding-right: 0.5rem;
padding-right: 1.2rem;
z-index: 2;
left: 0;
${({ isScrollable }) => isScrollable && borderRight('4.4rem')}
}

&:last-child {
padding-right: 2.2rem;
padding-left: 0.5rem;
padding-right: 2rem;
padding-left: 1.2rem;
}

${({ isScrollable }) =>
Expand All @@ -109,14 +102,28 @@ export const HeaderLabel = styled.span`

// Table Body
export const Body = styled.tbody`
tr:not(:last-child) {
td {
border-bottom: 1px solid ${({ theme: { palette } }) => palette.veryLightBlue};
}
th {
border-bottom: 1px solid ${({ theme: { palette } }) => palette.veryLightBlue};
}
}
td {
height: 5.2rem;

padding: 0 0.5rem;
padding: 0 1.2rem;
white-space: nowrap;
vertical-align: middle;

font-feature-settings: 'tnum';

min-width: 10rem;
${({ colsLength }) =>
colsLength &&
css`
width: calc(100% / ${colsLength});
`};
box-sizing: border-box;

&:first-child {
padding-left: 2rem;
Expand All @@ -134,7 +141,6 @@ export const ChildRow = styled(Row)`
(hasChildren || selectable) &&
css`
cursor: pointer;
}
`};

${({ striped }) =>
Expand Down Expand Up @@ -174,8 +180,16 @@ export const BodyRow = styled(ChildRow)`

export const RowHeader = styled.th`
height: 5.2rem;
display: flex;
align-items: center;

/* Avoid cell to grow when the cell's text overflow */
max-width: 30rem;
min-width: 20rem;
${breakpoint('xs', 'sm')`
max-width: 50vw;
min-width: 0;
`};
white-space: nowrap;
box-sizing: border-box;

${({ isScrollable }) =>
isScrollable &&
Expand All @@ -190,19 +204,25 @@ export const RowHeader = styled.th`
white-space: nowrap;
overflow: hidden;

padding: 0 0.5rem 0 2rem;
padding: 0 1.2rem 0 2rem;

${({ isChild }) =>
isChild &&
css`
padding-left: 4rem;
`}

max-width: 30rem;
min-width: 15rem;
background-color: ${({ theme: { palette } }) => palette.white};
`;

export const TextEllipsis = styled.div`
display: inline-block;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
max-width: 100%;
`;

// Table Footer
export const Footer = styled.tfoot`
position: sticky;
Expand All @@ -212,6 +232,7 @@ export const Footer = styled.tfoot`

td {
font-feature-settings: 'tnum';
box-sizing: border-box;
}

th,
Expand All @@ -235,7 +256,7 @@ export const Footer = styled.tfoot`

height: 5.2rem;

padding: 0 0.5rem;
padding: 0 1.2rem;
background-color: ${({ theme: { palette } }) => palette.white};

color: ${({ theme: { palette } }) => palette.primary.default};
Expand All @@ -251,11 +272,11 @@ export const Footer = styled.tfoot`
${borderRight('5.2rem')}
`}

padding: 0 0.5rem 0 2rem;
padding: 0 1.2rem 0 2rem;
}

td:last-of-type {
padding: 0 3rem 0 0.5rem;
padding: 0 2rem 0 1.2rem;
}

${({ isHoverable }) =>
Expand All @@ -280,3 +301,32 @@ export const Container = styled.div`
`}
position: relative;
`;

// Shadow Container
export const ShadowContainer = styled.div`
pointer-events: none;
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: ${({ firstCellWidth }) => firstCellWidth && `${firstCellWidth}px`};
z-index: 10;
box-shadow: ${({ side, theme: { palette } }) => {
const shadowColor = transparentize(0.9, palette.black);
if (side === 'left') {
return `inset -8px 0 6px -6px ${shadowColor};`;
} else if (side === 'right') {
return `inset 8px 0 6px -6px ${shadowColor};`;
} else if (side === 'both') {
return `inset -8px 0 6px -6px ${shadowColor}, inset 8px 0 6px -6px ${shadowColor};`;
}
return null;
}}
background-repeat: no-repeat;
background-size: 10px 100%;
`;

// Shadow Container
export const ShadowWrapped = styled.div`
position: relative;
`;
109 changes: 97 additions & 12 deletions src/Table/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Fragment, PureComponent } from 'react';
import React, { Fragment, PureComponent, createRef } from 'react';
import PropTypes from 'prop-types';

import { Icon, Theme } from '..';
Expand All @@ -15,6 +15,9 @@ import {
RowHeader,
Footer,
ChildRow,
ShadowContainer,
TextEllipsis,
ShadowWrapped,
} from './elements';

/** Lookup object for next sorting direction. */
Expand Down Expand Up @@ -51,6 +54,59 @@ class Table extends PureComponent {
// Use `-1` for no row selected.
selected: -1,
selectedRows: [],
firstCellWidth: 0,
shadowSide: null,
};

rowHeaderRef = createRef();
containerRef = createRef();
bodyRef = createRef();

componentDidMount() {
const { isScrollable } = this.props;
if (isScrollable) {
this.setFirstCellWidth();
addEventListener('resize', this.onResize);
this.containerRef.current.addEventListener('scroll', this.onScroll);
}
}

componentWillUnmount() {
removeEventListener('resize', this.onResize);
this.containerRef.current.removeEventListener('scroll', this.onScroll);
}

onResize = () => {
this.setFirstCellWidth();
this.setContainerShadow();
};

onScroll = () => {
this.setContainerShadow();
};

setFirstCellWidth = () => {
const firstCellWidth =
this.rowHeaderRef && this.rowHeaderRef.current && this.rowHeaderRef.current.offsetWidth;
this.setState({ firstCellWidth });
};

setContainerShadow = () => {
const containerRefRect = this.containerRef.current.getBoundingClientRect();
const bodyRefRect = this.bodyRef.current.getBoundingClientRect();

let shadowSide = 'both';
if (
containerRefRect.left === bodyRefRect.left &&
containerRefRect.right === bodyRefRect.right
) {
shadowSide = null;
} else if (containerRefRect.left === bodyRefRect.left) {
shadowSide = 'left';
} else if (containerRefRect.right === bodyRefRect.right) {
shadowSide = 'right';
}
this.setState({ shadowSide });
};

/**
Expand Down Expand Up @@ -193,8 +249,14 @@ class Table extends PureComponent {
return sortedData;
};

// Rule:
// first cell count 2 fractions of the table
// normal cell count 1 fractions of the table
// To calculate the cell width we need to know the column's number and add it one to take care of the first cell which take 2 fractions.
const colsLength = colsDef.length + 1;

return (
<Body>
<Body colsLength={colsLength} ref={this.bodyRef}>
{sortData(data).map(({ key, item }, index) => (
<Fragment key={key}>
<BodyRow
Expand All @@ -209,7 +271,12 @@ class Table extends PureComponent {
>
{colsDef.map(({ isRowHeader, value, format, align }, columnIndex) =>
isRowHeader ? (
<RowHeader align={align} isScrollable={isScrollable} key={`row-header-${index}`}>
<RowHeader
ref={this.rowHeaderRef}
align={align}
isScrollable={isScrollable}
key={`row-header-${index}`}
>
{item.children && (
<Icon
name={selectedRows.includes(key) ? 'chevron-down' : 'chevron-right'}
Expand All @@ -218,7 +285,7 @@ class Table extends PureComponent {
height="20px"
/>
)}
{value(item, index)}
<TextEllipsis>{value(item, index)}</TextEllipsis>
</RowHeader>
) : (
<td key={`row-${index}-column-${columnIndex}`} align={align}>
Expand Down Expand Up @@ -248,7 +315,7 @@ class Table extends PureComponent {
key={`row-header-${key}-${index}`}
isChild
>
{value(childrenItem, index)}
<TextEllipsis>{value(childrenItem, index)}</TextEllipsis>
</RowHeader>
) : (
<td key={`row-${index}-column-${key}-${columnIndex}`} align={align}>
Expand Down Expand Up @@ -295,6 +362,16 @@ class Table extends PureComponent {
);
}

/**
* Renders the shadow of the table.
*
* @return {jsx}
*/
renderShadow() {
const { firstCellWidth, shadowSide } = this.state;
return <ShadowContainer side={shadowSide} firstCellWidth={firstCellWidth} />;
}

/**
* Renders the component.
*
Expand All @@ -304,13 +381,21 @@ class Table extends PureComponent {
const { dataTotal, height, isScrollable, width } = this.props;

return (
<Container data-testid="table-container" height={height} isScrollable={isScrollable}>
<TableElement width={isScrollable ? 'initial' : width}>
{this.renderHeader()}
{this.renderBody()}
{dataTotal && this.renderFooter()}
</TableElement>
</Container>
<ShadowWrapped>
{this.renderShadow()}
<Container
ref={this.containerRef}
data-testid="table-container"
height={height}
isScrollable={isScrollable}
>
<TableElement width={isScrollable ? 'initial' : width}>
{this.renderHeader()}
{this.renderBody()}
{dataTotal && this.renderFooter()}
</TableElement>
</Container>
</ShadowWrapped>
);
}
}
Expand Down