Skip to content

Commit

Permalink
Sites list table
Browse files Browse the repository at this point in the history
  • Loading branch information
pantheredeye committed Nov 23, 2024
1 parent 4cfbaf2 commit 625793f
Show file tree
Hide file tree
Showing 12 changed files with 402 additions and 0 deletions.
1 change: 1 addition & 0 deletions web/src/Routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const Routes = () => {
<Route path="/profile/{id}" page={ProfilePage} name="profile" />
<Route path="/view-inspection" page={ViewInspectionPage} name="viewInspection" />
<Route path="/inspections" page={InspectionsPage} name="inspections" />
<Route path="/sites" page={SitesPage} name="sites" />
<Route path="/bmps" page={StandardBMPSettingsPage} name="bmps" />
<Route path="/new-inspection" page={NewInspectionPage} name="newInspection" />
<Route path="/new-site" page={NewSitePage} name="newSite" />
Expand Down
7 changes: 7 additions & 0 deletions web/src/components/SitesListCell/SitesListCell.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Define your own mock data here:
export const standard = (/* vars, { ctx, req } */) => ({
sitesList: {
__typename: "sitesList" as const,

Check warning on line 4 in web/src/components/SitesListCell/SitesListCell.mock.ts

View workflow job for this annotation

GitHub Actions / lint-and-test

Replace `"sitesList"` with `'sitesList'`
id: 42,
},
});

Check warning on line 7 in web/src/components/SitesListCell/SitesListCell.mock.ts

View workflow job for this annotation

GitHub Actions / lint-and-test

Delete `;`
35 changes: 35 additions & 0 deletions web/src/components/SitesListCell/SitesListCell.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { Meta, StoryObj } from "@storybook/react";

Check warning on line 1 in web/src/components/SitesListCell/SitesListCell.stories.tsx

View workflow job for this annotation

GitHub Actions / lint-and-test

Replace `"@storybook/react";` with `'@storybook/react'`

import { Loading, Empty, Failure, Success } from "./SitesListCell";

Check warning on line 3 in web/src/components/SitesListCell/SitesListCell.stories.tsx

View workflow job for this annotation

GitHub Actions / lint-and-test

Replace `"./SitesListCell";` with `'./SitesListCell'`
import { standard } from "./SitesListCell.mock";

Check warning on line 4 in web/src/components/SitesListCell/SitesListCell.stories.tsx

View workflow job for this annotation

GitHub Actions / lint-and-test

Replace `"./SitesListCell.mock";` with `'./SitesListCell.mock'`

const meta: Meta = {
title: "Cells/SitesListCell",

Check warning on line 7 in web/src/components/SitesListCell/SitesListCell.stories.tsx

View workflow job for this annotation

GitHub Actions / lint-and-test

Replace `"Cells/SitesListCell"` with `'Cells/SitesListCell'`
tags: ["autodocs"],

Check warning on line 8 in web/src/components/SitesListCell/SitesListCell.stories.tsx

View workflow job for this annotation

GitHub Actions / lint-and-test

Replace `"autodocs"` with `'autodocs'`
};

Check warning on line 9 in web/src/components/SitesListCell/SitesListCell.stories.tsx

View workflow job for this annotation

GitHub Actions / lint-and-test

Delete `;`

export default meta;

Check warning on line 11 in web/src/components/SitesListCell/SitesListCell.stories.tsx

View workflow job for this annotation

GitHub Actions / lint-and-test

Delete `;`

export const loading: StoryObj<typeof Loading> = {
render: () => {
return Loading ? <Loading /> : <></>;

Check warning on line 15 in web/src/components/SitesListCell/SitesListCell.stories.tsx

View workflow job for this annotation

GitHub Actions / lint-and-test

Delete `;`
},
};

export const empty: StoryObj<typeof Empty> = {
render: () => {
return Empty ? <Empty /> : <></>;
},
};

export const failure: StoryObj<typeof Failure> = {
render: (args) => {
return Failure ? <Failure error={new Error("Oh no")} {...args} /> : <></>;
},
};

export const success: StoryObj<typeof Success> = {
render: (args) => {
return Success ? <Success {...standard()} {...args} /> : <></>;
},
};
42 changes: 42 additions & 0 deletions web/src/components/SitesListCell/SitesListCell.test.tsx
Original file line number Diff line number Diff line change
@@ -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(<Loading />);
}).not.toThrow();
});

it("renders Empty successfully", async () => {
expect(() => {
render(<Empty />);
}).not.toThrow();
});

it("renders Failure successfully", async () => {
expect(() => {
render(<Failure error={new Error("Oh no")} />);
}).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(<Success sitesList={standard().sitesList} />);
}).not.toThrow();
});
});
58 changes: 58 additions & 0 deletions web/src/components/SitesListCell/SitesListCell.tsx
Original file line number Diff line number Diff line change
@@ -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'

Check failure on line 13 in web/src/components/SitesListCell/SitesListCell.tsx

View workflow job for this annotation

GitHub Actions / lint-and-test

There should be at least one empty line between import groups
import { Link, routes } from '@redwoodjs/router'

Check failure on line 14 in web/src/components/SitesListCell/SitesListCell.tsx

View workflow job for this annotation

GitHub Actions / lint-and-test

`@redwoodjs/router` import should occur before type import of `@redwoodjs/web`

export const QUERY: TypedDocumentNode<
FindSitesListQuery,
FindSitesListQueryVariables
> = gql`
query FindSitesListQuery {
sites: sites {
id
name
ownerName
addressLine1
}
}
`

export const Loading = () => <div>Loading...</div>

export const Empty = () => <div>Empty</div>

export const Failure = ({
error,
}: CellFailureProps<FindSitesListQueryVariables>) => (
<div style={{ color: 'red' }}>Error: {error?.message}</div>
)

export const Success = ({
sites,
}: CellSuccessProps<
FindSitesListQuery,
FindSitesListQueryVariables
>) => {
return (
<div className="container mx-auto py-10">

<Link
to={routes.newSite()}
className="flex w-full items-center justify-center rounded-xl bg-indigo-600 px-4 py-2 font-medium text-white shadow-lg hover:bg-indigo-500 focus:outline-none"
>
Add Site
</Link>
<DataTable columns={columns} data={sites} />
</div>
)
}
26 changes: 26 additions & 0 deletions web/src/components/SitesTable/SitesTable.stories.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof SitesTable> = {
component: SitesTable,
tags: ['autodocs'],
}

export default meta

type Story = StoryObj<typeof SitesTable>

export const Primary: Story = {}
14 changes: 14 additions & 0 deletions web/src/components/SitesTable/SitesTable.test.tsx
Original file line number Diff line number Diff line change
@@ -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(<SitesTable columns={[]} data={[]} />)
}).not.toThrow()
})
})
89 changes: 89 additions & 0 deletions web/src/components/SitesTable/SitesTable.tsx
Original file line number Diff line number Diff line change
@@ -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<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
data: TData[]
}

const SitesTable = <TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) => {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
})

return (
<div className="rounded-xl border border-gray-700 bg-gray-800 shadow-lg">
<Table className="w-full text-gray-200">
<TableHeader className="bg-gray-900">
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id} className="border-b border-gray-700">
{headerGroup.headers.map((header) => {
return (
<TableHead
key={header.id}
className="px-4 py-2 text-sm font-semibold text-gray-300"
>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
)
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && 'selected'}
className="hover:bg-gray-700"
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id} className="px-4 py-2">
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center text-gray-400"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
)
}

export default SitesTable
88 changes: 88 additions & 0 deletions web/src/components/SitesTable/columns.tsx
Original file line number Diff line number Diff line change
@@ -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'

Check failure on line 18 in web/src/components/SitesTable/columns.tsx

View workflow job for this annotation

GitHub Actions / lint-and-test

'ExportPDFButton' is defined but never used. Allowed unused vars must match /^_/u

export type Site = {
id: number
name: string
ownerName: string
addressLine1: string
}

export const columns: ColumnDef<Site>[] = [
{
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 (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem
onClick={() =>
navigator.clipboard.writeText(inspection.id.toString())
}
>
Create New Inspection
</DropdownMenuItem>
<DropdownMenuItem
onClick={() =>
navigate(routes.viewInspection({ id: inspection.id }))
}
>
View All Inspections
</DropdownMenuItem>
<DropdownMenuSeparator />

<DropdownMenuItem
onClick={() =>
navigate(routes.viewInspection({ id: inspection.id }))
}
>
Edit Site
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
},
},
]
13 changes: 13 additions & 0 deletions web/src/pages/SitesPage/SitesPage.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Meta, StoryObj } from "@storybook/react";

import SitesPage from "./SitesPage";

const meta: Meta<typeof SitesPage> = {
component: SitesPage,
};

export default meta;

type Story = StoryObj<typeof SitesPage>;

export const Primary: Story = {};
Loading

0 comments on commit 625793f

Please sign in to comment.