From 14786d7a1ad9a6957cf76818b0832af385cfd169 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Fri, 29 Nov 2019 15:57:36 +0100 Subject: [PATCH 01/12] create sortable table --- scm-ui/ui-components/src/table/Column.tsx | 12 ++++ .../ui-components/src/table/Table.stories.tsx | 19 ++++++ scm-ui/ui-components/src/table/Table.tsx | 64 +++++++++++++++++++ scm-ui/ui-components/src/table/TextColumn.tsx | 26 ++++++++ scm-ui/ui-components/src/table/types.ts | 9 +++ 5 files changed, 130 insertions(+) create mode 100644 scm-ui/ui-components/src/table/Column.tsx create mode 100644 scm-ui/ui-components/src/table/Table.stories.tsx create mode 100644 scm-ui/ui-components/src/table/Table.tsx create mode 100644 scm-ui/ui-components/src/table/TextColumn.tsx create mode 100644 scm-ui/ui-components/src/table/types.ts diff --git a/scm-ui/ui-components/src/table/Column.tsx b/scm-ui/ui-components/src/table/Column.tsx new file mode 100644 index 0000000000..e1e7a33156 --- /dev/null +++ b/scm-ui/ui-components/src/table/Column.tsx @@ -0,0 +1,12 @@ +import React, { FC, ReactNode } from "react"; +import { ColumnProps } from "./types"; + +type Props = ColumnProps & { + children: (row: any) => ReactNode; +}; + +const Column: FC = ({ row, children }) => { + return <>{children(row)}; +}; + +export default Column; diff --git a/scm-ui/ui-components/src/table/Table.stories.tsx b/scm-ui/ui-components/src/table/Table.stories.tsx new file mode 100644 index 0000000000..dafc60f5b6 --- /dev/null +++ b/scm-ui/ui-components/src/table/Table.stories.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import { storiesOf } from "@storybook/react"; +import Table from "./Table"; +import Column from "./Column"; +import TextColumn from "./TextColumn"; + +storiesOf("Table|Table", module) + .add("Default", () => ( + + {(row: any) =>

{row.first}

}
+ {(row: any) =>

{row.second}

}
+
+ )) + .add("TextColumn", () => ( + + + +
+ )); diff --git a/scm-ui/ui-components/src/table/Table.tsx b/scm-ui/ui-components/src/table/Table.tsx new file mode 100644 index 0000000000..766ebf9317 --- /dev/null +++ b/scm-ui/ui-components/src/table/Table.tsx @@ -0,0 +1,64 @@ +import React, { FC, useState } from "react"; + +type SortableTableProps = { + data: any[]; +}; + +// @ts-ignore +const Table: FC = ({ data, children }) => { + const [tableData, setTableData] = useState(data); + const [ascending, setAscending] = useState(true); + const [lastSortBy, setlastSortBy] = useState(0); + + // @ts-ignore + const sortFunctions = React.Children.map(children, child => child.props.createComparator(child.props)); + + const mapDataToColumns = (row: any) => { + return ( + + {React.Children.map(children, child => { + // @ts-ignore + return {React.cloneElement(child, { ...child.props, row })}; + })} + + ); + }; + + const sortDescending = (sortAscending: (a: any, b: any) => number) => { + return (a: any, b: any) => { + return sortAscending(a, b) * -1; + }; + }; + + const tableSort = (index: number) => { + const sortableData = [...tableData]; + let sortOrder = ascending; + if (lastSortBy !== index) { + setAscending(true); + sortOrder = true; + } + const sortFunction = sortOrder ? sortFunctions[index] : sortDescending(sortFunctions[index]); + sortableData.sort(sortFunction); + setTableData(sortableData); + setAscending(!sortOrder); + setlastSortBy(index); + }; + + return ( + tableData.length > 0 && ( + + + + {React.Children.map(children, (child, index) => ( + // @ts-ignore + + ))} + + + {tableData.map(mapDataToColumns)} +
tableSort(index)}>{child.props.header}
+ ) + ); +}; + +export default Table; diff --git a/scm-ui/ui-components/src/table/TextColumn.tsx b/scm-ui/ui-components/src/table/TextColumn.tsx new file mode 100644 index 0000000000..eeca01eea7 --- /dev/null +++ b/scm-ui/ui-components/src/table/TextColumn.tsx @@ -0,0 +1,26 @@ +import React, { FC } from "react"; +import {ColumnProps} from "./types"; + +type Props = ColumnProps & { + dataKey: string; +}; + +const TextColumn: FC = ({ row, dataKey }) => { + return row[dataKey]; +}; + +TextColumn.defaultProps = { + createComparator: (props: Props) => { + return (a: any, b: any) => { + if (a[props.dataKey] < b[props.dataKey]) { + return -1; + } else if (a[props.dataKey] > b[props.dataKey]) { + return 1; + } else { + return 0; + } + }; + } +}; + +export default TextColumn; diff --git a/scm-ui/ui-components/src/table/types.ts b/scm-ui/ui-components/src/table/types.ts new file mode 100644 index 0000000000..b05a402711 --- /dev/null +++ b/scm-ui/ui-components/src/table/types.ts @@ -0,0 +1,9 @@ +import { ReactNode } from "react"; + +export type Comparator = (a: any, b: any) => number; + +export type ColumnProps = { + header: ReactNode; + row?: any; + createComparator?: (props: any) => Comparator; +}; From dc169ecff9c937d53b14a61370bddf1195fbe285 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Fri, 29 Nov 2019 17:13:39 +0100 Subject: [PATCH 02/12] style table --- scm-ui/ui-components/src/table/Table.tsx | 39 ++++++++++++++++--- scm-ui/ui-components/src/table/TextColumn.tsx | 7 ++-- scm-ui/ui-components/src/table/types.ts | 6 +++ 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/scm-ui/ui-components/src/table/Table.tsx b/scm-ui/ui-components/src/table/Table.tsx index 766ebf9317..847fe05d05 100644 --- a/scm-ui/ui-components/src/table/Table.tsx +++ b/scm-ui/ui-components/src/table/Table.tsx @@ -1,4 +1,15 @@ import React, { FC, useState } from "react"; +import styled from "styled-components"; +import { SortTypes } from "./types"; +import Icon from "../Icon"; + +const StyledTable = styled.table.attrs(() => ({ + className: "table content is-hoverable" +}))``; + +const IconWithMarginLeft = styled(Icon)` + margin-left: 0.25em; +`; type SortableTableProps = { data: any[]; @@ -7,11 +18,14 @@ type SortableTableProps = { // @ts-ignore const Table: FC = ({ data, children }) => { const [tableData, setTableData] = useState(data); - const [ascending, setAscending] = useState(true); + const [ascending, setAscending] = useState(false); const [lastSortBy, setlastSortBy] = useState(0); // @ts-ignore - const sortFunctions = React.Children.map(children, child => child.props.createComparator(child.props)); + const sortFunctions = React.Children.map(children, child => + // @ts-ignore + child.props.createComparator ? child.props.createComparator(child.props) : undefined + ); const mapDataToColumns = (row: any) => { return ( @@ -46,19 +60,34 @@ const Table: FC = ({ data, children }) => { return ( tableData.length > 0 && ( - + {React.Children.map(children, (child, index) => ( // @ts-ignore - + ))} {tableData.map(mapDataToColumns)} -
tableSort(index)}>{child.props.header} tableSort(index) : undefined} + > + {child.props.header} + + {child.props.createComparator && renderSortIcon(child.props.sortType, ascending)} +
+ ) ); }; +const renderSortIcon = (contentType: string, ascending: boolean) => { + if (contentType === SortTypes.Text) { + return ; + } else { + return ; + } +}; + export default Table; diff --git a/scm-ui/ui-components/src/table/TextColumn.tsx b/scm-ui/ui-components/src/table/TextColumn.tsx index eeca01eea7..0ccc69be81 100644 --- a/scm-ui/ui-components/src/table/TextColumn.tsx +++ b/scm-ui/ui-components/src/table/TextColumn.tsx @@ -1,5 +1,5 @@ -import React, { FC } from "react"; -import {ColumnProps} from "./types"; +import React, {FC} from "react"; +import {ColumnProps, SortTypes} from "./types"; type Props = ColumnProps & { dataKey: string; @@ -20,7 +20,8 @@ TextColumn.defaultProps = { return 0; } }; - } + }, + sortType: SortTypes.Text }; export default TextColumn; diff --git a/scm-ui/ui-components/src/table/types.ts b/scm-ui/ui-components/src/table/types.ts index b05a402711..683566da6d 100644 --- a/scm-ui/ui-components/src/table/types.ts +++ b/scm-ui/ui-components/src/table/types.ts @@ -6,4 +6,10 @@ export type ColumnProps = { header: ReactNode; row?: any; createComparator?: (props: any) => Comparator; + sortType: SortTypes; }; + +export enum SortTypes { + Text = "text", + Other = "other" +} From 52057abef5008ef71fa6393a47c04b429383867a Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Mon, 2 Dec 2019 13:58:56 +0100 Subject: [PATCH 03/12] refactor table --- scm-ui/ui-components/src/table/Column.tsx | 12 +++- scm-ui/ui-components/src/table/SortIcon.tsx | 19 ++++++ .../ui-components/src/table/Table.stories.tsx | 20 +++++- scm-ui/ui-components/src/table/Table.tsx | 62 +++++++++++-------- scm-ui/ui-components/src/table/TextColumn.tsx | 9 +-- scm-ui/ui-components/src/table/types.ts | 11 ++-- 6 files changed, 92 insertions(+), 41 deletions(-) create mode 100644 scm-ui/ui-components/src/table/SortIcon.tsx diff --git a/scm-ui/ui-components/src/table/Column.tsx b/scm-ui/ui-components/src/table/Column.tsx index e1e7a33156..7f95535c36 100644 --- a/scm-ui/ui-components/src/table/Column.tsx +++ b/scm-ui/ui-components/src/table/Column.tsx @@ -2,11 +2,17 @@ import React, { FC, ReactNode } from "react"; import { ColumnProps } from "./types"; type Props = ColumnProps & { - children: (row: any) => ReactNode; + children: (row: any, columnIndex: number) => ReactNode; }; -const Column: FC = ({ row, children }) => { - return <>{children(row)}; +const Column: FC = ({ row, columnIndex, children }) => { + if (row === undefined) { + throw new Error("missing row, use column only as child of Table"); + } + if (columnIndex === undefined) { + throw new Error("missing row, use column only as child of Table"); + } + return <>{children(row, columnIndex)}; }; export default Column; diff --git a/scm-ui/ui-components/src/table/SortIcon.tsx b/scm-ui/ui-components/src/table/SortIcon.tsx new file mode 100644 index 0000000000..6f823d3d95 --- /dev/null +++ b/scm-ui/ui-components/src/table/SortIcon.tsx @@ -0,0 +1,19 @@ +import React, { FC } from "react"; +import styled from "styled-components"; +import Icon from "../Icon"; + +type Props = { + name: string; + isHidden: boolean; +}; + +const IconWithMarginLeft = styled(Icon)` + visibility: ${(props: Props) => (props.isHidden ? "hidden" : "visible")}; + margin-left: 0.25em; +`; + +const SortIcon: FC = (props: Props) => { + return ; +}; + +export default SortIcon; diff --git a/scm-ui/ui-components/src/table/Table.stories.tsx b/scm-ui/ui-components/src/table/Table.stories.tsx index dafc60f5b6..84b80f001f 100644 --- a/scm-ui/ui-components/src/table/Table.stories.tsx +++ b/scm-ui/ui-components/src/table/Table.stories.tsx @@ -3,12 +3,28 @@ import { storiesOf } from "@storybook/react"; import Table from "./Table"; import Column from "./Column"; import TextColumn from "./TextColumn"; +import { ColumnProps } from "./types"; storiesOf("Table|Table", module) .add("Default", () => ( - +
{(row: any) =>

{row.first}

}
- {(row: any) =>

{row.second}

}
+ { + return (a: any, b: any) => { + if (a.second > b.second) { + return -1; + } else if (a.second < b.second) { + return 1; + } else { + return 0; + } + }; + }} + > + {(row: any) =>

{row.second}

} +
)) .add("TextColumn", () => ( diff --git a/scm-ui/ui-components/src/table/Table.tsx b/scm-ui/ui-components/src/table/Table.tsx index 847fe05d05..a36481005e 100644 --- a/scm-ui/ui-components/src/table/Table.tsx +++ b/scm-ui/ui-components/src/table/Table.tsx @@ -1,38 +1,44 @@ import React, { FC, useState } from "react"; import styled from "styled-components"; -import { SortTypes } from "./types"; -import Icon from "../Icon"; +import { Comparator } from "./types"; +import SortIcon from "./SortIcon"; const StyledTable = styled.table.attrs(() => ({ className: "table content is-hoverable" }))``; -const IconWithMarginLeft = styled(Icon)` - margin-left: 0.25em; -`; - -type SortableTableProps = { +type Props = { data: any[]; + sortable?: boolean; }; // @ts-ignore -const Table: FC = ({ data, children }) => { +const Table: FC = ({ data, sortable, children }) => { const [tableData, setTableData] = useState(data); const [ascending, setAscending] = useState(false); - const [lastSortBy, setlastSortBy] = useState(0); + const [lastSortBy, setlastSortBy] = useState(); + const [hoveredIndex, setHoveredIndex] = useState(); - // @ts-ignore - const sortFunctions = React.Children.map(children, child => - // @ts-ignore - child.props.createComparator ? child.props.createComparator(child.props) : undefined - ); + const isSortable = (child: any) => { + return sortable && child.props.createComparator; + }; + + const sortFunctions: Comparator | undefined[] = []; + React.Children.forEach(children, (child, index) => { + if (isSortable(child)) { + // @ts-ignore + sortFunctions.push(child.props.createComparator(child.props, index)); + } else { + sortFunctions.push(undefined); + } + }); const mapDataToColumns = (row: any) => { return ( - {React.Children.map(children, child => { + {React.Children.map(children, (child, columnIndex) => { // @ts-ignore - return {React.cloneElement(child, { ...child.props, row })}; + return {React.cloneElement(child, { ...child.props, columnIndex, row })}; })} ); @@ -51,6 +57,7 @@ const Table: FC = ({ data, children }) => { setAscending(true); sortOrder = true; } + // @ts-ignore const sortFunction = sortOrder ? sortFunctions[index] : sortDescending(sortFunctions[index]); sortableData.sort(sortFunction); setTableData(sortableData); @@ -58,20 +65,21 @@ const Table: FC = ({ data, children }) => { setlastSortBy(index); }; + // @ts-ignore return ( tableData.length > 0 && ( {React.Children.map(children, (child, index) => ( - // @ts-ignore tableSort(index) : undefined} + className={isSortable(child) && "has-cursor-pointer"} + onClick={isSortable(child) ? () => tableSort(index) : undefined} + onMouseEnter={() => setHoveredIndex(index)} + onMouseLeave={() => setHoveredIndex(undefined)} > {child.props.header} - - {child.props.createComparator && renderSortIcon(child.props.sortType, ascending)} + {isSortable(child) && renderSortIcon(child, ascending, index === lastSortBy || index === hoveredIndex)} ))} @@ -82,11 +90,15 @@ const Table: FC = ({ data, children }) => { ); }; -const renderSortIcon = (contentType: string, ascending: boolean) => { - if (contentType === SortTypes.Text) { - return ; +Table.defaultProps = { + sortable: true +}; + +const renderSortIcon = (child: any, ascending: boolean, showIcon: boolean) => { + if (child.props.ascendingIcon && child.props.descendingIcon) { + return ; } else { - return ; + return ; } }; diff --git a/scm-ui/ui-components/src/table/TextColumn.tsx b/scm-ui/ui-components/src/table/TextColumn.tsx index 0ccc69be81..c3184741da 100644 --- a/scm-ui/ui-components/src/table/TextColumn.tsx +++ b/scm-ui/ui-components/src/table/TextColumn.tsx @@ -1,5 +1,5 @@ -import React, {FC} from "react"; -import {ColumnProps, SortTypes} from "./types"; +import React, { FC } from "react"; +import { ColumnProps } from "./types"; type Props = ColumnProps & { dataKey: string; @@ -10,7 +10,7 @@ const TextColumn: FC = ({ row, dataKey }) => { }; TextColumn.defaultProps = { - createComparator: (props: Props) => { + createComparator: (props: Props, columnIndex) => { return (a: any, b: any) => { if (a[props.dataKey] < b[props.dataKey]) { return -1; @@ -21,7 +21,8 @@ TextColumn.defaultProps = { } }; }, - sortType: SortTypes.Text + ascendingIcon: "sort-alpha-down-alt", + descendingIcon: "sort-alpha-down" }; export default TextColumn; diff --git a/scm-ui/ui-components/src/table/types.ts b/scm-ui/ui-components/src/table/types.ts index 683566da6d..9af3e872cb 100644 --- a/scm-ui/ui-components/src/table/types.ts +++ b/scm-ui/ui-components/src/table/types.ts @@ -5,11 +5,8 @@ export type Comparator = (a: any, b: any) => number; export type ColumnProps = { header: ReactNode; row?: any; - createComparator?: (props: any) => Comparator; - sortType: SortTypes; + columnIndex?: number; + createComparator?: (props: any, columnIndex: number) => Comparator; + ascendingIcon?: string; + descendingIcon?: string; }; - -export enum SortTypes { - Text = "text", - Other = "other" -} From 75468483cfd4953b597aa393d8c69f8b8aa92893 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Mon, 2 Dec 2019 14:03:50 +0100 Subject: [PATCH 04/12] refactor --- scm-ui/ui-components/src/table/SortIcon.tsx | 6 +++--- scm-ui/ui-components/src/table/Table.tsx | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/scm-ui/ui-components/src/table/SortIcon.tsx b/scm-ui/ui-components/src/table/SortIcon.tsx index 6f823d3d95..0d553041c6 100644 --- a/scm-ui/ui-components/src/table/SortIcon.tsx +++ b/scm-ui/ui-components/src/table/SortIcon.tsx @@ -4,16 +4,16 @@ import Icon from "../Icon"; type Props = { name: string; - isHidden: boolean; + isVisible: boolean; }; const IconWithMarginLeft = styled(Icon)` - visibility: ${(props: Props) => (props.isHidden ? "hidden" : "visible")}; + visibility: ${(props: Props) => (props.isVisible ? "visible" : "hidden")}; margin-left: 0.25em; `; const SortIcon: FC = (props: Props) => { - return ; + return ; }; export default SortIcon; diff --git a/scm-ui/ui-components/src/table/Table.tsx b/scm-ui/ui-components/src/table/Table.tsx index a36481005e..d9e3c80124 100644 --- a/scm-ui/ui-components/src/table/Table.tsx +++ b/scm-ui/ui-components/src/table/Table.tsx @@ -17,7 +17,7 @@ const Table: FC = ({ data, sortable, children }) => { const [tableData, setTableData] = useState(data); const [ascending, setAscending] = useState(false); const [lastSortBy, setlastSortBy] = useState(); - const [hoveredIndex, setHoveredIndex] = useState(); + const [hoveredColumnIndex, setHoveredColumnIndex] = useState(); const isSortable = (child: any) => { return sortable && child.props.createComparator; @@ -75,11 +75,11 @@ const Table: FC = ({ data, sortable, children }) => { tableSort(index) : undefined} - onMouseEnter={() => setHoveredIndex(index)} - onMouseLeave={() => setHoveredIndex(undefined)} + onMouseEnter={() => setHoveredColumnIndex(index)} + onMouseLeave={() => setHoveredColumnIndex(undefined)} > {child.props.header} - {isSortable(child) && renderSortIcon(child, ascending, index === lastSortBy || index === hoveredIndex)} + {isSortable(child) && renderSortIcon(child, ascending, index === lastSortBy || index === hoveredColumnIndex)} ))} @@ -96,9 +96,9 @@ Table.defaultProps = { const renderSortIcon = (child: any, ascending: boolean, showIcon: boolean) => { if (child.props.ascendingIcon && child.props.descendingIcon) { - return ; + return ; } else { - return ; + return ; } }; From ef74a54b9136395173a8be188f653ae44e8871c2 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Mon, 2 Dec 2019 14:10:04 +0100 Subject: [PATCH 05/12] fix typescript error --- scm-ui/ui-components/src/table/Table.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scm-ui/ui-components/src/table/Table.tsx b/scm-ui/ui-components/src/table/Table.tsx index d9e3c80124..98f9d9f038 100644 --- a/scm-ui/ui-components/src/table/Table.tsx +++ b/scm-ui/ui-components/src/table/Table.tsx @@ -66,6 +66,7 @@ const Table: FC = ({ data, sortable, children }) => { }; // @ts-ignore + const { header } = child.props; return ( tableData.length > 0 && ( @@ -78,8 +79,9 @@ const Table: FC = ({ data, sortable, children }) => { onMouseEnter={() => setHoveredColumnIndex(index)} onMouseLeave={() => setHoveredColumnIndex(undefined)} > - {child.props.header} - {isSortable(child) && renderSortIcon(child, ascending, index === lastSortBy || index === hoveredColumnIndex)} + {header} + {isSortable(child) && + renderSortIcon(child, ascending, index === lastSortBy || index === hoveredColumnIndex)} ))} From 2e676a8f5153f8e410d77683ced2700b578c391c Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Mon, 2 Dec 2019 15:59:32 +0100 Subject: [PATCH 06/12] export table components --- scm-ui/ui-components/src/index.ts | 1 + scm-ui/ui-components/src/table/Column.tsx | 2 +- scm-ui/ui-components/src/table/Table.stories.tsx | 2 +- scm-ui/ui-components/src/table/Table.tsx | 2 +- scm-ui/ui-components/src/table/TextColumn.tsx | 2 +- scm-ui/ui-components/src/table/index.ts | 4 ++++ scm-ui/ui-components/src/table/{types.ts => table.ts} | 0 7 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 scm-ui/ui-components/src/table/index.ts rename scm-ui/ui-components/src/table/{types.ts => table.ts} (100%) diff --git a/scm-ui/ui-components/src/index.ts b/scm-ui/ui-components/src/index.ts index f5c7f62418..d37a04b2e5 100644 --- a/scm-ui/ui-components/src/index.ts +++ b/scm-ui/ui-components/src/index.ts @@ -63,6 +63,7 @@ export * from "./layout"; export * from "./modals"; export * from "./navigation"; export * from "./repos"; +export * from "./table"; export { File, diff --git a/scm-ui/ui-components/src/table/Column.tsx b/scm-ui/ui-components/src/table/Column.tsx index 7f95535c36..4cd0f5d30d 100644 --- a/scm-ui/ui-components/src/table/Column.tsx +++ b/scm-ui/ui-components/src/table/Column.tsx @@ -1,5 +1,5 @@ import React, { FC, ReactNode } from "react"; -import { ColumnProps } from "./types"; +import { ColumnProps } from "./table"; type Props = ColumnProps & { children: (row: any, columnIndex: number) => ReactNode; diff --git a/scm-ui/ui-components/src/table/Table.stories.tsx b/scm-ui/ui-components/src/table/Table.stories.tsx index 84b80f001f..b29db2c965 100644 --- a/scm-ui/ui-components/src/table/Table.stories.tsx +++ b/scm-ui/ui-components/src/table/Table.stories.tsx @@ -3,7 +3,7 @@ import { storiesOf } from "@storybook/react"; import Table from "./Table"; import Column from "./Column"; import TextColumn from "./TextColumn"; -import { ColumnProps } from "./types"; +import { ColumnProps } from "./table"; storiesOf("Table|Table", module) .add("Default", () => ( diff --git a/scm-ui/ui-components/src/table/Table.tsx b/scm-ui/ui-components/src/table/Table.tsx index 98f9d9f038..a41ad84d5a 100644 --- a/scm-ui/ui-components/src/table/Table.tsx +++ b/scm-ui/ui-components/src/table/Table.tsx @@ -1,6 +1,6 @@ import React, { FC, useState } from "react"; import styled from "styled-components"; -import { Comparator } from "./types"; +import { Comparator } from "./table"; import SortIcon from "./SortIcon"; const StyledTable = styled.table.attrs(() => ({ diff --git a/scm-ui/ui-components/src/table/TextColumn.tsx b/scm-ui/ui-components/src/table/TextColumn.tsx index c3184741da..3b55a78edf 100644 --- a/scm-ui/ui-components/src/table/TextColumn.tsx +++ b/scm-ui/ui-components/src/table/TextColumn.tsx @@ -1,5 +1,5 @@ import React, { FC } from "react"; -import { ColumnProps } from "./types"; +import { ColumnProps } from "./table"; type Props = ColumnProps & { dataKey: string; diff --git a/scm-ui/ui-components/src/table/index.ts b/scm-ui/ui-components/src/table/index.ts new file mode 100644 index 0000000000..517273661b --- /dev/null +++ b/scm-ui/ui-components/src/table/index.ts @@ -0,0 +1,4 @@ +export { default as Table} from "./Table"; +export { default as Column} from "./Column"; +export { default as TextColumn} from "./TextColumn"; +export { default as SortIcon} from "./SortIcon"; diff --git a/scm-ui/ui-components/src/table/types.ts b/scm-ui/ui-components/src/table/table.ts similarity index 100% rename from scm-ui/ui-components/src/table/types.ts rename to scm-ui/ui-components/src/table/table.ts From 5e3e57eb48924b247da0f98c59f7a44adea14375 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Tue, 3 Dec 2019 09:37:52 +0100 Subject: [PATCH 07/12] fix ts error --- scm-ui/ui-components/src/table/Table.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scm-ui/ui-components/src/table/Table.tsx b/scm-ui/ui-components/src/table/Table.tsx index a41ad84d5a..c89c72e89e 100644 --- a/scm-ui/ui-components/src/table/Table.tsx +++ b/scm-ui/ui-components/src/table/Table.tsx @@ -65,8 +65,6 @@ const Table: FC = ({ data, sortable, children }) => { setlastSortBy(index); }; - // @ts-ignore - const { header } = child.props; return ( tableData.length > 0 && ( @@ -79,7 +77,10 @@ const Table: FC = ({ data, sortable, children }) => { onMouseEnter={() => setHoveredColumnIndex(index)} onMouseLeave={() => setHoveredColumnIndex(undefined)} > - {header} + { + // @ts-ignore + child.props.header + } {isSortable(child) && renderSortIcon(child, ascending, index === lastSortBy || index === hoveredColumnIndex)} From 5e907ea0b55d8bd70d0c8c32b8948a690b8959e2 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Tue, 3 Dec 2019 09:42:12 +0100 Subject: [PATCH 08/12] refactor --- scm-ui/ui-components/src/table/Table.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scm-ui/ui-components/src/table/Table.tsx b/scm-ui/ui-components/src/table/Table.tsx index c89c72e89e..6d9ccca5c5 100644 --- a/scm-ui/ui-components/src/table/Table.tsx +++ b/scm-ui/ui-components/src/table/Table.tsx @@ -65,6 +65,10 @@ const Table: FC = ({ data, sortable, children }) => { setlastSortBy(index); }; + const shouldShowIcon = (index: number) => { + return index === lastSortBy || index === hoveredColumnIndex; + }; + return ( tableData.length > 0 && ( @@ -81,8 +85,7 @@ const Table: FC = ({ data, sortable, children }) => { // @ts-ignore child.props.header } - {isSortable(child) && - renderSortIcon(child, ascending, index === lastSortBy || index === hoveredColumnIndex)} + {isSortable(child) && renderSortIcon(child, ascending, shouldShowIcon(index))} ))} From e50d3840ce6e1ff19c041a98dd678a2b26479e42 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Tue, 3 Dec 2019 09:54:08 +0100 Subject: [PATCH 09/12] push updated stories --- .../src/__snapshots__/storyshots.test.ts.snap | 133 +++++++++++++++++- 1 file changed, 129 insertions(+), 4 deletions(-) diff --git a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap index e8c0f9d3f4..844dadca2b 100644 --- a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap +++ b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap @@ -336,7 +336,7 @@ exports[`Storyshots DateFromNow Default 1`] = ` exports[`Storyshots Forms|Checkbox Default 1`] = `
`; + +exports[`Storyshots Table|Table Default 1`] = ` + + + + + + + + + + + + + + + + + +
+ FIRST + + SECOND + +
+

+ dddd +

+
+

+ xyz +

+
+

+ abc +

+
+

+ bbbb +

+
+`; + +exports[`Storyshots Table|Table TextColumn 1`] = ` + + + + + + + + + + + + + + + + + + + + + +
+ FIRST + + + SECOND + +
+ d + + y +
+ a + + b +
+ z + + a +
+`; From a08d47df821497fb54ed2092b893ef3dffbf37a3 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Tue, 3 Dec 2019 15:28:26 +0100 Subject: [PATCH 10/12] update test data --- .../src/__snapshots__/storyshots.test.ts.snap | 80 ++++++++++++++----- .../ui-components/src/table/Table.stories.tsx | 20 ++--- 2 files changed, 69 insertions(+), 31 deletions(-) diff --git a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap index 844dadca2b..206a2075b4 100644 --- a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap +++ b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap @@ -2322,7 +2322,7 @@ exports[`Storyshots Table|Table Default 1`] = ` onMouseEnter={[Function]} onMouseLeave={[Function]} > - FIRST + First Name - SECOND + Last Name + + E-Mail + -

- dddd -

+

+ Tricia +

-

- xyz -

+ McMillan + + + + + tricia@hitchhiker.com + -

- abc -

+

+ Arthur +

-

- bbbb -

+ Dent + + + + + arthur@hitchhiker.com + @@ -2390,7 +2406,7 @@ exports[`Storyshots Table|Table TextColumn 1`] = ` onMouseEnter={[Function]} onMouseLeave={[Function]} > - FIRST + Id @@ -2401,7 +2417,18 @@ exports[`Storyshots Table|Table TextColumn 1`] = ` onMouseEnter={[Function]} onMouseLeave={[Function]} > - SECOND + Name + + + + Description @@ -2411,26 +2438,35 @@ exports[`Storyshots Table|Table TextColumn 1`] = ` - d + 21 - y + Pommes + + + Fried potato sticks - a + 42 - b + Quarter-Pounder + + + Big burger - z + -84 - a + Icecream + + + Cold dessert diff --git a/scm-ui/ui-components/src/table/Table.stories.tsx b/scm-ui/ui-components/src/table/Table.stories.tsx index b29db2c965..3d2241dcba 100644 --- a/scm-ui/ui-components/src/table/Table.stories.tsx +++ b/scm-ui/ui-components/src/table/Table.stories.tsx @@ -7,15 +7,15 @@ import { ColumnProps } from "./table"; storiesOf("Table|Table", module) .add("Default", () => ( - - {(row: any) =>

{row.first}

}
+
+ {(row: any) =>

{row.firstname}

}
{ return (a: any, b: any) => { - if (a.second > b.second) { + if (a.lastname > b.lastname) { return -1; - } else if (a.second < b.second) { + } else if (a.lastname < b.lastname) { return 1; } else { return 0; @@ -23,13 +23,15 @@ storiesOf("Table|Table", module) }; }} > - {(row: any) =>

{row.second}

} + {(row: any) => {row.lastname}}
+ {(row: any) => {row.email}}
)) .add("TextColumn", () => ( - - - +
+ + +
)); From 54ac5a1f148154da6d18b1e7ee9f88f7d01c4630 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 3 Dec 2019 16:04:11 +0100 Subject: [PATCH 11/12] remove ts-ignore, added empty message and small bug fixes --- .../ui-components/src/table/Table.stories.tsx | 30 ++++++-- scm-ui/ui-components/src/table/Table.tsx | 72 ++++++++++--------- scm-ui/ui-components/src/table/TextColumn.tsx | 2 +- scm-ui/ui-components/src/table/index.ts | 8 +-- 4 files changed, 67 insertions(+), 45 deletions(-) diff --git a/scm-ui/ui-components/src/table/Table.stories.tsx b/scm-ui/ui-components/src/table/Table.stories.tsx index 3d2241dcba..b4df2a1cb8 100644 --- a/scm-ui/ui-components/src/table/Table.stories.tsx +++ b/scm-ui/ui-components/src/table/Table.stories.tsx @@ -3,15 +3,19 @@ import { storiesOf } from "@storybook/react"; import Table from "./Table"; import Column from "./Column"; import TextColumn from "./TextColumn"; -import { ColumnProps } from "./table"; storiesOf("Table|Table", module) .add("Default", () => ( - +
{(row: any) =>

{row.firstname}

}
{ + createComparator={() => { return (a: any, b: any) => { if (a.lastname > b.lastname) { return -1; @@ -29,9 +33,21 @@ storiesOf("Table|Table", module)
)) .add("TextColumn", () => ( - - - - +
+ + + +
+ )) + .add("Empty", () => ( + + +
)); diff --git a/scm-ui/ui-components/src/table/Table.tsx b/scm-ui/ui-components/src/table/Table.tsx index 6d9ccca5c5..ffcd38238f 100644 --- a/scm-ui/ui-components/src/table/Table.tsx +++ b/scm-ui/ui-components/src/table/Table.tsx @@ -1,7 +1,8 @@ -import React, { FC, useState } from "react"; +import React, { FC, ReactElement, useState } from "react"; import styled from "styled-components"; import { Comparator } from "./table"; import SortIcon from "./SortIcon"; +import Notification from "../Notification"; const StyledTable = styled.table.attrs(() => ({ className: "table content is-hoverable" @@ -10,23 +11,23 @@ const StyledTable = styled.table.attrs(() => ({ type Props = { data: any[]; sortable?: boolean; + emptyMessage?: string; + children: Array; }; -// @ts-ignore -const Table: FC = ({ data, sortable, children }) => { +const Table: FC = ({ data, sortable, children, emptyMessage }) => { const [tableData, setTableData] = useState(data); const [ascending, setAscending] = useState(false); const [lastSortBy, setlastSortBy] = useState(); const [hoveredColumnIndex, setHoveredColumnIndex] = useState(); - const isSortable = (child: any) => { + const isSortable = (child: ReactElement) => { return sortable && child.props.createComparator; }; const sortFunctions: Comparator | undefined[] = []; React.Children.forEach(children, (child, index) => { - if (isSortable(child)) { - // @ts-ignore + if (child && isSortable(child)) { sortFunctions.push(child.props.createComparator(child.props, index)); } else { sortFunctions.push(undefined); @@ -37,7 +38,6 @@ const Table: FC = ({ data, sortable, children }) => { return ( {React.Children.map(children, (child, columnIndex) => { - // @ts-ignore return {React.cloneElement(child, { ...child.props, columnIndex, row })}; })} @@ -51,14 +51,17 @@ const Table: FC = ({ data, sortable, children }) => { }; const tableSort = (index: number) => { + const sortFn = sortFunctions[index]; + if (!sortFn) { + throw new Error(`column with index ${index} is not sortable`); + } const sortableData = [...tableData]; let sortOrder = ascending; if (lastSortBy !== index) { setAscending(true); sortOrder = true; } - // @ts-ignore - const sortFunction = sortOrder ? sortFunctions[index] : sortDescending(sortFunctions[index]); + const sortFunction = sortOrder ? sortFn : sortDescending(sortFn); sortableData.sort(sortFunction); setTableData(sortableData); setAscending(!sortOrder); @@ -69,30 +72,33 @@ const Table: FC = ({ data, sortable, children }) => { return index === lastSortBy || index === hoveredColumnIndex; }; + if (!tableData || tableData.length <= 0) { + if (emptyMessage) { + return {emptyMessage}; + } else { + return null; + } + } + return ( - tableData.length > 0 && ( - - - - {React.Children.map(children, (child, index) => ( - tableSort(index) : undefined} - onMouseEnter={() => setHoveredColumnIndex(index)} - onMouseLeave={() => setHoveredColumnIndex(undefined)} - > - { - // @ts-ignore - child.props.header - } - {isSortable(child) && renderSortIcon(child, ascending, shouldShowIcon(index))} - - ))} - - - {tableData.map(mapDataToColumns)} - - ) + + + + {React.Children.map(children, (child, index) => ( + tableSort(index) : undefined} + onMouseEnter={() => setHoveredColumnIndex(index)} + onMouseLeave={() => setHoveredColumnIndex(undefined)} + > + {child.props.header} + {isSortable(child) && renderSortIcon(child, ascending, shouldShowIcon(index))} + + ))} + + + {tableData.map(mapDataToColumns)} + ); }; @@ -100,7 +106,7 @@ Table.defaultProps = { sortable: true }; -const renderSortIcon = (child: any, ascending: boolean, showIcon: boolean) => { +const renderSortIcon = (child: ReactElement, ascending: boolean, showIcon: boolean) => { if (child.props.ascendingIcon && child.props.descendingIcon) { return ; } else { diff --git a/scm-ui/ui-components/src/table/TextColumn.tsx b/scm-ui/ui-components/src/table/TextColumn.tsx index 3b55a78edf..a170b02192 100644 --- a/scm-ui/ui-components/src/table/TextColumn.tsx +++ b/scm-ui/ui-components/src/table/TextColumn.tsx @@ -10,7 +10,7 @@ const TextColumn: FC = ({ row, dataKey }) => { }; TextColumn.defaultProps = { - createComparator: (props: Props, columnIndex) => { + createComparator: (props: Props) => { return (a: any, b: any) => { if (a[props.dataKey] < b[props.dataKey]) { return -1; diff --git a/scm-ui/ui-components/src/table/index.ts b/scm-ui/ui-components/src/table/index.ts index 517273661b..c99a4e64c1 100644 --- a/scm-ui/ui-components/src/table/index.ts +++ b/scm-ui/ui-components/src/table/index.ts @@ -1,4 +1,4 @@ -export { default as Table} from "./Table"; -export { default as Column} from "./Column"; -export { default as TextColumn} from "./TextColumn"; -export { default as SortIcon} from "./SortIcon"; +export { default as Table } from "./Table"; +export { default as Column } from "./Column"; +export { default as TextColumn } from "./TextColumn"; +export { default as SortIcon } from "./SortIcon"; From 74029e455373ccd8a39385baa1c92e84b56e7e8b Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Tue, 3 Dec 2019 15:08:13 +0000 Subject: [PATCH 12/12] Close branch feature/sortable_table