-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #42 from NYPL/SCC-3730/styling-etc
Functional Filters!
- Loading branch information
Showing
13 changed files
with
1,015 additions
and
1 deletion.
There are no files selected for viewing
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,208 @@ | ||
export const normalAggs = [ | ||
{ | ||
"@type": "nypl:Aggregation", | ||
"@id": "res:location", | ||
id: "location", | ||
field: "location", | ||
values: [ | ||
{ | ||
value: "loc:mal82", | ||
count: 572, | ||
label: "Schwarzman Building - Main Reading Room 315", | ||
}, | ||
{ | ||
value: "loc:makk3", | ||
count: 133, | ||
label: "Schwarzman Building - Dewitt Wallace Reference Desk Room 108", | ||
}, | ||
{ | ||
value: "loc:rc2ma", | ||
count: 66, | ||
label: "Offsite", | ||
}, | ||
{ | ||
value: "loc:rcma2", | ||
count: 66, | ||
label: "Offsite", | ||
}, | ||
], | ||
}, | ||
{ | ||
"@type": "nypl:Aggregation", | ||
"@id": "res:format", | ||
id: "format", | ||
field: "format", | ||
values: [ | ||
{ | ||
value: "Text", | ||
count: 704, | ||
label: "Text", | ||
}, | ||
{ | ||
value: "AUG. 23, 2021-CURRENT", | ||
count: 109, | ||
label: "AUG. 23, 2021-CURRENT", | ||
}, | ||
{ | ||
value: "FEB. 15/22, 2021 - AUG. 16, 2021", | ||
count: 24, | ||
label: "FEB. 15/22, 2021 - AUG. 16, 2021", | ||
}, | ||
], | ||
}, | ||
{ | ||
"@type": "nypl:Aggregation", | ||
"@id": "res:status", | ||
id: "status", | ||
field: "status", | ||
values: [ | ||
{ | ||
value: "status:a", | ||
count: 826, | ||
label: "Available", | ||
}, | ||
{ | ||
value: "status:na", | ||
count: 6, | ||
label: "Not available", | ||
}, | ||
{ | ||
value: "status:co", | ||
count: 4, | ||
label: "Loaned", | ||
}, | ||
{ | ||
value: "status:oh", | ||
count: 1, | ||
label: "On Holdshelf", | ||
}, | ||
], | ||
}, | ||
] | ||
|
||
export const aggsWithRepeatedValues = [ | ||
{ | ||
"@type": "nypl:Aggregation", | ||
"@id": "res:location", | ||
id: "location", | ||
field: "location", | ||
values: [ | ||
{ | ||
value: "loc:mym32", | ||
count: 8, | ||
label: "Performing Arts Research Collections - Music", | ||
}, | ||
], | ||
}, | ||
{ | ||
"@type": "nypl:Aggregation", | ||
"@id": "res:format", | ||
id: "format", | ||
field: "format", | ||
values: [], | ||
}, | ||
{ | ||
"@type": "nypl:Aggregation", | ||
"@id": "res:status", | ||
id: "status", | ||
field: "status", | ||
values: [ | ||
{ | ||
value: "status:a", | ||
count: 4, | ||
label: "Available", | ||
}, | ||
{ | ||
value: "status:a", | ||
count: 4, | ||
label: "Available ", | ||
}, | ||
], | ||
}, | ||
] | ||
|
||
export const aggsWithMissingProperties = [ | ||
{ | ||
"@id": "res:location", | ||
"@type": "nypl:Aggregation", | ||
field: "location", | ||
id: "location", | ||
values: [ | ||
{ | ||
count: 4, | ||
value: "loc:maj03", | ||
label: "SASB M1 - General Research - Room 315", | ||
}, | ||
{ | ||
count: 12, | ||
label: "Offsite", | ||
value: "loc:rc2ma", | ||
}, | ||
{ | ||
count: 12, | ||
label: "Off site", | ||
value: "loc:rc2ma", | ||
}, | ||
{ | ||
count: 12, | ||
label: "Off-site", | ||
value: "loc:rc2ma", | ||
}, | ||
{ | ||
count: 12, | ||
label: "off-site", | ||
value: "loc:rc2ma", | ||
}, | ||
{ | ||
count: 12, | ||
label: "off site", | ||
value: "loc:rc2ma", | ||
}, | ||
{ | ||
count: 2, | ||
value: "offsite", | ||
label: "Offsite", | ||
}, | ||
{ | ||
count: 2, | ||
value: "blank", | ||
label: "", | ||
}, | ||
{ | ||
count: 2, | ||
value: "blaaaank", | ||
}, | ||
], | ||
}, | ||
{ | ||
"@id": "res:format", | ||
"@type": "nypl:Aggregation", | ||
field: "format", | ||
id: "format", | ||
values: [ | ||
{ | ||
count: 12, | ||
label: "Text", | ||
value: "Text", | ||
}, | ||
], | ||
}, | ||
{ | ||
"@id": "res:status", | ||
"@type": "nypl:Aggregation", | ||
field: "status", | ||
id: "status", | ||
values: [ | ||
{ | ||
count: 12, | ||
label: "Available", | ||
value: "status:a", | ||
}, | ||
{ | ||
count: 12, | ||
label: "Not Available (ReCAP", | ||
value: "status:na", | ||
}, | ||
], | ||
}, | ||
] |
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,79 @@ | ||
import { useEffect, useRef, useState } from "react" | ||
import { useRouter } from "next/router" | ||
import React from "react" | ||
import { Text, useCloseDropDown } from "@nypl/design-system-react-components" | ||
|
||
import styles from "../../../styles/components/ItemFilters.module.scss" | ||
import type { ItemAggregation } from "../../types/filterTypes" | ||
import { ItemFilterData, LocationFilterData } from "../../models/itemFilterData" | ||
import ItemFilter from "./ItemFilter" | ||
import { | ||
buildItemFilterQueryParams, | ||
parseItemFilterQueryParams, | ||
} from "../../utils/itemFilterUtils" | ||
|
||
interface ItemFilterContainerProps { | ||
itemAggs: ItemAggregation[] | ||
} | ||
|
||
const ItemFilterContainer = ({ itemAggs }: ItemFilterContainerProps) => { | ||
const { query } = useRouter() | ||
const filterData = itemAggs.map((agg: ItemAggregation) => { | ||
if (agg.field === "location") return new LocationFilterData(agg) | ||
else return new ItemFilterData(agg) | ||
}) | ||
const [activeFilters, setAppliedFilters] = useState({ | ||
location: [], | ||
format: [], | ||
status: [], | ||
}) | ||
|
||
const ref = useRef<HTMLDivElement>(null) | ||
useCloseDropDown(() => setWhichFilterIsOpen(""), ref) | ||
|
||
const [whichFilterIsOpen, setWhichFilterIsOpen] = useState("") | ||
|
||
const [tempQueryDisplay, setTempQueryDisplay] = useState("") | ||
|
||
const tempSubmitFilters = () => { | ||
const locationFilterData = filterData.find( | ||
(filter) => filter.field === "location" | ||
) as LocationFilterData | ||
setTempQueryDisplay( | ||
buildItemFilterQueryParams( | ||
activeFilters, | ||
locationFilterData.recapLocations() | ||
) | ||
) | ||
} | ||
|
||
useEffect(() => { | ||
setAppliedFilters(parseItemFilterQueryParams(query)) | ||
}, [query]) | ||
|
||
useEffect(tempSubmitFilters, [activeFilters, tempSubmitFilters]) | ||
|
||
return ( | ||
<div className={styles.filtersContainer}> | ||
<Text data-testid="filter-text" size="body2" isBold={true}> | ||
Filter by | ||
</Text> | ||
<div className={styles.filterGroup} ref={ref}> | ||
{filterData.map((field: ItemFilterData) => ( | ||
<ItemFilter | ||
isOpen={whichFilterIsOpen === field.field} | ||
setWhichFilterIsOpen={setWhichFilterIsOpen} | ||
key={field.field} | ||
itemFilterData={field} | ||
setAppliedFilters={setAppliedFilters} | ||
activeFilters={activeFilters} | ||
submitFilters={tempSubmitFilters} | ||
/> | ||
))} | ||
</div> | ||
<p>{tempQueryDisplay}</p> | ||
</div> | ||
) | ||
} | ||
|
||
export default ItemFilterContainer |
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,92 @@ | ||
import { CheckboxGroup, Checkbox } from "@nypl/design-system-react-components" | ||
import type { Dispatch } from "react" | ||
import { useCallback, useEffect, useState } from "react" | ||
|
||
import type { ItemFilterData } from "../../models/itemFilterData" | ||
import type { Option, AppliedFilters } from "../../types/filterTypes" | ||
import styles from "../../../styles/components/ItemFilters.module.scss" | ||
|
||
import ItemFilterButtons from "./ItemFilterButtons" | ||
import ItemFilterLabel from "./ItemFilterLabel" | ||
|
||
interface ItemFilterProps { | ||
itemFilterData: ItemFilterData | ||
setAppliedFilters: Dispatch<React.SetStateAction<AppliedFilters>> | ||
activeFilters: AppliedFilters | ||
// this type is temporary for dev use only. could end up being different. | ||
submitFilters: Dispatch<React.SetStateAction<AppliedFilters>> | ||
isOpen: boolean | ||
setWhichFilterIsOpen: Dispatch<React.SetStateAction<string>> | ||
} | ||
|
||
const ItemFilter = ({ | ||
itemFilterData, | ||
setAppliedFilters, | ||
activeFilters, | ||
isOpen, | ||
setWhichFilterIsOpen, | ||
}: ItemFilterProps) => { | ||
const field = itemFilterData.field | ||
const [selectedOptions, setSelectedOptions] = useState(activeFilters[field]) | ||
|
||
const resetToAppliedOptions = useCallback(() => { | ||
updateCheckboxGroupValue(activeFilters[field]) | ||
}, [activeFilters, field]) | ||
|
||
const updateCheckboxGroupValue = (data: string[]) => { | ||
setSelectedOptions(data) | ||
} | ||
|
||
// When the filter is close with unapplied options, those options are not | ||
// persisted. Instead, reset to the options that were last queried for. | ||
useEffect(() => { | ||
if (!isOpen) resetToAppliedOptions() | ||
}, [isOpen, resetToAppliedOptions]) | ||
|
||
return ( | ||
<div className={styles.itemFilter}> | ||
<ItemFilterLabel | ||
field={field} | ||
selectedOptions={selectedOptions} | ||
setWhichFilterIsOpen={setWhichFilterIsOpen} | ||
isOpen={isOpen} | ||
/> | ||
{isOpen && ( | ||
<div className={styles.itemFilterOptionsContainer}> | ||
<CheckboxGroup | ||
labelText={field} | ||
showLabel={false} | ||
key={field} | ||
name={field} | ||
id={field} | ||
onChange={updateCheckboxGroupValue} | ||
// isSelected of the children checkboxes is controlled by this value | ||
// attribute. The options whose value attribute match those present in | ||
// the CheckboxGroup value array are selected. | ||
value={selectedOptions} | ||
> | ||
{itemFilterData.displayOptions().map(({ value, label }: Option) => { | ||
return ( | ||
<Checkbox | ||
className={styles.filterOption} | ||
id={value} | ||
key={value} | ||
value={value} | ||
labelText={label} | ||
/> | ||
) | ||
})} | ||
</CheckboxGroup> | ||
<ItemFilterButtons | ||
field={field} | ||
selectedOptions={selectedOptions} | ||
setSelectedOptions={setSelectedOptions} | ||
setAppliedFilters={setAppliedFilters} | ||
/> | ||
</div> | ||
)} | ||
</div> | ||
) | ||
} | ||
|
||
export default ItemFilter |
Oops, something went wrong.