diff --git a/web/src/OverviewTable.tsx b/web/src/OverviewTable.tsx index c1a580b55d..cbbdaa0868 100644 --- a/web/src/OverviewTable.tsx +++ b/web/src/OverviewTable.tsx @@ -3,7 +3,7 @@ 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, @@ -11,13 +11,19 @@ import { 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 { @@ -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" @@ -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}; @@ -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 ( + + + + + + + ) +} + 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 ( @@ -568,7 +622,7 @@ export function Table(props: TableProps) { ))} - {rows.map((row: Row) => { + {page.map((row: Row) => { prepareRow(row) return ( ) })} + ) @@ -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>() + useState>() const useControlledState = (state: TableState) => useMemo(() => { diff --git a/web/src/ShowMoreButton.tsx b/web/src/ShowMoreButton.tsx new file mode 100644 index 0000000000..670e6bc2bb --- /dev/null +++ b/web/src/ShowMoreButton.tsx @@ -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 ( + <> + + …Show more + + + + ) +} diff --git a/web/src/SidebarResources.tsx b/web/src/SidebarResources.tsx index aa6e2cd96a..11e504f25e 100644 --- a/web/src/SidebarResources.tsx +++ b/web/src/SidebarResources.tsx @@ -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, @@ -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" @@ -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 = ( - …Show More - - {`(${remaining})`} + analyticsTags={{ type: AnalyticsType.Detail }} + currentListSize={maxItems} + itemCount={props.items.length} + /> ) } diff --git a/web/src/constants.ts b/web/src/constants.ts index baf60cdc20..fd4d4ba351 100644 --- a/web/src/constants.ts +++ b/web/src/constants.ts @@ -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 diff --git a/web/src/react-table-config.d.ts b/web/src/react-table-config.d.ts index 949d7f0b07..f680cea73b 100644 --- a/web/src/react-table-config.d.ts +++ b/web/src/react-table-config.d.ts @@ -1,6 +1,9 @@ // From https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react-table import { + UsePaginationInstanceProps, + UsePaginationOptions, + UsePaginationState, UseSortByColumnOptions, UseSortByColumnProps, UseSortByHooks, @@ -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> - extends UseSortByOptions { + extends UseSortByOptions, + UsePaginationOptions { // UseExpandedOptions, // UseFiltersOptions, // UseGlobalFiltersOptions, // UseGroupByOptions, - // UsePaginationOptions, // UseResizeColumnsOptions, // UseRowSelectOptions, // UseRowStateOptions, @@ -39,13 +42,13 @@ declare module "react-table" { export interface TableInstance< D extends Record = Record - > extends UseSortByInstanceProps { + > extends UseSortByInstanceProps, + UsePaginationInstanceProps { // UseColumnOrderInstanceProps, // UseExpandedInstanceProps, // UseFiltersInstanceProps, // UseGlobalFiltersInstanceProps, // UseGroupByInstanceProps, - // UsePaginationInstanceProps, // UseRowSelectInstanceProps, // UseRowStateInstanceProps, // UseSortByInstanceProps @@ -53,13 +56,13 @@ declare module "react-table" { export interface TableState< D extends Record = Record - > extends UseSortByState { + > extends UseSortByState, + UsePaginationState { // UseColumnOrderState, // UseExpandedState, // UseFiltersState, // UseGlobalFiltersState, // UseGroupByState, - // UsePaginationState, // UseResizeColumnsState, // UseRowSelectState, // UseRowStateState,