From 86b42b8166c68cb1d8818c02b31e3b79f2e4ca52 Mon Sep 17 00:00:00 2001 From: Ed Leeks Date: Thu, 1 Apr 2021 13:51:53 +0100 Subject: [PATCH] feat(flat-table-cell, flat-table-row-header): add width prop to table cell and new truncate styling Adds `width` prop for `FlatTableCell`. Adds `truncate` and `title` props to support styling content that overflows the width of the column and overriding the text displayed when the mouse hovers over a cell. --- .../__snapshots__/flat-table.spec.js.snap | 13 ++-- .../flat-table-cell.component.js | 25 +++++-- .../flat-table-cell/flat-table-cell.d.ts | 6 ++ .../flat-table-cell/flat-table-cell.spec.js | 60 ++++++++++++++++ .../flat-table-cell/flat-table-cell.style.js | 51 +++++++++---- .../flat-table-expandable.stories.mdx | 71 +++++++++++++++++++ .../flat-table-row-header.component.js | 20 ++++-- .../flat-table-row-header.d.ts | 4 ++ .../flat-table-row-header.spec.js | 34 +++++++++ .../flat-table-row-header.style.js | 23 +++++- .../__snapshots__/flat-table-row.spec.js.snap | 2 - .../flat-table/flat-table.stories.mdx | 33 ++++++++- 12 files changed, 302 insertions(+), 40 deletions(-) create mode 100644 src/components/flat-table/flat-table-cell/flat-table-cell.spec.js diff --git a/src/components/flat-table/__snapshots__/flat-table.spec.js.snap b/src/components/flat-table/__snapshots__/flat-table.spec.js.snap index 8a9a5a86a2..95cebc0173 100644 --- a/src/components/flat-table/__snapshots__/flat-table.spec.js.snap +++ b/src/components/flat-table/__snapshots__/flat-table.spec.js.snap @@ -24,7 +24,7 @@ exports[`FlatTable when rendered with proper table data should have expected str padding-left: 1px; } -.c9.c9.c9 > div { +.c10.c10.c10 > div { box-sizing: border-box; } @@ -32,14 +32,12 @@ exports[`FlatTable when rendered with proper table data should have expected str background-color: #fff; border-width: 0; border-bottom: 1px solid #CCD6DB; - text-overflow: ellipsis; text-align: left; vertical-align: middle; - white-space: nowrap; padding: 0; } -.c11.c11.c11 > div { +.c12.c12.c12 > div { box-sizing: border-box; } @@ -55,14 +53,12 @@ exports[`FlatTable when rendered with proper table data should have expected str background-color: #fff; border-width: 0; border-bottom: 1px solid #CCD6DB; - text-overflow: ellipsis; text-align: left; vertical-align: middle; - white-space: nowrap; padding: 0; } -.c12.c12.c12 > div { +.c13.c13.c13 > div { box-sizing: border-box; } @@ -90,11 +86,10 @@ exports[`FlatTable when rendered with proper table data should have expected str text-align: left; top: auto; vertical-align: middle; - white-space: nowrap; padding: 0; } -.c8 > div { +.c8.c8.c8 > div { box-sizing: border-box; padding-top: 10px; padding-bottom: 10px; diff --git a/src/components/flat-table/flat-table-cell/flat-table-cell.component.js b/src/components/flat-table/flat-table-cell/flat-table-cell.component.js index f68dbd755f..c6a003a40a 100644 --- a/src/components/flat-table/flat-table-cell/flat-table-cell.component.js +++ b/src/components/flat-table/flat-table-cell/flat-table-cell.component.js @@ -9,7 +9,7 @@ import { import Icon from "../../icon"; const FlatTableCell = ({ - align, + align = "left", children, colspan, rowspan, @@ -21,6 +21,9 @@ const FlatTableCell = ({ reportCellWidth, cellIndex, leftPosition, + width, + truncate = false, + title, ...rest }) => { const ref = useRef(null); @@ -45,9 +48,17 @@ const FlatTableCell = ({ onClick={expandable && onClick ? onClick : undefined} tabIndex={expandable && onClick ? 0 : undefined} onKeyDown={expandable && onKeyDown ? onKeyDown : undefined} + colWidth={width} + isTruncated={truncate} + expandable={expandable} {...rest} > - + {expandable && } {children} @@ -65,6 +76,12 @@ FlatTableCell.propTypes = { colspan: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), /** Number of rows that a cell should span */ rowspan: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + /** Column width, pass a number to set a fixed width in pixels */ + width: PropTypes.number, + /** Truncate cell content and add ellipsis to any text that overflows */ + truncate: PropTypes.bool, + /** Title text to display if cell content truncates */ + title: PropTypes.string, /** * @private * @ignore @@ -100,8 +117,4 @@ FlatTableCell.propTypes = { reportCellWidth: PropTypes.func, }; -FlatTableCell.defaultProps = { - align: "left", -}; - export default FlatTableCell; diff --git a/src/components/flat-table/flat-table-cell/flat-table-cell.d.ts b/src/components/flat-table/flat-table-cell/flat-table-cell.d.ts index fdbd841a64..7aa8473ee1 100644 --- a/src/components/flat-table/flat-table-cell/flat-table-cell.d.ts +++ b/src/components/flat-table/flat-table-cell/flat-table-cell.d.ts @@ -9,6 +9,12 @@ export interface FlatTableCellProps extends SpacingProps { colspan?: number | string; /** Number of rows that a cell should span */ rowspan?: number | string; + /** Column width, pass a number to set a fixed width in pixels */ + width?: number; + /** Truncate cell content and add ellipsis to any text that overflows */ + truncate?: boolean; + /** Title text to display if cell content truncates */ + title?: string; } declare const FlatTableCell: React.FunctionComponent; diff --git a/src/components/flat-table/flat-table-cell/flat-table-cell.spec.js b/src/components/flat-table/flat-table-cell/flat-table-cell.spec.js new file mode 100644 index 0000000000..c105c94f78 --- /dev/null +++ b/src/components/flat-table/flat-table-cell/flat-table-cell.spec.js @@ -0,0 +1,60 @@ +import React from "react"; +import { mount } from "enzyme"; + +import { StyledFlatTableCell } from "./flat-table-cell.style"; +import FlatTableRowCell from "./flat-table-cell.component"; +import { assertStyleMatch } from "../../../__spec_helper__/test-utils"; + +describe("FlatTableRowCell", () => { + it("renders with proper width style rule when width prop is passed", () => { + const wrapper = mount(); + assertStyleMatch( + { + width: "40px", + }, + wrapper.find(StyledFlatTableCell) + ); + + assertStyleMatch( + { + width: "40px", + }, + wrapper.find(StyledFlatTableCell), + { modifier: "&&& > div" } + ); + }); + + describe("when truncate prop is true", () => { + let wrapper; + beforeEach(() => { + wrapper = mount(Foo); + }); + + it("should apply expected styling", () => { + assertStyleMatch( + { + textOverflow: "ellipsis", + overflow: "hidden", + whiteSpace: "nowrap", + }, + wrapper.find(StyledFlatTableCell), + { modifier: "&&& > div" } + ); + }); + + it("should set the title if children is string", () => { + expect(wrapper.find("div").props().title).toEqual("Foo"); + }); + + describe("and title prop is set", () => { + it("should override the default behaviour", () => { + wrapper = mount( + + Foo + + ); + expect(wrapper.find("div").props().title).toEqual("Bar"); + }); + }); + }); +}); diff --git a/src/components/flat-table/flat-table-cell/flat-table-cell.style.js b/src/components/flat-table/flat-table-cell/flat-table-cell.style.js index ad92c67489..e54af450fd 100644 --- a/src/components/flat-table/flat-table-cell/flat-table-cell.style.js +++ b/src/components/flat-table/flat-table-cell/flat-table-cell.style.js @@ -4,19 +4,44 @@ import { space } from "styled-system"; import baseTheme from "../../../style/themes/base"; const StyledFlatTableCell = styled.td` - ${({ align, theme, rowSpan, leftPosition, makeCellSticky }) => css` + ${({ + align, + theme, + rowSpan, + leftPosition, + makeCellSticky, + colWidth, + isTruncated, + expandable, + }) => css` background-color: #fff; border-width: 0; border-bottom: 1px solid ${theme.table.secondary}; - text-overflow: ellipsis; text-align: ${align}; vertical-align: middle; - white-space: nowrap; padding: 0; + ${colWidth && + css` + width: ${colWidth}px; + `} + &&& { > div { box-sizing: border-box; + + ${isTruncated && + css` + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + `} + + ${colWidth && + css` + width: ${colWidth}px; + `} + ${space} } } @@ -42,19 +67,21 @@ const StyledFlatTableCell = styled.td` left: ${leftPosition}px; position: sticky; `} + + ${expandable && + css` + white-space: nowrap; + `} `} `; const StyledCellContent = styled.div` - ${({ expandable }) => css` - { - ${expandable && - css` - display: flex; - align-items: center; - `} - } - `} + ${({ expandable }) => + expandable && + css` + display: flex; + align-items: center; + `} `; StyledFlatTableCell.defaultProps = { diff --git a/src/components/flat-table/flat-table-expandable.stories.mdx b/src/components/flat-table/flat-table-expandable.stories.mdx index db7b21d736..b3d0584eab 100644 --- a/src/components/flat-table/flat-table-expandable.stories.mdx +++ b/src/components/flat-table/flat-table-expandable.stories.mdx @@ -1,5 +1,6 @@ import { Meta, Props, Preview, Story } from "@storybook/addon-docs/blocks"; import { useState } from "react"; +import styled from "styled-components"; import { FlatTable, FlatTableHead, @@ -969,6 +970,76 @@ By writing your own selectable logic, the child rows only can be made selectable +### Truncated cell content +In order to achieve the content truncation when using the `expandable` feature you will need to pass in a node with the +truncated styling applied, this is because of the caret icon that is rendered within the cell. + + + + {() => { + const SubRows = [ + + Child one + York + Single + 2 + , + + Child two + Edinburgh + Single + 1 + , + ]; + const Truncate = styled.span` + box-sizing: border-box; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + width: 48px; + `; + return ( + + + + Name + Location + Relationship Status + Dependents + + + + + John Doe + London + Single + 0 + + + Jane Doe + York + Married + 2 + + + John Smith + Edinburgh + Single + 1 + + + Jane Smith + Newcastle + Married + 5 + + + + ); + }} + + + ## Props ### FlatTableRow diff --git a/src/components/flat-table/flat-table-row-header/flat-table-row-header.component.js b/src/components/flat-table/flat-table-row-header/flat-table-row-header.component.js index 4e81ca4b01..1caf675370 100644 --- a/src/components/flat-table/flat-table-row-header/flat-table-row-header.component.js +++ b/src/components/flat-table/flat-table-row-header/flat-table-row-header.component.js @@ -6,7 +6,7 @@ import StyledFlatTableRowHeader from "./flat-table-row-header.style"; import Icon from "../../icon"; const FlatTableRowHeader = ({ - align, + align = "left", children, width, py, @@ -15,6 +15,8 @@ const FlatTableRowHeader = ({ onClick, onKeyDown, leftPosition, + truncate, + title, ...rest }) => { return ( @@ -28,9 +30,15 @@ const FlatTableRowHeader = ({ onClick={expandable && onClick ? onClick : undefined} tabIndex={expandable && onClick ? 0 : undefined} onKeyDown={expandable && onKeyDown ? onKeyDown : undefined} + isTruncated={truncate} + expandable={expandable} {...rest} > -
+
{expandable && } {children}
@@ -46,6 +54,10 @@ FlatTableRowHeader.propTypes = { children: PropTypes.oneOfType([PropTypes.node, PropTypes.string]), /** Column width, pass a number to set a fixed width in pixels */ width: PropTypes.number, + /** Truncate cell content and add ellipsis to any text that overflows */ + truncate: PropTypes.bool, + /** Title text to display if cell content truncates */ + title: PropTypes.string, /** * @private * @ignore @@ -63,8 +75,4 @@ FlatTableRowHeader.propTypes = { onKeyDown: PropTypes.func, }; -FlatTableRowHeader.defaultProps = { - align: "left", -}; - export default FlatTableRowHeader; diff --git a/src/components/flat-table/flat-table-row-header/flat-table-row-header.d.ts b/src/components/flat-table/flat-table-row-header/flat-table-row-header.d.ts index 6e1b2b1351..2bd86cab5a 100644 --- a/src/components/flat-table/flat-table-row-header/flat-table-row-header.d.ts +++ b/src/components/flat-table/flat-table-row-header/flat-table-row-header.d.ts @@ -7,6 +7,10 @@ export interface FlatTableRowHeaderProps extends SpacingProps { children?: React.ReactNode | string; /** Column width, pass a number to set a fixed width in pixels */ width?: number; + /** Truncate cell content and add ellipsis to any text that overflows */ + truncate?: boolean; + /** Title text to display if cell content truncates */ + title?: string; } declare const FlatTableRowHeader: React.FunctionComponent; diff --git a/src/components/flat-table/flat-table-row-header/flat-table-row-header.spec.js b/src/components/flat-table/flat-table-row-header/flat-table-row-header.spec.js index fa6c424e41..21c66e2cd2 100644 --- a/src/components/flat-table/flat-table-row-header/flat-table-row-header.spec.js +++ b/src/components/flat-table/flat-table-row-header/flat-table-row-header.spec.js @@ -68,4 +68,38 @@ describe("FlatTableRowHeader", () => { }); }); }); + + describe("when truncate prop is true", () => { + let wrapper; + beforeEach(() => { + wrapper = mount(Foo); + }); + + it("should apply expected styling", () => { + assertStyleMatch( + { + textOverflow: "ellipsis", + overflow: "hidden", + whiteSpace: "nowrap", + }, + wrapper.find(StyledFlatTableRowHeader), + { modifier: "&&& > div" } + ); + }); + + it("should set the title if children is string", () => { + expect(wrapper.find("div").props().title).toEqual("Foo"); + }); + + describe("and title prop is set", () => { + it("should override the default behaviour", () => { + wrapper = mount( + + Foo + + ); + expect(wrapper.find("div").props().title).toEqual("Bar"); + }); + }); + }); }); diff --git a/src/components/flat-table/flat-table-row-header/flat-table-row-header.style.js b/src/components/flat-table/flat-table-row-header/flat-table-row-header.style.js index 6337f8c082..e03339e106 100644 --- a/src/components/flat-table/flat-table-row-header/flat-table-row-header.style.js +++ b/src/components/flat-table/flat-table-row-header/flat-table-row-header.style.js @@ -4,7 +4,7 @@ import { space } from "styled-system"; import baseTheme from "../../../style/themes/base"; const StyledFlatTableRowHeader = styled.th` - ${({ align, theme, colWidth, leftPosition }) => css` + ${({ align, theme, colWidth, leftPosition, isTruncated, expandable }) => css` background-color: #fff; border: 1px solid ${theme.table.secondary}; border-top: none; @@ -15,8 +15,8 @@ const StyledFlatTableRowHeader = styled.th` text-align: ${align}; top: auto; vertical-align: middle; - white-space: nowrap; padding: 0; + ${colWidth && css` width: ${colWidth}px; @@ -25,14 +25,31 @@ const StyledFlatTableRowHeader = styled.th` &&& { > div { box-sizing: border-box; - + + ${isTruncated && + css` + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + `} + ${colWidth && css` width: ${colWidth}px; `} + ${space} } } + + &&& { + left: ${leftPosition}px; + } + + ${expandable && + css` + white-space: nowrap; + `} `} `; diff --git a/src/components/flat-table/flat-table-row/__snapshots__/flat-table-row.spec.js.snap b/src/components/flat-table/flat-table-row/__snapshots__/flat-table-row.spec.js.snap index 2e6ee9c254..a37137fa80 100644 --- a/src/components/flat-table/flat-table-row/__snapshots__/flat-table-row.spec.js.snap +++ b/src/components/flat-table/flat-table-row/__snapshots__/flat-table-row.spec.js.snap @@ -5,10 +5,8 @@ exports[`FlatTableRow should have expected styles 1`] = ` background-color: #fff; border-width: 0; border-bottom: 1px solid #CCD6DB; - text-overflow: ellipsis; text-align: left; vertical-align: middle; - white-space: nowrap; padding: 0; } diff --git a/src/components/flat-table/flat-table.stories.mdx b/src/components/flat-table/flat-table.stories.mdx index f73ca00d43..8b1d77e299 100644 --- a/src/components/flat-table/flat-table.stories.mdx +++ b/src/components/flat-table/flat-table.stories.mdx @@ -173,7 +173,7 @@ Padding can be set on each cell individually by using the spacing props syntax. ### With custom column width -Column width can be set by passing number as a `width` prop to the column header component. +Column width can be set by passing number as a `width` prop to the column header or cell component. **Column width can't be smaller than the sum of horizontal cell padding and content width.** @@ -181,7 +181,7 @@ Column width can be set by passing number as a `width` prop to the column header - Name + Name Location Notes @@ -208,6 +208,35 @@ Column width can be set by passing number as a `width` prop to the column header +### With truncated cell content +When setting column widths it is also possible to set the content to `truncate`: this will prevent the content from wrapping +and add ellipsis to any content that overflows the given width. By default the `title` will be set to the children if it +is a string. However, if the passed children are not a string it is possible to override it by passing in a different +`title` string. + + + + + + + Name + Location + Notes + + + + {[1,2,3,4].map((key) => + + John Doe + London + + + )} + + + + + ### With stickyHead prop set to true