Skip to content

Commit

Permalink
add main Table component
Browse files Browse the repository at this point in the history
  • Loading branch information
notsidney committed May 19, 2022
1 parent 007a4f8 commit 8fb9a47
Show file tree
Hide file tree
Showing 53 changed files with 3,567 additions and 49 deletions.
11 changes: 11 additions & 0 deletions craco.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,15 @@ module.exports = {
return jestConfig;
},
},
webpack: {
configure: {
resolve: {
// Need to add polyfill for csv-parse
fallback: {
stream: require.resolve("stream-browserify"),
buffer: require.resolve("buffer"),
},
},
},
},
};
2 changes: 1 addition & 1 deletion emulators/auth_export/accounts.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"kind":"identitytoolkit#DownloadAccountResponse","users":[{"localId":"26CJMrwlouNRwkiLofNK07DNgKhw","createdAt":"1651022832613","lastLoginAt":"1652237658250","displayName":"Admin User","photoUrl":"","passwordHash":"fakeHash:salt=fakeSaltWjasmDYtQJU3vEm0cdg9:password=adminUser","salt":"fakeSaltWjasmDYtQJU3vEm0cdg9","passwordUpdatedAt":1652169642047,"customAttributes":"{\"roles\": [\"ADMIN\"]}","providerUserInfo":[{"providerId":"google.com","rawId":"abc123","federatedId":"abc123","displayName":"Admin User","email":"[email protected]"},{"providerId":"password","email":"[email protected]","federatedId":"[email protected]","rawId":"[email protected]","displayName":"Admin User","photoUrl":""}],"validSince":"1652169642","email":"[email protected]","emailVerified":true,"disabled":false,"lastRefreshAt":"2022-05-11T02:54:18.250Z"},{"localId":"3xTRVPnJGT2GE6lkiWKZp1jShuXj","createdAt":"1651023059442","lastLoginAt":"1651727720399","displayName":"Editor User","photoUrl":"","passwordHash":"fakeHash:salt=fakeSaltmNJg6BtcsUfyZKz6wZQY:password=editorUser","salt":"fakeSaltmNJg6BtcsUfyZKz6wZQY","passwordUpdatedAt":1652169642047,"providerUserInfo":[{"providerId":"google.com","rawId":"1535779573397289142795231390488730790451","federatedId":"1535779573397289142795231390488730790451","displayName":"Editor User","email":"[email protected]"},{"providerId":"password","email":"[email protected]","federatedId":"[email protected]","rawId":"[email protected]","displayName":"Editor User","photoUrl":""}],"validSince":"1652169642","email":"[email protected]","emailVerified":true,"disabled":false}]}
{"kind":"identitytoolkit#DownloadAccountResponse","users":[{"localId":"26CJMrwlouNRwkiLofNK07DNgKhw","createdAt":"1651022832613","lastLoginAt":"1652766366467","displayName":"Admin User","photoUrl":"","passwordHash":"fakeHash:salt=fakeSaltWjasmDYtQJU3vEm0cdg9:password=adminUser","salt":"fakeSaltWjasmDYtQJU3vEm0cdg9","passwordUpdatedAt":1652670601103,"customAttributes":"{\"roles\": [\"ADMIN\"]}","providerUserInfo":[{"providerId":"google.com","rawId":"abc123","federatedId":"abc123","displayName":"Admin User","email":"[email protected]"},{"providerId":"password","email":"[email protected]","federatedId":"[email protected]","rawId":"[email protected]","displayName":"Admin User","photoUrl":""}],"validSince":"1652670601","email":"[email protected]","emailVerified":true,"disabled":false,"lastRefreshAt":"2022-05-19T05:13:45.818Z"},{"localId":"3xTRVPnJGT2GE6lkiWKZp1jShuXj","createdAt":"1651023059442","lastLoginAt":"1651727720399","displayName":"Editor User","photoUrl":"","passwordHash":"fakeHash:salt=fakeSaltmNJg6BtcsUfyZKz6wZQY:password=editorUser","salt":"fakeSaltmNJg6BtcsUfyZKz6wZQY","passwordUpdatedAt":1652670601104,"providerUserInfo":[{"providerId":"google.com","rawId":"1535779573397289142795231390488730790451","federatedId":"1535779573397289142795231390488730790451","displayName":"Editor User","email":"[email protected]"},{"providerId":"password","email":"[email protected]","federatedId":"[email protected]","rawId":"[email protected]","displayName":"Editor User","photoUrl":""}],"validSince":"1652670601","email":"[email protected]","emailVerified":true,"disabled":false}]}
Binary file not shown.
Binary file modified emulators/firestore_export/all_namespaces/all_kinds/output-0
Binary file not shown.
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
"@mui/styles": "^5.6.2",
"@rowy/form-builder": "^0.6.1",
"@rowy/multiselect": "^0.3.0",
"buffer": "^6.0.3",
"compare-versions": "^4.1.3",
"csv-parse": "^5.0.4",
"date-fns": "^2.28.0",
"dompurify": "^2.3.6",
"firebase": "^9.6.11",
Expand All @@ -34,7 +36,10 @@
"react-color-palette": "^6.2.0",
"react-data-grid": "7.0.0-beta.5",
"react-div-100vh": "^0.7.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^18.0.0",
"react-dropzone": "^10",
"react-element-scroll-hook": "^1.1.0",
"react-error-boundary": "^3.1.4",
"react-helmet-async": "^1.3.0",
Expand All @@ -44,6 +49,7 @@
"react-router-hash-link": "^2.4.3",
"react-scripts": "^5.0.0",
"remark-gfm": "^3.0.1",
"stream-browserify": "^3.0.0",
"swr": "^1.3.0",
"tss-react": "^3.6.2",
"typescript": "^4.6.3",
Expand Down
4 changes: 2 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const TableSettingsDialog = lazy(() => import("@src/components/TableSettingsDial
// prettier-ignore
const TablesPage = lazy(() => import("@src/pages/Tables" /* webpackChunkName: "TablesPage" */));
// prettier-ignore
const TablePage = lazy(() => import("@src/pages/TableTest" /* webpackChunkName: "TablePage" */));
const TablePage = lazy(() => import("@src/pages/Table" /* webpackChunkName: "TablePage" */));

// prettier-ignore
const UserSettingsPage = lazy(() => import("@src/pages/Settings/UserSettings" /* webpackChunkName: "UserSettingsPage" */));
Expand Down Expand Up @@ -119,7 +119,7 @@ export default function App() {
/>
{/* <Route path={ROUTES.rowyRunTest} element={<RowyRunTestPage />} /> */}

<Route path="/jotaiTest" element={<JotaiTestPage />} />
<Route path="/test/jotai" element={<JotaiTestPage />} />
</Route>

<Route path={ROUTES.themeTest} element={<ThemeTestPage />} />
Expand Down
15 changes: 15 additions & 0 deletions src/atoms/globalScope/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,18 @@ export const tableSettingsDialogSchemaAtom = atom(async (get) => {
if (!tableId || !getTableSchema) return {} as TableSchema;
return getTableSchema(tableId);
});

/** Persist the state of the add row ID type */
export const tableAddRowIdTypeAtom = atomWithStorage<
"decrement" | "random" | "custom"
>("__ROWY__ADD_ROW_ID_TYPE", "decrement");
/** Persist when the user dismissed the row out of order warning */
export const tableOutOfOrderDismissedAtom = atomWithStorage(
"__ROWY__OUT_OF_ORDER_TOOLTIP_DISMISSED",
false
);
/** Store tables where user has dismissed the description tooltip */
export const tableDescriptionDismissedAtom = atomWithStorage<string[]>(
"__ROWY__TABLE_DESCRIPTION_DISMISSED",
[]
);
24 changes: 6 additions & 18 deletions src/atoms/tableScope/columnActions.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,13 @@
import { atom } from "jotai";
import { orderBy, findIndex } from "lodash-es";
import { findIndex } from "lodash-es";

import { tableSchemaAtom, updateTableSchemaAtom } from "./table";
import {
tableColumnsOrderedAtom,
tableColumnsReducer,
updateTableSchemaAtom,
} from "./table";
import { ColumnConfig } from "@src/types/table";

/** Store the table columns as an ordered array */
export const tableColumnsOrderedAtom = atom<ColumnConfig[]>((get) => {
const tableSchema = get(tableSchemaAtom);
if (!tableSchema || !tableSchema.columns) return [];
return orderBy(Object.values(tableSchema?.columns ?? {}), "index");
});
/** Reducer function to convert from array of columns to columns object */
export const tableColumnsReducer = (
a: Record<string, ColumnConfig>,
c: ColumnConfig,
index: number
) => {
a[c.key] = { ...c, index };
return a;
};

export interface IAddColumnOptions {
/** Column config to add. `config.index` is ignored */
config: Omit<ColumnConfig, "index">;
Expand Down
3 changes: 3 additions & 0 deletions src/atoms/tableScope/rowActions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,9 @@ describe("addRow", () => {
});
});

// TODO: NESTED FIELDS TESTS
// TODO: TEST _rowy_* fields are removed

describe("multiple", () => {
test("adds multiple rows with pre-defined id", async () => {
initRows(generatedRows);
Expand Down
4 changes: 2 additions & 2 deletions src/atoms/tableScope/rowActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import { currentUserAtom } from "@src/atoms/globalScope";
import {
auditChangeAtom,
tableSettingsAtom,
tableColumnsOrderedAtom,
tableFiltersAtom,
tableRowsLocalAtom,
tableRowsAtom,
_updateRowDbAtom,
_deleteRowDbAtom,
} from "./table";
import { tableColumnsOrderedAtom } from "./columnActions";
import { TableRow } from "@src/types/table";
import {
rowyUser,
Expand Down Expand Up @@ -216,7 +216,7 @@ export interface IUpdateFieldOptions {
* Updates or deletes a field in a row.
* Adds to rowsDb if it has no missing required fields,
* otherwise keeps in rowsLocal.
* @param options - {@link IAddRowOptions}
* @param options - {@link IUpdateFieldOptions}
*
* @example Basic usage:
* ```
Expand Down
36 changes: 33 additions & 3 deletions src/atoms/tableScope/table.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { atom } from "jotai";
import { atomWithReducer } from "jotai/utils";
import { uniqBy, sortBy, findIndex, cloneDeep, unset } from "lodash-es";
import {
uniqBy,
sortBy,
findIndex,
cloneDeep,
unset,
orderBy,
} from "lodash-es";

import {
TableSettings,
TableSchema,
ColumnConfig,
TableFilter,
TableOrder,
TableRow,
Expand All @@ -15,15 +23,37 @@ import {
import { updateRowData } from "@src/utils/table";

/** Root atom from which others are derived */
export const tableIdAtom = atom<string | undefined>(undefined);
export const tableIdAtom = atom("");
/** Store tableSettings from project settings document */
export const tableSettingsAtom = atom<TableSettings | undefined>(undefined);
export const tableSettingsAtom = atom<TableSettings>({
id: "",
collection: "",
name: "",
roles: [],
section: "",
tableType: "primaryCollection",
});
/** Store tableSchema from schema document */
export const tableSchemaAtom = atom<TableSchema>({});
/** Store function to update tableSchema */
export const updateTableSchemaAtom = atom<
UpdateDocFunction<TableSchema> | undefined
>(undefined);
/** Store the table columns as an ordered array */
export const tableColumnsOrderedAtom = atom<ColumnConfig[]>((get) => {
const tableSchema = get(tableSchemaAtom);
if (!tableSchema || !tableSchema.columns) return [];
return orderBy(Object.values(tableSchema?.columns ?? {}), "index");
});
/** Reducer function to convert from array of columns to columns object */
export const tableColumnsReducer = (
a: Record<string, ColumnConfig>,
c: ColumnConfig,
index: number
) => {
a[c.key] = { ...c, index };
return a;
};

/** Filters applied to the local view */
export const tableFiltersAtom = atom<TableFilter[]>([]);
Expand Down
60 changes: 60 additions & 0 deletions src/components/ButtonWithStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, { forwardRef } from "react";

import { Button, ButtonProps } from "@mui/material";
import { alpha } from "@mui/material/styles";

export interface IButtonWithStatusProps extends ButtonProps {
active?: boolean;
}

export const ButtonWithStatus = forwardRef(function ButtonWithStatus_(
{ active = false, className, ...props }: IButtonWithStatusProps,
ref: React.Ref<HTMLButtonElement>
) {
return (
<Button
{...props}
ref={ref}
variant="outlined"
color={active ? "primary" : "secondary"}
sx={[
{
position: "relative",
zIndex: 1,
},
active
? {
color: (theme) =>
theme.palette.mode === "dark"
? theme.palette.primary.light
: theme.palette.primary.dark,
backgroundColor: (theme) =>
alpha(
theme.palette.primary.main,
theme.palette.action.selectedOpacity
),
borderColor: "primary.main",

"&:hover": {
color: (theme) =>
theme.palette.mode === "dark"
? theme.palette.primary.light
: theme.palette.primary.dark,
backgroundColor: (theme) =>
alpha(
theme.palette.mode === "dark"
? theme.palette.primary.light
: theme.palette.primary.dark,
theme.palette.action.selectedOpacity +
theme.palette.action.hoverOpacity
),
borderColor: "currentColor",
},
}
: {},
]}
/>
);
});

export default ButtonWithStatus;
10 changes: 9 additions & 1 deletion src/components/ErrorFallback.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState, useEffect } from "react";
import { FallbackProps } from "react-error-boundary";
import { Link } from "react-router-dom";
import { useLocation, Link } from "react-router-dom";

import { Button } from "@mui/material";
import ReloadIcon from "@mui/icons-material/Refresh";
Expand All @@ -20,6 +21,13 @@ export default function ErrorFallback({
resetErrorBoundary,
...props
}: IErrorFallbackProps) {
// Reset error boundary when navigating away from the page
const location = useLocation();
const [errorPathname] = useState(location.pathname);
useEffect(() => {
if (errorPathname !== location.pathname) resetErrorBoundary();
}, [errorPathname, location.pathname, resetErrorBoundary]);

if ((error as any).code === "permission-denied")
return (
<AccessDenied error={error} resetErrorBoundary={resetErrorBoundary} />
Expand Down
Loading

0 comments on commit 8fb9a47

Please sign in to comment.