Skip to content

Commit

Permalink
Adds Pagination to Data Table (#1367)
Browse files Browse the repository at this point in the history
* pagination added

* added tests

* added displayLabel key to fields to avoid manipulating string in data table

* added display label field to data table story

* display label fix in commits table

* woke up in a cold sweat realizing that the indexing for the text was wrong so I fixed it

* redo w/o tests

* redo two with tests

* data table styling changes

* removed displayLabel and added textProps to Link in order to alter link size in data table + other small fixes

* got rid of TextProps it was bad
  • Loading branch information
joshri committed Feb 2, 2022
1 parent dcaf5be commit 0d6fd9c
Show file tree
Hide file tree
Showing 9 changed files with 409 additions and 59 deletions.
52 changes: 38 additions & 14 deletions ui/components/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import styled from "styled-components";
import Button from "./Button";
import Flex from "./Flex";
import Icon, { IconType } from "./Icon";
import Link from "./Link";
import Spacer from "./Spacer";
import Text from "./Text";

Expand All @@ -26,14 +27,15 @@ export interface Props {
}[];
/** A list of data that will be iterated through to create the columns described in `fields`. */
rows: any[];
/** A list of strings representing the sortable columns of the table, passed into lodash's `_.sortBy`. */
sortFields: string[];
/** A list of strings representing the sortable columns of the table, passed into lodash's `_.sortBy`. Must be lowercase. */
sortFields?: string[];
/** an optional list of string widths for each field/column. */
widths?: string[];
/** for passing pagination */
children?: any;
}

const EmptyRow = styled(TableRow)<{ colSpan: number }>`
font-style: italic;
td {
text-align: center;
}
Expand All @@ -46,24 +48,24 @@ const TableButton = styled(Button)`
text-transform: none;
}
&.MuiButton-text {
color: ${(props) => props.theme.colors.neutral30};
min-width: 0px;
.selected {
color: ${(props) => props.theme.colors.neutral40};
}
}
&.arrow {
min-width: 0px;
}
&.selected {
color: ${(props) => props.theme.colors.neutral40};
}
`;

/** Form DataTable */
function UnstyledDataTable({
className,
fields,
rows,
sortFields,
sortFields = [],
widths,
children,
}: Props) {
const [sort, setSort] = React.useState(sortFields[0]);
const [reverseSort, setReverseSort] = React.useState(false);
Expand All @@ -85,7 +87,9 @@ function UnstyledDataTable({
setSort(label.toLowerCase());
}}
>
<h2>{label}</h2>
<h2 className={sort === label.toLowerCase() && "selected"}>
{label}
</h2>
</TableButton>
<Spacer padding="xxs" />
{sort === label.toLowerCase() ? (
Expand Down Expand Up @@ -122,7 +126,7 @@ function UnstyledDataTable({
{sortFields.includes(f.label.toLowerCase()) ? (
<SortableLabel label={f.label} />
) : (
<h2 className="thead">{f.label}</h2>
<h2>{f.label}</h2>
)}
</TableCell>
))}
Expand All @@ -134,24 +138,44 @@ function UnstyledDataTable({
) : (
<EmptyRow colSpan={fields.length}>
<TableCell colSpan={fields.length}>
<span style={{ fontStyle: "italic" }}>No rows</span>
<Flex center align>
<Icon
color="neutral20"
type={IconType.RemoveCircleIcon}
size="base"
/>
<Spacer padding="xxs" />
<Text color="neutral30">No data</Text>
</Flex>
</TableCell>
</EmptyRow>
)}
</TableBody>
</Table>
</TableContainer>
<Spacer padding="xs" />
{/* optional pagination component */}
{children}
</div>
);
}

export const DataTable = styled(UnstyledDataTable)`
h2 {
font-size: 14px;
font-weight: 600;
color: ${(props) => props.theme.colors.neutral30};
margin: 0px;
}
.thead {
color: ${(props) => props.theme.colors.neutral30};
font-weight: 800;
.MuiTableRow-root {
transition: background 0.5s ease-in-out;
}
.MuiTableRow-root:not(.MuiTableRow-head):hover {
background: ${(props) => props.theme.colors.neutral10};
transition: background 0.5s ease-in-out;
}
${Link} ${Text} {
font-size: 14px;
}
`;

Expand Down
28 changes: 24 additions & 4 deletions ui/components/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ import DeleteIcon from "@material-ui/icons/Delete";
import ErrorIcon from "@material-ui/icons/Error";
import HourglassFullIcon from "@material-ui/icons/HourglassFull";
import LaunchIcon from "@material-ui/icons/Launch";
import NavigateBeforeIcon from "@material-ui/icons/NavigateBefore";
import NavigateNextIcon from "@material-ui/icons/NavigateNext";
import RemoveCircleIcon from "@material-ui/icons/RemoveCircle";
import SaveAltIcon from "@material-ui/icons/SaveAlt";
import SkipNextIcon from "@material-ui/icons/SkipNext";
import SkipPreviousIcon from "@material-ui/icons/SkipPrevious";
import * as React from "react";
import styled from "styled-components";
import { colors, spacing } from "../typedefs/styled";
Expand All @@ -21,11 +25,15 @@ export enum IconType {
AddIcon,
ArrowUpwardIcon,
DeleteIcon,
NavigateNextIcon,
SaveAltIcon,
ErrorIcon,
CheckCircleIcon,
HourglassFullIcon,
NavigateNextIcon,
NavigateBeforeIcon,
SkipNextIcon,
SkipPreviousIcon,
RemoveCircleIcon,
}

type Props = {
Expand Down Expand Up @@ -56,9 +64,6 @@ function getIcon(i: IconType) {
case IconType.DeleteIcon:
return DeleteIcon;

case IconType.NavigateNextIcon:
return NavigateNextIcon;

case IconType.SaveAltIcon:
return SaveAltIcon;

Expand All @@ -71,6 +76,21 @@ function getIcon(i: IconType) {
case IconType.ErrorIcon:
return ErrorIcon;

case IconType.NavigateNextIcon:
return NavigateNextIcon;

case IconType.NavigateBeforeIcon:
return NavigateBeforeIcon;

case IconType.SkipNextIcon:
return SkipNextIcon;

case IconType.SkipPreviousIcon:
return SkipPreviousIcon;

case IconType.RemoveCircleIcon:
return RemoveCircleIcon;

default:
break;
}
Expand Down
10 changes: 9 additions & 1 deletion ui/components/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,15 @@ type Props = {
newTab?: boolean;
};

function Link({ children, href, className, to = "", newTab, ...props }: Props) {
function Link({
children,
href,
className,
to = "",
newTab,

...props
}: Props) {
const txt = <Text color="primary">{children}</Text>;

if (href) {
Expand Down
118 changes: 118 additions & 0 deletions ui/components/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { FormControl, MenuItem, Select } from "@material-ui/core";
import * as React from "react";
import styled from "styled-components";
import Button from "./Button";
import Flex from "./Flex";
import Icon, { IconType } from "./Icon";
import Spacer from "./Spacer";
import Text from "./Text";

export interface Props {
/** CSS MUI Overrides or other styling. */
className?: string;
/** func for forward one page button */
onForward: () => void;
/** func for skip to last page button */
onSkipForward: () => void;
/** func for back one page button */
onBack: () => void;
/** func for skip to start button */
onSkipBack: () => void;
/** onChange func for perPage select */
onSelect: (value: string) => void;
/** options for perPage select */
perPageOptions?: number[];
/** starting index */
index: number;
/** total rows */
length: number;
/** all objects */
totalObjects: number;
}

function unstyledPagination({
className,
onForward,
onSkipForward,
onBack,
onSkipBack,
onSelect,
perPageOptions = [25, 50, 75, 100],
index,
length,
totalObjects,
}: Props) {
return (
<Flex wide align end className={className}>
<FormControl>
<Flex align>
<label htmlFor="pagination">Rows Per Page: </label>
<Spacer padding="xxs" />
<Select
id="pagination"
variant="outlined"
defaultValue={perPageOptions[0]}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
onSelect(e.target.value);
}}
>
{perPageOptions.map((option, index) => {
return (
<MenuItem key={index} value={option}>
{option}
</MenuItem>
);
})}
</Select>
</Flex>
</FormControl>
<Spacer padding="base" />
<Text>
{index + 1} - {index + length} out of {totalObjects}
</Text>
<Spacer padding="base" />
<Flex>
<Button
color="inherit"
variant="text"
aria-label="skip to first page"
disabled={index === 0}
onClick={onSkipBack}
>
<Icon type={IconType.SkipPreviousIcon} size="medium" />
</Button>
<Button
color="inherit"
variant="text"
aria-label="back one page"
disabled={index === 0}
onClick={onBack}
>
<Icon type={IconType.NavigateBeforeIcon} size="medium" />
</Button>
<Button
color="inherit"
variant="text"
aria-label="forward one page"
disabled={index + length >= totalObjects}
onClick={onForward}
>
<Icon type={IconType.NavigateNextIcon} size="medium" />
</Button>
<Button
color="inherit"
variant="text"
aria-label="skip to last page"
disabled={index + length >= totalObjects}
onClick={onSkipForward}
>
<Icon type={IconType.SkipNextIcon} size="medium" />
</Button>
</Flex>
</Flex>
);
}

export const Pagination = styled(unstyledPagination)``;

export default Pagination;

0 comments on commit 0d6fd9c

Please sign in to comment.