Skip to content

Commit

Permalink
(feat) Add pagination to resource list tables to mirror Detail View
Browse files Browse the repository at this point in the history
  • Loading branch information
lizzthabet committed Feb 16, 2022
1 parent dc93b3f commit e0fbaa2
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 58 deletions.
86 changes: 73 additions & 13 deletions web/src/OverviewTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,27 @@ import {
AccordionDetails,
AccordionSummary,
} from "@material-ui/core"
import React, { ChangeEvent, useMemo, useState } from "react"
import React, { ChangeEvent, MouseEvent, useMemo, useState } from "react"
import {
HeaderGroup,
Row,
SortingRule,
TableHeaderProps,
TableOptions,
TableState,
usePagination,
useSortBy,
UseSortByState,
useTable,
} from "react-table"
import styled from "styled-components"
import { buildAlerts, runtimeAlerts } from "./alerts"
import { AnalyticsType } from "./analytics"
import { ApiButtonType, buttonsForComponent } from "./ApiButton"
import {
DEFAULT_RESOURCE_LIST_LIMIT,
RESOURCE_LIST_MULTIPLIER,
} from "./constants"
import Features, { Flag, useFeatures } from "./feature"
import { Hold } from "./Hold"
import {
Expand Down Expand Up @@ -57,6 +63,7 @@ import {
resourceTargetType,
} from "./ResourceStatus"
import { TableGroupStatusSummary } from "./ResourceStatusSummary"
import { ShowMoreButton } from "./ShowMoreButton"
import { buildStatus, runtimeStatus } from "./status"
import { Color, Font, FontSize, SizeUnit, Width } from "./style-helpers"
import { isZeroTime, timeDiff } from "./time"
Expand Down Expand Up @@ -168,7 +175,12 @@ export const ResourceTableData = styled.td`
&.isSorted {
background-color: ${Color.gray};
}
&.alignRight {
text-align: right;
}
`

export const ResourceTableHeader = styled(ResourceTableData)`
color: ${Color.gray7};
font-size: ${FontSize.small};
Expand Down Expand Up @@ -539,21 +551,63 @@ export function ResourceTableHeadRow({
)
}

function ShowMoreResourcesRow({
colSpan,
itemCount,
pageSize,
onClick,
}: {
colSpan: number
itemCount: number
pageSize: number
onClick: (e: MouseEvent) => void
}) {
if (itemCount <= pageSize) {
return null
}

return (
<ResourceTableRow style={{ height: "45px" }}>
<ResourceTableData colSpan={colSpan - 2} />
<ResourceTableData className="alignRight" colSpan={2}>
<ShowMoreButton
itemCount={itemCount}
currentListSize={pageSize}
onClick={onClick}
analyticsTags={{ type: AnalyticsType.Grid }}
/>
</ResourceTableData>
</ResourceTableRow>
)
}

export function Table(props: TableProps) {
if (props.data.length === 0) {
return null
}

const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } =
useTable(
{
columns: props.columns,
data: props.data,
autoResetSortBy: false,
useControlledState: props.useControlledState,
},
useSortBy
)
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
page,
prepareRow,
state: { pageSize },
setPageSize,
} = useTable(
{
columns: props.columns,
data: props.data,
autoResetSortBy: false,
useControlledState: props.useControlledState,
initialState: { pageSize: DEFAULT_RESOURCE_LIST_LIMIT },
},
useSortBy,
usePagination
)

const showMoreOnClick = () => setPageSize(pageSize * RESOURCE_LIST_MULTIPLIER)

// TODO (lizz): Consider adding `aria-sort` markup to table headings
return (
Expand All @@ -568,7 +622,7 @@ export function Table(props: TableProps) {
))}
</ResourceTableHead>
<tbody {...getTableBodyProps()}>
{rows.map((row: Row<RowValues>) => {
{page.map((row: Row<RowValues>) => {
prepareRow(row)
return (
<ResourceTableRow
Expand All @@ -587,6 +641,12 @@ export function Table(props: TableProps) {
</ResourceTableRow>
)
})}
<ShowMoreResourcesRow
itemCount={rows.length}
pageSize={pageSize}
onClick={showMoreOnClick}
colSpan={props.columns.length}
/>
</tbody>
</ResourceTable>
)
Expand Down Expand Up @@ -640,7 +700,7 @@ export function TableGroupedByLabels({
// tables by the same column
// See: https://react-table.tanstack.com/docs/faq#how-can-i-manually-control-the-table-state
const [globalTableSettings, setGlobalTableSettings] =
useState<TableState<RowValues>>()
useState<UseSortByState<RowValues>>()

const useControlledState = (state: TableState<RowValues>) =>
useMemo(() => {
Expand Down
65 changes: 65 additions & 0 deletions web/src/ShowMoreButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React, { MouseEventHandler } from "react"
import styled from "styled-components"
import { Tags } from "./analytics"
import { InstrumentedButton } from "./instrumentedComponents"
import {
AnimDuration,
Color,
Font,
FontSize,
mixinResetButtonStyle,
} from "./style-helpers"

const ShowMoreButtonRoot = styled(InstrumentedButton)`
${mixinResetButtonStyle};
color: ${Color.gray6};
font-family: ${Font.sansSerif};
font-size: ${FontSize.small};
padding: 0 0.5em;
transition: color ${AnimDuration.default} ease;
&:hover,
&:focus,
&:active {
color: ${Color.blue};
}
`

const ShowMoreCount = styled.span`
color: ${Color.gray7};
font-family: ${Font.sansSerif};
font-size: ${FontSize.small};
`

export function ShowMoreButton({
itemCount,
currentListSize,
onClick,
analyticsTags,
}: {
itemCount: number
currentListSize: number
analyticsTags: Tags
onClick: MouseEventHandler
}) {
if (itemCount <= currentListSize) {
return null
}

const remainingCount = itemCount - currentListSize

return (
<>
<ShowMoreButtonRoot
analyticsName="ui.web.showMore"
analyticsTags={analyticsTags}
onClick={onClick}
>
…Show more
</ShowMoreButtonRoot>
<ShowMoreCount aria-label={`${remainingCount} hidden resources`}>
({remainingCount})
</ShowMoreCount>
</>
)
}
52 changes: 13 additions & 39 deletions web/src/SidebarResources.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import {
} from "@material-ui/core"
import React, { ChangeEvent, useCallback, useMemo, useState } from "react"
import styled from "styled-components"
import { AnalyticsType, emptyTags } from "./analytics"
import { AnalyticsType } from "./analytics"
import {
DEFAULT_RESOURCE_LIST_LIMIT,
RESOURCE_LIST_MULTIPLIER,
} from "./constants"
import { FeaturesContext, Flag, useFeatures } from "./feature"
import { InstrumentedButton } from "./instrumentedComponents"
import {
GroupByLabelView,
orderLabels,
Expand All @@ -28,20 +31,14 @@ import { useResourceGroups } from "./ResourceGroupsContext"
import { ResourceListOptions } from "./ResourceListOptionsContext"
import { matchesResourceName } from "./ResourceNameFilter"
import { SidebarGroupStatusSummary } from "./ResourceStatusSummary"
import { ShowMoreButton } from "./ShowMoreButton"
import SidebarItem from "./SidebarItem"
import SidebarItemView, {
sidebarItemIsDisabled,
SidebarItemRoot,
} from "./SidebarItemView"
import SidebarKeyboardShortcuts from "./SidebarKeyboardShortcuts"
import {
AnimDuration,
Color,
Font,
FontSize,
mixinResetButtonStyle,
SizeUnit,
} from "./style-helpers"
import { Color, Font, FontSize, SizeUnit } from "./style-helpers"
import { triggerUpdate } from "./trigger"
import { ResourceStatus, ResourceView } from "./types"

Expand Down Expand Up @@ -199,58 +196,35 @@ export function SidebarListSection(props: SidebarSectionProps): JSX.Element {
)
}

const defaultMaxItems = 20

const ShowMoreRow = styled.li`
margin: ${SizeUnit(0.5)} ${SizeUnit(0.5)} 0 ${SizeUnit(0.5)};
color: ${Color.gray7};
font-size: ${FontSize.small};
display: flex;
align-items: center;
justify-content: right;
font-family: ${Font.sansSerif};
`

const ShowMoreButton = styled(InstrumentedButton)`
${mixinResetButtonStyle};
font-size: ${FontSize.small};
color: ${Color.gray6};
transition: color ${AnimDuration.default} ease;
cursor: pointer;
padding: 0 0.5em;
&:hover {
color: ${Color.blue};
}
`

function SidebarListSectionItems(props: SidebarSectionProps) {
let [maxItems, setMaxItems] = useState(defaultMaxItems)
let [maxItems, setMaxItems] = useState(DEFAULT_RESOURCE_LIST_LIMIT)
let displayItems = props.items
let remaining = 0
let moreItems = Math.max(displayItems.length - maxItems, 0)
if (moreItems) {
remaining = displayItems.length - maxItems
displayItems = displayItems.slice(0, maxItems)
}

let showMore = useCallback(() => {
setMaxItems(maxItems * 2)
setMaxItems(maxItems * RESOURCE_LIST_MULTIPLIER)
}, [maxItems, setMaxItems])

let showMoreItemsButton = null
if (moreItems > 0) {
let text = ` (${remaining})`
showMoreItemsButton = (
<ShowMoreRow>
<ShowMoreButton
onClick={showMore}
analyticsName="ui.web.sidebarShowMore"
analyticsTags={emptyTags}
>
…Show More
</ShowMoreButton>
<span aria-label={`${remaining} hidden`}>{`(${remaining})`}</span>
analyticsTags={{ type: AnalyticsType.Detail }}
currentListSize={maxItems}
itemCount={props.items.length}
/>
</ShowMoreRow>
)
}
Expand Down
3 changes: 3 additions & 0 deletions web/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@ export function linkToTiltDocs(page?: TiltDocsPage, anchor?: string) {

return `${TILT_DOCS_LINK}/${page}${anchor ?? ""}`
}

export const DEFAULT_RESOURCE_LIST_LIMIT = 20
export const RESOURCE_LIST_MULTIPLIER = 2
15 changes: 9 additions & 6 deletions web/src/react-table-config.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// From https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react-table

import {
UsePaginationInstanceProps,
UsePaginationOptions,
UsePaginationState,
UseSortByColumnOptions,
UseSortByColumnProps,
UseSortByHooks,
Expand All @@ -13,12 +16,12 @@ declare module "react-table" {
// take this file as-is, or comment out the sections that don't apply to your plugin configuration

export interface TableOptions<D extends Record<string, unknown>>
extends UseSortByOptions<D> {
extends UseSortByOptions<D>,
UsePaginationOptions<D> {
// UseExpandedOptions<D>,
// UseFiltersOptions<D>,
// UseGlobalFiltersOptions<D>,
// UseGroupByOptions<D>,
// UsePaginationOptions<D>,
// UseResizeColumnsOptions<D>,
// UseRowSelectOptions<D>,
// UseRowStateOptions<D>,
Expand All @@ -39,27 +42,27 @@ declare module "react-table" {

export interface TableInstance<
D extends Record<string, unknown> = Record<string, unknown>
> extends UseSortByInstanceProps<D> {
> extends UseSortByInstanceProps<D>,
UsePaginationInstanceProps<D> {
// UseColumnOrderInstanceProps<D>,
// UseExpandedInstanceProps<D>,
// UseFiltersInstanceProps<D>,
// UseGlobalFiltersInstanceProps<D>,
// UseGroupByInstanceProps<D>,
// UsePaginationInstanceProps<D>,
// UseRowSelectInstanceProps<D>,
// UseRowStateInstanceProps<D>,
// UseSortByInstanceProps<D>
}

export interface TableState<
D extends Record<string, unknown> = Record<string, unknown>
> extends UseSortByState<D> {
> extends UseSortByState<D>,
UsePaginationState<D> {
// UseColumnOrderState<D>,
// UseExpandedState<D>,
// UseFiltersState<D>,
// UseGlobalFiltersState<D>,
// UseGroupByState<D>,
// UsePaginationState<D>,
// UseResizeColumnsState<D>,
// UseRowSelectState<D>,
// UseRowStateState<D>,
Expand Down

0 comments on commit e0fbaa2

Please sign in to comment.