Skip to content

Commit

Permalink
Web: Table Updates (#43156)
Browse files Browse the repository at this point in the history
* Reduce search input height for tables

* Allow table to show top and bottom pager

Default table to show top pager unless current page data
is greater than 5

* Persist table current page if not sorting or searching

* Update snaps

* Pull table search input outside of pager position conditional

* Add margin top

* Update snaps

* Address CR
  • Loading branch information
kimlisa authored Jun 25, 2024
1 parent e163c14 commit 220552d
Show file tree
Hide file tree
Showing 23 changed files with 1,927 additions and 1,193 deletions.
18 changes: 12 additions & 6 deletions web/packages/design/src/DataTable/InputSearch/InputSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ export default function InputSearch({
searchValue,
setSearchValue,
children,
bigInputSize = false,
}: Props) {
return (
<WrapperBackground>
<WrapperBackground bigSize={bigInputSize}>
<Wrapper>
<StyledInput
bigInputSize={bigInputSize}
placeholder="Search..."
px={3}
value={searchValue}
Expand All @@ -56,6 +58,7 @@ type Props = {
searchValue: string;
setSearchValue: React.Dispatch<SetStateAction<string>>;
children?: JSX.Element;
bigInputSize?: boolean;
};

const ChildWrapper = styled.div`
Expand Down Expand Up @@ -90,20 +93,23 @@ const Wrapper = styled.div`
max-width: 725px;
`;

const WrapperBackground = styled.div`
const WrapperBackground = styled.div<{ bigSize: boolean }>`
border-radius: 200px;
width: 100%;
height: ${props => props.theme.space[8]}px;
margin-bottom: 12px;
height: ${props =>
props.bigSize ? props.theme.space[7] : props.theme.space[6]}px;
`;

interface StyledInputProps extends ColorProps, SpaceProps, HeightProps {}
interface StyledInputProps extends ColorProps, SpaceProps, HeightProps {
bigInputSize: boolean;
}

const StyledInput = styled.input<StyledInputProps>`
border: none;
outline: none;
box-sizing: border-box;
font-size: ${props => props.theme.fontSizes[3]}px;
font-size: ${props =>
props.bigInputSize ? props.theme.fontSizes[3] : props.theme.fontSizes[2]}px;
width: 100%;
transition: all 0.2s;
${color}
Expand Down
6 changes: 5 additions & 1 deletion web/packages/design/src/DataTable/Pager/ServerSidePager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ export function ServerSidePager({ nextPage, prevPage, isLoading }: Props) {
const isPrevDisabled = !prevPage || isLoading;

return (
<Flex justifyContent="flex-end" width="100%">
<Flex
justifyContent="flex-end"
width="100%"
css={{ flexShrink: 1, flexGrow: 0, flexBasis: 0 }}
>
<Flex>
<StyledArrowBtn
onClick={prevPage}
Expand Down
4 changes: 2 additions & 2 deletions web/packages/design/src/DataTable/StyledTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export const StyledPanel = styled.nav`
flex-shrink: 0;
align-items: center;
justify-content: space-between;
padding: ${props => props.theme.space[3]}px 0;
padding: 0 0 ${props => props.theme.space[3]}px 0;
max-height: ${props => props.theme.space[6]}px;
margin-bottom: ${p => p.theme.space[2]}px;
margin-top: ${props => props.theme.space[1]}px;
`;
93 changes: 61 additions & 32 deletions web/packages/design/src/DataTable/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { StyledTable, StyledPanel } from './StyledTable';
import {
BasicTableProps,
PagedTableProps,
PagerPosition,
SearchableBasicTableProps,
ServersideTableProps,
TableProps,
Expand Down Expand Up @@ -232,7 +233,12 @@ function SearchableBasicTable<T>({
}: SearchableBasicTableProps<T>) {
return (
<>
<InputSearch searchValue={searchValue} setSearchValue={setSearchValue} />
<Box mb={3}>
<InputSearch
searchValue={searchValue}
setSearchValue={setSearchValue}
/>
</Box>
<StyledTable
className={className}
borderTopLeftRadius={0}
Expand Down Expand Up @@ -260,46 +266,33 @@ function PagedTable<T>({
style,
}: PagedTableProps<T>) {
const { pagerPosition, paginatedData, currentPage } = pagination;
const isTopPager = pagerPosition === 'top';

const radiusProps = {
borderTopLeftRadius: 3,
borderTopRightRadius: 3,
borderBottomLeftRadius: 3,
borderBottomRightRadius: 3,
};
const { showBothPager, showBottomPager, showTopPager } = getPagerPosition(
pagerPosition,
paginatedData[currentPage].length
);

if (isTopPager) {
radiusProps.borderTopLeftRadius = 0;
radiusProps.borderTopRightRadius = 0;
} else {
radiusProps.borderBottomLeftRadius = 0;
radiusProps.borderBottomRightRadius = 0;
}
return (
<>
{isTopPager && (
<>
<StyledPanel>
<InputSearch
searchValue={searchValue}
setSearchValue={setSearchValue}
/>
</StyledPanel>
<StyledPanel>
<InputSearch
searchValue={searchValue}
setSearchValue={setSearchValue}
/>
{(showTopPager || showBothPager) && (
<ClientSidePager
nextPage={nextPage}
prevPage={prevPage}
data={data}
{...fetching}
{...pagination}
/>
</>
)}
<StyledTable {...radiusProps} className={className} style={style}>
)}
</StyledPanel>
<StyledTable className={className} style={style}>
{renderHeaders()}
{renderBody(paginatedData[currentPage])}
</StyledTable>
{!isTopPager && (
{(showBottomPager || showBothPager) && (
<StyledPanel>
<ClientSidePager
nextPage={nextPage}
Expand All @@ -323,22 +316,36 @@ function ServersideTable<T>({
style,
serversideProps,
fetchStatus,
pagination,
}: ServersideTableProps<T>) {
const { showTopPager, showBothPager, showBottomPager } = getPagerPosition(
pagination?.pagerPosition,
data.length
);
return (
<>
{serversideProps.serversideSearchPanel}
<StyledPanel>
{serversideProps.serversideSearchPanel}
{(showTopPager || showBothPager) && (
<ServerSidePager
nextPage={nextPage}
prevPage={prevPage}
isLoading={fetchStatus === 'loading'}
/>
)}
</StyledPanel>
<StyledTable className={className} style={style}>
{renderHeaders()}
{renderBody(data)}
</StyledTable>
{(nextPage || prevPage) && (
<StyledPanel>
{(showBottomPager || showBothPager) && (
<Box mt={2}>
<ServerSidePager
nextPage={nextPage}
prevPage={prevPage}
isLoading={fetchStatus === 'loading'}
/>
</StyledPanel>
</Box>
)}
</>
);
Expand Down Expand Up @@ -419,3 +426,25 @@ const LoadingIndicator = ({ colSpan }: { colSpan: number }) => (
</tr>
</tfoot>
);

/**
* Returns pager position flags.
*
* If pagerPosition is not defined, it defaults to:
* - top pager only: if current dataLen < 5
* - both top and bottom pager if dataLen > 5
*/
export function getPagerPosition(
pagerPosition: PagerPosition,
dataLen: number
) {
const hasSufficientData = dataLen > 5;

const showBottomPager = pagerPosition === 'bottom';
const showTopPager =
pagerPosition === 'top' || (!pagerPosition && !hasSufficientData);
const showBothPager =
pagerPosition === 'both' || (!pagerPosition && hasSufficientData);

return { showBothPager, showBottomPager, showTopPager };
}
12 changes: 11 additions & 1 deletion web/packages/design/src/DataTable/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,19 @@ type TableColumnBase<T> = {
isNonRender?: boolean;
};

export type PagerPosition = 'top' | 'bottom' | 'both';

export type PaginationConfig<T> = {
pageSize?: number;
pagerPosition?: 'top' | 'bottom';
/**
* "undefined" will show both pagers if data on current page is some
* sufficient length.
*
* Otherwise, it will only show the top pager.
*
* "both" will show both regardless of data length.
*/
pagerPosition?: PagerPosition;
CustomTable?: (p: PagedTableProps<T>) => JSX.Element;
};

Expand Down
30 changes: 24 additions & 6 deletions web/packages/design/src/DataTable/useTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export default function useTable<T>({
? {
paginatedData: paginateData(data, pagination.pageSize),
currentPage: 0,
pagerPosition: pagination.pagerPosition || 'top',
pagerPosition: pagination.pagerPosition,
pageSize: pagination.pageSize || 15,
CustomTable: pagination.CustomTable,
}
Expand All @@ -87,7 +87,11 @@ export default function useTable<T>({
return false;
}

const updateData = (sort: typeof state.sort, searchValue: string) => {
const updateData = (
sort: typeof state.sort,
searchValue: string,
resetCurrentPage = false
) => {
// Don't do clientside sorting and filtering if serversideProps are defined
const sortedAndFiltered = serversideProps
? data
Expand All @@ -101,15 +105,28 @@ export default function useTable<T>({
showFirst
);
if (pagination && !serversideProps) {
const pages = paginateData(sortedAndFiltered, pagination.pageSize);
// Preserve the current page, instead of resetting it every data update.
// The currentPage index can be out of range if data were deleted
// from the original data. If that were the case, reset the currentPage
// to the last page.
let currentPage = state.pagination.currentPage;
if (resetCurrentPage) {
// Resetting the current page is desirable when user is newly sorting
// or entered a new search.
currentPage = 0;
} else if (currentPage && pages.length > 0 && !pages[currentPage]) {
currentPage = pages.length - 1;
}
setState({
...state,
sort,
searchValue,
data: sortedAndFiltered,
pagination: {
...state.pagination,
currentPage: 0,
paginatedData: paginateData(sortedAndFiltered, pagination.pageSize),
currentPage,
paginatedData: pages,
},
});
} else {
Expand Down Expand Up @@ -137,12 +154,13 @@ export default function useTable<T>({
onSort: column.onSort,
dir: state.sort?.dir === 'ASC' ? 'DESC' : 'ASC',
},
state.searchValue
state.searchValue,
true /* resetCurrentPage */
);
}

function setSearchValue(searchValue: string) {
updateData(state.sort, searchValue);
updateData(state.sort, searchValue, true /* resetCurrentPage */);
}

function nextPage() {
Expand Down
Loading

0 comments on commit 220552d

Please sign in to comment.