Skip to content

Commit

Permalink
Fix query table (#76)
Browse files Browse the repository at this point in the history
* fix: QueryTable margin and add refresh button

* feat: auto-select QueryTable comparator filter

* feat: add refreshRef for QueryTable
  • Loading branch information
bestickley authored May 6, 2022
1 parent 2a5ef75 commit dac8635
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 20 deletions.
5 changes: 5 additions & 0 deletions .changeset/angry-cows-leave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"gboost-ui": patch
---

Auto select comparator if only one in QueryTable Filter
5 changes: 5 additions & 0 deletions .changeset/light-dryers-eat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"gboost-ui": patch
---

Fix margin between bottom of table and pagination
5 changes: 5 additions & 0 deletions .changeset/long-wombats-greet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"gboost-ui": minor
---

Add refreshRef to QueryTable to allow manual refreshing
5 changes: 5 additions & 0 deletions .changeset/selfish-geese-add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"gboost-ui": minor
---

Add refresh button on QueryTable action bar
16 changes: 14 additions & 2 deletions packages/gboost-ui/src/QueryTable/ActionBar/ActionBar.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { ReactElement, RefObject, useMemo } from "react";
import { Heading } from "@aws-amplify/ui-react";
import { MutableRefObject, ReactElement, RefObject, useMemo } from "react";
import { Button, Heading, Icon } from "@aws-amplify/ui-react";
import { Box } from "../../Box.js";
import { Column } from "../QueryTable.js";
import { DownloadAction } from "./DownloadAction.js";
import { FilterAction, InternalFilter } from "./FilterAction/FilterAction.js";
import { ColumnVisibilityAction } from "./ColumnVisibilityAction.js";
import { Density, DensityAction } from "./DensityAction.js";
import { MdRefresh } from "react-icons/md";

interface ActionBarProps<T> {
columns: Column<T>[];
columnVisibility: Record<string, boolean>;
density: Density;
disableMultiFilter: boolean;
disableRefresh: boolean;
download: boolean;
downloadFileName: string;
filters: InternalFilter[];
Expand All @@ -20,6 +22,8 @@ interface ActionBarProps<T> {
onChangeColumnVisibility: (columnVisibility: Record<string, boolean>) => void;
onChangeDensity: (density: Density) => void;
onFilter: (filters: InternalFilter[]) => void;
onRefresh: () => void;
refreshRef?: MutableRefObject<HTMLButtonElement | null>;
rows: Record<string, string>[];
ActionMenu?: ReactElement;
}
Expand All @@ -33,6 +37,7 @@ export function ActionBar<T>(props: ActionBarProps<T>): ReactElement {
columnVisibility,
density,
disableMultiFilter,
disableRefresh,
download,
downloadFileName,
filters,
Expand All @@ -41,6 +46,8 @@ export function ActionBar<T>(props: ActionBarProps<T>): ReactElement {
onChangeColumnVisibility: handleChangeColumnVisibility,
onChangeDensity: handleChangeDensity,
onFilter,
onRefresh,
refreshRef,
rows,
ActionMenu,
} = props;
Expand All @@ -59,6 +66,11 @@ export function ActionBar<T>(props: ActionBarProps<T>): ReactElement {
>
{heading ? <Heading level={3}>{heading}</Heading> : <Heading />}
<Box css={{ display: "flex", gap: "$2" }}>
{!disableRefresh && (
<Button ref={refreshRef} size="large" onClick={onRefresh}>
<Icon ariaLabel="columns" as={MdRefresh} />
</Button>
)}
{filterColumns.length && (
<FilterAction
disableMultiFilter={disableMultiFilter}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { ChangeEventHandler, ReactElement, useCallback, useState } from "react";
import {
ChangeEventHandler,
ReactElement,
useCallback,
useEffect,
useState,
} from "react";
import { Button, Icon, SelectField } from "@aws-amplify/ui-react";
import { MdCheck, MdDelete } from "react-icons/md";
import { FilterValue as FilterValueComponent } from "./FilterValue.js";
Expand Down Expand Up @@ -34,14 +40,21 @@ export function FilterRow({
const handleChangeColumn: ChangeEventHandler<HTMLSelectElement> = useCallback(
(e) => {
setDirty(true);
handleUpdateFilter(id, {
const newFilter: InternalFilter = {
...filter,
column: e.target.value,
comparator: "",
value: "",
});
};
const newComparators =
filterColumnsObj[e.target.value]?.filterOptions?.comparators || [];
// if there is only 1 comparator for the column, pre-select it for user
if (!filter.comparator && newComparators.length === 1) {
newFilter.comparator = newComparators[0].value;
}
handleUpdateFilter(id, newFilter);
},
[filter, handleUpdateFilter, id]
[filter, filterColumnsObj, handleUpdateFilter, id]
);
const handleChangeComparator: ChangeEventHandler<HTMLSelectElement> =
useCallback(
Expand All @@ -67,6 +80,16 @@ export function FilterRow({
handleUpdateFilter(id, { ...filter, value });
setDirty(false);
}, [filter, handleUpdateFilter, id, value]);
useEffect(() => {
// if only 1 comparator option, pre-select it
if (!filter.comparator && filterOptions?.comparators.length === 1) {
handleUpdateFilter(id, {
...filter,
comparator: filterOptions?.comparators[0].value,
value: "",
});
}
}, [filter, filterOptions?.comparators, handleUpdateFilter, id]);
return (
<>
<SelectField
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
ChangeEventHandler,
ReactElement,
useCallback,
useLayoutEffect,
Expand Down Expand Up @@ -43,13 +44,31 @@ export function NewFilterRow({
onCreateFilter({ ...filter, id: randomId() });
setFilter(initFilter);
}, [filter, onCreateFilter]);
const handleChangeColumn: ChangeEventHandler<HTMLSelectElement> = useCallback(
(e) => {
setFilter((f) => {
const newFilter: InternalFilter = {
...filter,
column: e.target.value,
};
const newComparators =
filterColumnsObj[e.target.value]?.filterOptions?.comparators || [];
// if there is only 1 comparator for the column, pre-select it for user
if (!filter.comparator && newComparators.length === 1) {
newFilter.comparator = newComparators[0].value;
}
return newFilter;
});
},
[filter, filterColumnsObj]
);
return (
<>
<SelectField
ref={columnRef}
label="Column"
labelHidden
onChange={(e) => setFilter((f) => ({ ...f, column: e.target.value }))}
onChange={handleChangeColumn}
placeholder="Column"
value={filter.column}
>
Expand Down
53 changes: 40 additions & 13 deletions packages/gboost-ui/src/QueryTable/QueryTable.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
MutableRefObject,
ReactElement,
Reducer,
useCallback,
Expand Down Expand Up @@ -76,6 +77,10 @@ export interface OnQueryParams {
nextToken: string;
pageSize: number;
sorts: Sort[];
/**
* True if query was invoked by refresh
*/
refresh: boolean;
}
type OnQuerySuccessReturnValue = {
rows: Row[];
Expand All @@ -90,11 +95,6 @@ export type OnQueryReturnValue =
type SelectAction = "select" | "unselect";
interface QueryTableProps<T> {
columns: Column<T>[];
/**
* Number of placeholder rows that show when loading
* @default 10
*/
countLoadingRows?: number;
/**
* @default false
*/
Expand All @@ -103,6 +103,11 @@ interface QueryTableProps<T> {
* @default false
*/
disableMultiFilter?: boolean;
/**
* Removes refresh button in QueryTable's Action Bar
* @default false
*/
disableRefresh?: boolean;
/**
* Enable CSV file download
* @default false
Expand Down Expand Up @@ -173,6 +178,10 @@ interface QueryTableProps<T> {
* Function called upon update to selected rows
*/
onSelect?: (action: SelectAction, rows: T[], selected: T[]) => void;
/**
* Ref to enable manually refreshing with refreshRef.click()
*/
refreshRef?: MutableRefObject<HTMLButtonElement | null>;
/**
* Action Button Component placed on top right of table, often used for creating a row or
* displaying an actions menu button for user to perform actions on selected
Expand Down Expand Up @@ -203,6 +212,7 @@ interface TableState<T> {
pageSize: number;
pageSizeOptions: number[];
prevTokens: string[];
refresh: boolean; // if true, indicates query is from refresh
rows: T[];
selected: T[];
sorts: Sort[];
Expand Down Expand Up @@ -235,7 +245,12 @@ function tableReducer<T>(
case "changeDensity":
return { ...state, density: action.density };
case "changeError":
return { ...state, loading: false, errorMessage: action.message };
return {
...state,
loading: false,
errorMessage: action.message,
refresh: false,
};
case "changeLoading":
return { ...state, loading: action.loading, errorMessage: "" };
case "changePage":
Expand Down Expand Up @@ -274,6 +289,7 @@ function tableReducer<T>(
rows: action.rows,
nextNextToken: action.nextNextToken,
loading: false,
refresh: false,
};
case "changeSelected":
return {
Expand All @@ -283,8 +299,7 @@ function tableReducer<T>(
case "filter":
return { ...state, filters: action.filters };
case "refresh":
// trigger useEffect to run again
return { ...state, filters: [...state.filters] };
return { ...state, refresh: true };
case "sort":
return { ...state, sorts: action.sorts };
default:
Expand All @@ -306,9 +321,9 @@ export function QueryTable<T extends Record<string, any>>(
): ReactElement {
const {
columns = [],
countLoadingRows = 10,
disableMultiSort = false,
disableMultiFilter = false,
disableRefresh = false,
download = false,
downloadFileName = "data.csv",
enableSelect = false,
Expand All @@ -325,6 +340,7 @@ export function QueryTable<T extends Record<string, any>>(
onQuery,
// eslint-disable-next-line @typescript-eslint/no-empty-function
onSelect = (s) => {},
refreshRef,
ActionButton,
tableProps,
} = props;
Expand All @@ -342,6 +358,7 @@ export function QueryTable<T extends Record<string, any>>(
pageSize,
pageSizeOptions,
prevTokens,
refresh,
rows,
selected,
sorts,
Expand All @@ -362,6 +379,7 @@ export function QueryTable<T extends Record<string, any>>(
pageSize: initPageSize,
pageSizeOptions: [10, 20, 50],
prevTokens: [],
refresh: false,
rows: [],
selected: initSelected,
sorts: initSorts,
Expand All @@ -379,6 +397,7 @@ export function QueryTable<T extends Record<string, any>>(
nextToken,
pageSize,
sorts,
refresh,
});
if ("rows" in res) {
dispatch({
Expand All @@ -398,7 +417,7 @@ export function QueryTable<T extends Record<string, any>>(
}
}
fetchData();
}, [filters, nextToken, onQuery, pageSize, sorts]);
}, [filters, nextToken, onQuery, pageSize, refresh, sorts]);
const handlePageChange = useCallback(async (newPage: number) => {
dispatch({ type: "changePage", page: newPage });
}, []);
Expand All @@ -412,7 +431,7 @@ export function QueryTable<T extends Record<string, any>>(
<Box
css={{ display: "flex", gap: "$1", flexDirection: "column", my: "$2" }}
>
{[...Array(countLoadingRows)].map((e, i) => (
{[...Array(pageSize)].map((e, i) => (
<StyledPlaceholder key={i} />
))}
</Box>
Expand Down Expand Up @@ -499,6 +518,7 @@ export function QueryTable<T extends Record<string, any>>(
columnVisibility={columnVisibility}
density={density}
disableMultiFilter={disableMultiFilter}
disableRefresh={disableRefresh}
download={download}
downloadFileName={downloadFileName}
filters={filters}
Expand All @@ -511,11 +531,19 @@ export function QueryTable<T extends Record<string, any>>(
dispatch({ type: "changeDensity", density })
}
onFilter={handleFilter}
onRefresh={() => dispatch({ type: "refresh" })}
refreshRef={refreshRef}
rows={rows}
ActionMenu={ActionButton}
/>
)}
<StyledTable {...tableProps} css={{ gridTemplateColumns }}>
<StyledTable
{...tableProps}
css={{
gridTemplateColumns,
mb: !loading ? 55 * (pageSize - rows.length) : undefined,
}}
>
<StyledTableHead>
<StyledTableRow>
{enableSelect && (
Expand Down Expand Up @@ -572,7 +600,6 @@ export function QueryTable<T extends Record<string, any>>(
{spanTableEl}
{!hidePagination && (
<Pagination
css={{ mt: 55 * countLoadingRows }}
disableNext={!nextNextToken}
disablePrev={!prevTokens.length}
onPageChange={handlePageChange}
Expand Down

0 comments on commit dac8635

Please sign in to comment.