-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add persistence to search tables (#86)
Closes #84 Filters are now persisted locally.
- Loading branch information
Showing
15 changed files
with
386 additions
and
209 deletions.
There are no files selected for viewing
14 changes: 0 additions & 14 deletions
14
libs/react/components/src/cta-search/cta-search.stories.tsx
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { SearchFilters } from "./search-filters"; |
55 changes: 55 additions & 0 deletions
55
libs/react/components/src/search-filters/search-filters.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { Select, Option } from "@mui/joy"; | ||
import { SearchFilters } from "./search-filters"; | ||
import type { SearchFiltersProps } from "./search-filters"; | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
|
||
type Story = StoryObj<typeof SearchFilters>; | ||
|
||
export const Playground: Story = { | ||
args: { | ||
filters: "2 selects" as unknown as SearchFiltersProps["filters"], | ||
fallback: "2 selects" as unknown as SearchFiltersProps["fallback"], | ||
}, | ||
}; | ||
|
||
const meta: Meta<typeof SearchFilters> = { | ||
title: "Components/SearchFilters", | ||
component: SearchFilters, | ||
tags: ["autodocs"], | ||
argTypes: { | ||
filters: { | ||
control: { type: "radio" }, | ||
options: ["2 selects"], | ||
mapping: { | ||
"2 selects": ( | ||
<> | ||
<Select> | ||
<Option value="Potatos">Potatos</Option> | ||
<Option value="Kiwis">Kiwis</Option> | ||
<Option value="Banas">Banas</Option> | ||
</Select> | ||
<Select> | ||
<Option value="Batman">Batman</Option> | ||
<Option value="Superman">Superman</Option> | ||
<Option value="Kiwiman">Kiwiman</Option> | ||
</Select> | ||
</> | ||
), | ||
}, | ||
}, | ||
fallback: { | ||
control: { type: "radio" }, | ||
options: ["2 selects"], | ||
mapping: { | ||
"2 selects": ( | ||
<> | ||
<Select /> | ||
<Select /> | ||
</> | ||
), | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
export default meta; |
77 changes: 77 additions & 0 deletions
77
libs/react/components/src/search-filters/search-filters.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { useEffect, useState } from "react"; | ||
import { NoSsr } from "@mui/base"; | ||
import { default as FilterIcon } from "@mui/icons-material/FilterAltOutlined"; | ||
import { | ||
Badge, | ||
Button, | ||
Divider, | ||
IconButton, | ||
Modal, | ||
ModalClose, | ||
ModalDialog, | ||
Stack, | ||
Typography, | ||
} from "@mui/joy"; | ||
import { useDisclose } from "../hooks/use-disclose"; | ||
import type { FC, ReactNode } from "react"; | ||
|
||
export type SearchFiltersProps = { | ||
filters: ReactNode; | ||
fallback: ReactNode; | ||
activeFilters: number; | ||
mobileBreakpoint?: "sm" | "md" | "lg"; | ||
}; | ||
|
||
/** | ||
* Opinionated component to display search filters. Includes a `NoSsr`boundary | ||
* to make it safe to render when filter information is persisted client side. | ||
* | ||
* Provided `fallback`should closely mirror `filters`. | ||
*/ | ||
export const SearchFilters: FC<SearchFiltersProps> = ({ | ||
filters: filters, | ||
fallback, | ||
activeFilters, | ||
mobileBreakpoint = "md", | ||
}) => { | ||
const filterModal = useDisclose(); | ||
const [deferredActiveFilters, setDeferredActiveFilters] = useState(0); | ||
|
||
useEffect(() => setDeferredActiveFilters(activeFilters), [activeFilters]); | ||
|
||
return ( | ||
<> | ||
<Stack | ||
direction={"row"} | ||
gap={1} | ||
sx={{ display: { xs: "none", [mobileBreakpoint]: "flex" } }} | ||
> | ||
<NoSsr fallback={fallback}>{filters}</NoSsr> | ||
</Stack> | ||
<IconButton | ||
size="sm" | ||
variant="outlined" | ||
color="neutral" | ||
onClick={filterModal.open} | ||
sx={{ display: { [mobileBreakpoint]: "none" } }} | ||
> | ||
<Badge badgeContent={deferredActiveFilters} size="sm"> | ||
<FilterIcon /> | ||
</Badge> | ||
</IconButton> | ||
<Modal open={filterModal.isOpen} onClose={filterModal.close}> | ||
<ModalDialog aria-labelledby="filter-modal"> | ||
<ModalClose /> | ||
<Typography id="filter-modal" level="h2"> | ||
Filters | ||
</Typography> | ||
<Divider sx={{ my: 2 }} /> | ||
<Stack gap={2}>{filters}</Stack> | ||
<Button color="primary" onClick={filterModal.close}> | ||
Submit | ||
</Button> | ||
</ModalDialog> | ||
</Modal> | ||
</> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { SearchQuery } from "./search-query"; | ||
export type { SearchQueryProps } from "./search-query"; |
14 changes: 14 additions & 0 deletions
14
libs/react/components/src/search-query/search-query.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { SearchQuery } from "./search-query"; | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
|
||
type Story = StoryObj<typeof SearchQuery>; | ||
|
||
export const Playground: Story = {}; | ||
|
||
const meta: Meta<typeof SearchQuery> = { | ||
title: "Components/SearchQuery", | ||
component: SearchQuery, | ||
tags: ["autodocs"], | ||
}; | ||
|
||
export default meta; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
...earning-objectives/learning-objectives-search/learning-objective-search-config-schema.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { useEffect } from "react"; | ||
import { useForm } from "react-hook-form"; | ||
import { zodResolver } from "@hookform/resolvers/zod"; | ||
import { z } from "zod"; | ||
import { createUsePersistenceHook } from "../../hooks/use-persistence"; | ||
import type { QuestionBankName } from "@chair-flight/base/types"; | ||
import type { UseFormReturn } from "react-hook-form"; | ||
|
||
const searchConfigSchema = z.object({ | ||
searchField: z.enum(["text", "id", "all"]), | ||
subject: z.string(), | ||
course: z.enum([ | ||
"ATPL_A", | ||
"CPL_A", | ||
"ATPL_H_IR", | ||
"ATPL_H_VFR", | ||
"CPL_H", | ||
"IR", | ||
"CBIR_A", | ||
"all", | ||
]), | ||
}); | ||
|
||
type SearchConfig = z.infer<typeof searchConfigSchema>; | ||
|
||
const defaultSearchConfig: z.infer<typeof searchConfigSchema> = { | ||
searchField: "all", | ||
subject: "all", | ||
course: "all", | ||
}; | ||
|
||
const searchPersistence = { | ||
"cf-learning-objective-search-atpl": createUsePersistenceHook<SearchConfig>( | ||
"cf-learning-objective-search-atpl", | ||
), | ||
"cf-learning-objective-search-type": createUsePersistenceHook<SearchConfig>( | ||
"cf-learning-objective-search-type", | ||
), | ||
"cf-learning-objective-search-prep": createUsePersistenceHook<SearchConfig>( | ||
"cf-learning-objective-search-prep", | ||
), | ||
}; | ||
|
||
const resolver = zodResolver(searchConfigSchema); | ||
|
||
export const useSearchConfig = ( | ||
questionBank: QuestionBankName, | ||
): [SearchConfig, UseFormReturn<SearchConfig>] => { | ||
const key = `cf-learning-objective-search-${questionBank}` as const; | ||
const useSearchPersistence = searchPersistence[key]; | ||
const { persistedData, setPersistedData } = useSearchPersistence(); | ||
const defaultValues = persistedData ?? defaultSearchConfig; | ||
const form = useForm({ defaultValues, resolver }); | ||
const searchField = form.watch("searchField"); | ||
const course = form.watch("course"); | ||
const subject = form.watch("subject"); | ||
|
||
useEffect(() => { | ||
setPersistedData({ searchField, course, subject }); | ||
}, [searchField, course, subject, setPersistedData]); | ||
|
||
return [{ searchField, course, subject }, form]; | ||
}; |
Oops, something went wrong.
cf1b006
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
chair-flight – ./
chair-flight.com
www.chair-flight.com
chair-flight-git-main-chair-flight-team.vercel.app
chair-flight.vercel.app
chair-flight-chair-flight-team.vercel.app