From b0ec7b66a28bbec0262bbea566a40ec3cee042fd Mon Sep 17 00:00:00 2001 From: David Graham Date: Wed, 6 Dec 2023 12:41:16 -0500 Subject: [PATCH] Component-alize the MtnTable component, including a MtnStatusField for filtering by status and a new MtnSearchField component. The new filter fields stores and reads the state in a URL search parameter. quick aria label fixes for MtnList --- .../Mtn/MtnSearchField/MtnSearchField.tsx | 45 ++++++++++++ .../Mtn/MtnStatusField/MtnStatusField.tsx | 70 +++++++++++++++++++ client/src/components/Mtn/MtnTable.spec.tsx | 2 +- client/src/components/Mtn/MtnTable.tsx | 62 +++++----------- client/src/components/UI/HtForm.tsx | 3 +- .../features/ManifestList/ManifestList.tsx | 13 ++-- 6 files changed, 140 insertions(+), 55 deletions(-) create mode 100644 client/src/components/Mtn/MtnSearchField/MtnSearchField.tsx create mode 100644 client/src/components/Mtn/MtnStatusField/MtnStatusField.tsx diff --git a/client/src/components/Mtn/MtnSearchField/MtnSearchField.tsx b/client/src/components/Mtn/MtnSearchField/MtnSearchField.tsx new file mode 100644 index 000000000..57e6422a8 --- /dev/null +++ b/client/src/components/Mtn/MtnSearchField/MtnSearchField.tsx @@ -0,0 +1,45 @@ +import React, { ChangeEventHandler, useEffect } from 'react'; +import { Form } from 'react-bootstrap'; +import { useSearchParams } from 'react-router-dom'; + +interface MtnSearchFieldProps { + value: string; + onChange: ChangeEventHandler; +} + +export function MtnSearchField({ value, onChange }: MtnSearchFieldProps) { + const [searchParams, setSearchParams] = useSearchParams(); + + const setMtnSearchParam = (value: string) => { + if (value) { + searchParams.set('mtn', value); + } else { + searchParams.delete('mtn'); + } + setSearchParams(searchParams); + }; + + const onBlur = () => { + setMtnSearchParam(value); + }; + + const onKeyReturn = (event: React.KeyboardEvent) => { + if (event.key === 'Enter') { + setMtnSearchParam(value); + } + }; + + return ( + <> + + + ); +} diff --git a/client/src/components/Mtn/MtnStatusField/MtnStatusField.tsx b/client/src/components/Mtn/MtnStatusField/MtnStatusField.tsx new file mode 100644 index 000000000..74f993bf4 --- /dev/null +++ b/client/src/components/Mtn/MtnStatusField/MtnStatusField.tsx @@ -0,0 +1,70 @@ +import React, { useEffect, useState } from 'react'; +import { useSearchParams } from 'react-router-dom'; +import Select from 'react-select'; + +export interface StatusOption { + value: string; + label: string; +} + +const statusOptions: readonly StatusOption[] = [ + { value: 'Scheduled', label: 'Scheduled' }, + { value: 'InTransit', label: 'In Transit' }, + { value: 'ReadyForSignature', label: 'Ready to Sign' }, + { value: 'Corrected', label: 'Corrected' }, + { value: 'Signed', label: 'Signed' }, + { value: 'NotAssigned', label: 'Draft' }, + { value: 'UnderCorrection', label: 'Under Correction' }, +]; + +interface MtnStatusFieldProps { + onChange: (newValue: StatusOption | null) => void; +} + +const parseSearchParam = (searchParam: string | null): StatusOption | null => { + if (!searchParam) return null; + const option = statusOptions.find( + (option) => option.value.toLowerCase() === searchParam.toLowerCase() + ); + return option || null; +}; + +export function MtnStatusField({ onChange }: MtnStatusFieldProps) { + const [searchParams, setSearchParams] = useSearchParams(); + const statusParam = searchParams.get('status'); + const [searchValue, setSearchValue] = useState( + parseSearchParam(statusParam) + ); + + const onSelection = (newValue: StatusOption | null) => { + setSearchValue(newValue); + }; + + useEffect(() => { + onChange(searchValue); + if (searchValue) { + searchParams.set('status', searchValue?.value.toLowerCase()); + } else { + searchParams.delete('status'); + } + setSearchParams(searchParams); + }, [searchValue]); + + return ( +
+ { - setSearchValue(newValue); - setColumnFilters([{ id: 'status', value: newValue?.value ?? '' }]); - }} - options={statusOptions} - isClearable={true} - placeholder="Status" - classNames={{ - control: () => 'form-select py-0 ms-2 rounded-3', - placeholder: () => 'p-0 m-0 ps-1', - }} - components={{ IndicatorSeparator: () => null, DropdownIndicator: () => null }} - /> +
diff --git a/client/src/components/UI/HtForm.tsx b/client/src/components/UI/HtForm.tsx index 96c351486..470e6f69d 100644 --- a/client/src/components/UI/HtForm.tsx +++ b/client/src/components/UI/HtForm.tsx @@ -62,8 +62,7 @@ HtForm.Switch = React.forwardRef( (props: FormCheckProps, ref: React.Ref) => { const { children, dangerouslySetInnerHTML, ...rest } = props; return ( - - type="switch" + {children} ); diff --git a/client/src/features/ManifestList/ManifestList.tsx b/client/src/features/ManifestList/ManifestList.tsx index 1875ee27d..b97987cd6 100644 --- a/client/src/features/ManifestList/ManifestList.tsx +++ b/client/src/features/ManifestList/ManifestList.tsx @@ -23,11 +23,9 @@ export function ManifestList(): ReactElement { }); return ( - - -

{siteId}

-
- + +

{siteId}

+ - - - + + {isLoading ? (