From 625793ffbad7cba27e188bcdee85c0aaae330f46 Mon Sep 17 00:00:00 2001 From: PantheRedEye Date: Sat, 23 Nov 2024 14:55:45 -0600 Subject: [PATCH] Sites list table --- web/src/Routes.tsx | 1 + .../SitesListCell/SitesListCell.mock.ts | 7 ++ .../SitesListCell/SitesListCell.stories.tsx | 35 ++++++++ .../SitesListCell/SitesListCell.test.tsx | 42 +++++++++ .../SitesListCell/SitesListCell.tsx | 58 ++++++++++++ .../SitesTable/SitesTable.stories.tsx | 26 ++++++ .../components/SitesTable/SitesTable.test.tsx | 14 +++ web/src/components/SitesTable/SitesTable.tsx | 89 +++++++++++++++++++ web/src/components/SitesTable/columns.tsx | 88 ++++++++++++++++++ web/src/pages/SitesPage/SitesPage.stories.tsx | 13 +++ web/src/pages/SitesPage/SitesPage.test.tsx | 14 +++ web/src/pages/SitesPage/SitesPage.tsx | 15 ++++ 12 files changed, 402 insertions(+) create mode 100644 web/src/components/SitesListCell/SitesListCell.mock.ts create mode 100644 web/src/components/SitesListCell/SitesListCell.stories.tsx create mode 100644 web/src/components/SitesListCell/SitesListCell.test.tsx create mode 100644 web/src/components/SitesListCell/SitesListCell.tsx create mode 100644 web/src/components/SitesTable/SitesTable.stories.tsx create mode 100644 web/src/components/SitesTable/SitesTable.test.tsx create mode 100644 web/src/components/SitesTable/SitesTable.tsx create mode 100644 web/src/components/SitesTable/columns.tsx create mode 100644 web/src/pages/SitesPage/SitesPage.stories.tsx create mode 100644 web/src/pages/SitesPage/SitesPage.test.tsx create mode 100644 web/src/pages/SitesPage/SitesPage.tsx diff --git a/web/src/Routes.tsx b/web/src/Routes.tsx index 68b105c..de33537 100644 --- a/web/src/Routes.tsx +++ b/web/src/Routes.tsx @@ -19,6 +19,7 @@ const Routes = () => { + diff --git a/web/src/components/SitesListCell/SitesListCell.mock.ts b/web/src/components/SitesListCell/SitesListCell.mock.ts new file mode 100644 index 0000000..da5b955 --- /dev/null +++ b/web/src/components/SitesListCell/SitesListCell.mock.ts @@ -0,0 +1,7 @@ +// Define your own mock data here: +export const standard = (/* vars, { ctx, req } */) => ({ + sitesList: { + __typename: "sitesList" as const, + id: 42, + }, +}); diff --git a/web/src/components/SitesListCell/SitesListCell.stories.tsx b/web/src/components/SitesListCell/SitesListCell.stories.tsx new file mode 100644 index 0000000..0a85b8c --- /dev/null +++ b/web/src/components/SitesListCell/SitesListCell.stories.tsx @@ -0,0 +1,35 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { Loading, Empty, Failure, Success } from "./SitesListCell"; +import { standard } from "./SitesListCell.mock"; + +const meta: Meta = { + title: "Cells/SitesListCell", + tags: ["autodocs"], +}; + +export default meta; + +export const loading: StoryObj = { + render: () => { + return Loading ? : <>; + }, +}; + +export const empty: StoryObj = { + render: () => { + return Empty ? : <>; + }, +}; + +export const failure: StoryObj = { + render: (args) => { + return Failure ? : <>; + }, +}; + +export const success: StoryObj = { + render: (args) => { + return Success ? : <>; + }, +}; diff --git a/web/src/components/SitesListCell/SitesListCell.test.tsx b/web/src/components/SitesListCell/SitesListCell.test.tsx new file mode 100644 index 0000000..03ac04d --- /dev/null +++ b/web/src/components/SitesListCell/SitesListCell.test.tsx @@ -0,0 +1,42 @@ +import { render } from "@redwoodjs/testing/web"; + +import { Loading, Empty, Failure, Success } from "./SitesListCell"; +import { standard } from "./SitesListCell.mock"; + +// Generated boilerplate tests do not account for all circumstances +// and can fail without adjustments, e.g. Float and DateTime types. +// Please refer to the RedwoodJS Testing Docs: +// https://redwoodjs.com/docs/testing#testing-cells +// https://redwoodjs.com/docs/testing#jest-expect-type-considerations + +describe("SitesListCell", () => { + it("renders Loading successfully", () => { + expect(() => { + render(); + }).not.toThrow(); + }); + + it("renders Empty successfully", async () => { + expect(() => { + render(); + }).not.toThrow(); + }); + + it("renders Failure successfully", async () => { + expect(() => { + render(); + }).not.toThrow(); + }); + + // When you're ready to test the actual output of your component render + // you could test that, for example, certain text is present: + // + // 1. import { screen } from '@redwoodjs/testing/web' + // 2. Add test: expect(screen.getByText('Hello, world')).toBeInTheDocument() + + it("renders Success successfully", async () => { + expect(() => { + render(); + }).not.toThrow(); + }); +}); diff --git a/web/src/components/SitesListCell/SitesListCell.tsx b/web/src/components/SitesListCell/SitesListCell.tsx new file mode 100644 index 0000000..68f4ce9 --- /dev/null +++ b/web/src/components/SitesListCell/SitesListCell.tsx @@ -0,0 +1,58 @@ +import type { + FindSitesListQuery, + FindSitesListQueryVariables, +} from 'types/graphql' + +import type { + CellSuccessProps, + CellFailureProps, + TypedDocumentNode, +} from '@redwoodjs/web' + +import { columns } from 'src/components/SitesTable/columns' +import DataTable from 'src/components/SitesTable/SitesTable' +import { Link, routes } from '@redwoodjs/router' + +export const QUERY: TypedDocumentNode< + FindSitesListQuery, + FindSitesListQueryVariables +> = gql` + query FindSitesListQuery { + sites: sites { + id + name + ownerName + addressLine1 + } + } +` + +export const Loading = () =>
Loading...
+ +export const Empty = () =>
Empty
+ +export const Failure = ({ + error, +}: CellFailureProps) => ( +
Error: {error?.message}
+) + +export const Success = ({ + sites, +}: CellSuccessProps< + FindSitesListQuery, + FindSitesListQueryVariables +>) => { + return ( +
+ + + Add Site + + +
+ ) +} diff --git a/web/src/components/SitesTable/SitesTable.stories.tsx b/web/src/components/SitesTable/SitesTable.stories.tsx new file mode 100644 index 0000000..130eb8d --- /dev/null +++ b/web/src/components/SitesTable/SitesTable.stories.tsx @@ -0,0 +1,26 @@ +// Pass props to your component by passing an `args` object to your story +// +// ```tsx +// export const Primary: Story = { +// args: { +// propName: propValue +// } +// } +// ``` +// +// See https://storybook.js.org/docs/react/writing-stories/args. + +import type { Meta, StoryObj } from '@storybook/react' + +import SitesTable from './SitesTable' + +const meta: Meta = { + component: SitesTable, + tags: ['autodocs'], +} + +export default meta + +type Story = StoryObj + +export const Primary: Story = {} diff --git a/web/src/components/SitesTable/SitesTable.test.tsx b/web/src/components/SitesTable/SitesTable.test.tsx new file mode 100644 index 0000000..f3c971d --- /dev/null +++ b/web/src/components/SitesTable/SitesTable.test.tsx @@ -0,0 +1,14 @@ +import { render } from '@redwoodjs/testing/web' + +import SitesTable from './SitesTable' + +// Improve this test with help from the Redwood Testing Doc: +// https://redwoodjs.com/docs/testing#testing-components + +describe('SitesTable', () => { + it('renders successfully', () => { + expect(() => { + render() + }).not.toThrow() + }) +}) diff --git a/web/src/components/SitesTable/SitesTable.tsx b/web/src/components/SitesTable/SitesTable.tsx new file mode 100644 index 0000000..19d0070 --- /dev/null +++ b/web/src/components/SitesTable/SitesTable.tsx @@ -0,0 +1,89 @@ +'use client' + +import { + ColumnDef, + flexRender, + getCoreRowModel, + useReactTable, +} from '@tanstack/react-table' + +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from 'src/components/ui/Table' + +interface DataTableProps { + columns: ColumnDef[] + data: TData[] +} + +const SitesTable = ({ + columns, + data, +}: DataTableProps) => { + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + }) + + return ( +
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ) + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+ ) +} + +export default SitesTable diff --git a/web/src/components/SitesTable/columns.tsx b/web/src/components/SitesTable/columns.tsx new file mode 100644 index 0000000..7e38875 --- /dev/null +++ b/web/src/components/SitesTable/columns.tsx @@ -0,0 +1,88 @@ +import React from 'react' + +import { ColumnDef } from '@tanstack/react-table' +import { MoreHorizontal } from 'lucide-react' + +import { navigate, routes } from '@redwoodjs/router' + +import { Button } from 'src/components/ui/Button' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from 'src/components/ui/DropdownMenu' + +import ExportPDFButton from '../ExportPDFButton/ExportPDFButton' + +export type Site = { + id: number + name: string + ownerName: string + addressLine1: string +} + +export const columns: ColumnDef[] = [ + { + accessorKey: 'site.name', + header: 'Site', + }, + { + accessorKey: 'name', + header: 'Site Name', + }, + { + accessorKey: 'ownerName', + header: 'Owner Name', + }, + + { + accessorKey: 'addressLine1', + header: 'Address', + }, + { + id: 'actions', + cell: ({ row }) => { + const inspection = row.original + + return ( + + + + + + Actions + + navigator.clipboard.writeText(inspection.id.toString()) + } + > + Create New Inspection + + + navigate(routes.viewInspection({ id: inspection.id })) + } + > + View All Inspections + + + + + navigate(routes.viewInspection({ id: inspection.id })) + } + > + Edit Site + + + + ) + }, + }, +] diff --git a/web/src/pages/SitesPage/SitesPage.stories.tsx b/web/src/pages/SitesPage/SitesPage.stories.tsx new file mode 100644 index 0000000..197696c --- /dev/null +++ b/web/src/pages/SitesPage/SitesPage.stories.tsx @@ -0,0 +1,13 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import SitesPage from "./SitesPage"; + +const meta: Meta = { + component: SitesPage, +}; + +export default meta; + +type Story = StoryObj; + +export const Primary: Story = {}; diff --git a/web/src/pages/SitesPage/SitesPage.test.tsx b/web/src/pages/SitesPage/SitesPage.test.tsx new file mode 100644 index 0000000..d2b75ce --- /dev/null +++ b/web/src/pages/SitesPage/SitesPage.test.tsx @@ -0,0 +1,14 @@ +import { render } from "@redwoodjs/testing/web"; + +import SitesPage from "./SitesPage"; + +// Improve this test with help from the Redwood Testing Doc: +// https://redwoodjs.com/docs/testing#testing-pages-layouts + +describe("SitesPage", () => { + it("renders successfully", () => { + expect(() => { + render(); + }).not.toThrow(); + }); +}); diff --git a/web/src/pages/SitesPage/SitesPage.tsx b/web/src/pages/SitesPage/SitesPage.tsx new file mode 100644 index 0000000..13d60a0 --- /dev/null +++ b/web/src/pages/SitesPage/SitesPage.tsx @@ -0,0 +1,15 @@ +import { Metadata } from '@redwoodjs/web' + +import SitesListCell from 'src/components/SitesListCell' + +const SitesPage = () => { + return ( + <> + + + + + ) +} + +export default SitesPage