Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DRAFT - Market Range Filters #1088

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/components/FilterView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { FC } from 'react';
import { PiX } from 'react-icons/pi';
import { ThemeButton } from './ThemeButton';
import { BarClickOptions } from '@/app/find-properties/[[...opa_id]]/page';
import { rcos, neighborhoods, zoning } from './Filters/filterOptions';
import {
rcos,
neighborhoods,
zoning,
marketValues,
} from './Filters/filterOptions';
import DimensionFilter from './Filters/DimensionFilter';

const filters = [
Expand All @@ -14,6 +19,12 @@ const filters = [
options: ['Low', 'Medium', 'High'],
type: 'buttonGroup',
},
{
property: 'market_value',
display: 'Market Value',
options: marketValues,
type: 'range',
},
{
property: 'get_access',
display: 'Get Access',
Expand Down
59 changes: 59 additions & 0 deletions src/components/Filters/AutoComplete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'use client';

import React, { FC } from 'react';
import {
AutocompleteFilter,
AutocompleteFilterItem,
} from './AutoCompleteVariants';

type AutocompleteProps = {
display: string;
options: string[];
limitType: string;
selectedRange: string | React.ChangeEvent<HTMLSelectElement>;
setSelectedRange: (prev: any) => void;
aria_describedby_label?: string;
handleSelectionChange: (selection: string, limitType: string) => void;
};

export const Autocomplete: FC<AutocompleteProps> = ({
options,
limitType,
selectedRange,
setSelectedRange,
aria_describedby_label,
handleSelectionChange,
}) => {
const selectedKey = options.indexOf(selectedRange as string).toString();

const onEnter = (key: string) => {
setSelectedRange((prev: any) => ({
...prev,
[limitType]: key,
}));
handleSelectionChange(key, limitType);
};
return (
<div className="space-x-2 min-h-[33.5px]">
<AutocompleteFilter
aria-describedby={aria_describedby_label}
defaultSelectedKey={selectedKey}
variant="flat"
size="md"
radius="md"
onKeyUp={(key) =>
key.key === 'Enter' && onEnter(key.currentTarget.value)
}
placeholder="Select options..."
>
{options.map((option: string, index: number) => (
<AutocompleteFilterItem key={index} shouldHighlightOnFocus={false}>
{option}
</AutocompleteFilterItem>
))}
</AutocompleteFilter>
</div>
);
};

export default Autocomplete;
43 changes: 43 additions & 0 deletions src/components/Filters/AutoCompleteVariants.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {
Autocomplete,
AutocompleteItem,
extendVariants,
} from '@nextui-org/react';

export const AutocompleteFilterItem = extendVariants(AutocompleteItem, {
variants: {
color: {
gray: {
title: ['text-gray-900'],
},
},
size: {
md: {
wrapper: 'm-12',
},
},
},
defaultVariants: {
color: 'gray',
size: 'md',
},
});

export const AutocompleteFilter = extendVariants(Autocomplete, {
variants: {
color: {
gray: {
base: 'text-gray-900',
},
},
size: {
md: {
base: 'py-2',
},
},
},
defaultVariants: {
color: 'gray',
size: 'md',
},
});
101 changes: 75 additions & 26 deletions src/components/Filters/DimensionFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useFilter } from '@/context/FilterContext';
import ButtonGroup from './ButtonGroup';
import MultiSelect from './MultiSelect';
import Panels from './Panels';
import RangedInputs from './RangedInputs';

type DimensionFilterProps = {
property: string;
Expand Down Expand Up @@ -36,10 +37,17 @@ const DimensionFilter: FC<DimensionFilterProps> = ({
const [selectedKeys, setSelectedKeys] = useState<string[]>(
appFilter[property]?.values || []
);
const [selectedRanges, setSelectedRanges] = useState<{
min: string | React.ChangeEvent<HTMLSelectElement>;
max: string | React.ChangeEvent<HTMLSelectElement>;
}>({
min: appFilter[property]?.rangedValues?.min as string,
max: appFilter[property]?.rangedValues?.max as string,
});
const initialSelectedPanelKeys = () => {
const panelKeyObj: { [key: string]: string[] } = {};
for (const key in appFilter) {
panelKeyObj[key] = appFilter[key].values;
panelKeyObj[key] = appFilter[key].values || [];
}
return panelKeyObj;
};
Expand Down Expand Up @@ -87,9 +95,20 @@ const DimensionFilter: FC<DimensionFilterProps> = ({
};

const handleSelectionChange = (
selection: React.ChangeEvent<HTMLSelectElement> | string
selection: React.ChangeEvent<HTMLSelectElement> | string,
limitType?: string
) => {
let newMultiSelect: string[] = [];
const newRangeValues = { ...selectedRanges };

if (limitType) {
if (limitType === 'min') {
newRangeValues.min = selection;
} else if (limitType === 'max') {
newRangeValues.max = selection;
}
}

if (typeof selection === 'string') {
newMultiSelect = selectedKeys.includes(selection)
? selectedKeys.filter((key) => key !== selection)
Expand All @@ -99,13 +118,23 @@ const DimensionFilter: FC<DimensionFilterProps> = ({
newMultiSelect = selection.target.value.split(',');
}
}
setSelectedKeys(newMultiSelect);
dispatch({
type: 'SET_DIMENSIONS',
property,
dimensions: newMultiSelect,
useIndexOfFilter,
});
if (limitType) {
dispatch({
type: 'SET_DIMENSIONS',
property,
limitType,
dimensions: newRangeValues,
useIndexOfFilter,
});
} else {
setSelectedKeys(newMultiSelect);
dispatch({
type: 'SET_DIMENSIONS',
property,
dimensions: newMultiSelect,
useIndexOfFilter,
});
}
};

const filter = useMemo(() => {
Expand All @@ -130,14 +159,25 @@ const DimensionFilter: FC<DimensionFilterProps> = ({
aria_describedby_label={filterLabelID}
/>
);
} else if (type === 'range') {
return (
<RangedInputs
display={display}
options={options}
selectedRanges={appFilter[property]?.rangedValues}
setSelectedRanges={setSelectedRanges}
handleSelectionChange={handleSelectionChange}
aria_describedby_label={filterLabelID}
/>
);
} else {
return (
<MultiSelect
display={display}
options={options}
selectedKeys={selectedKeys}
toggleDimension={toggleDimension}
handleSelectionChange={handleSelectionChange}
handleSelectionChange={handleSelectionChange as any}
aria_describedby_label={filterLabelID}
/>
);
Expand All @@ -150,10 +190,15 @@ const DimensionFilter: FC<DimensionFilterProps> = ({
desc: 'Find properties based on how much they can reduce gun violence considering the gun violence, cleanliness, and tree canopy nearby. ',
linkFragment: 'priority-method',
}
: {
desc: 'Find properties based on what we think is the easiest method to get legal access to them, based on the data available to us. ',
linkFragment: 'access-method',
};
: property === 'market_value'
? {
desc: 'Find properties based on their market value (USD). ',
linkFragment: 'access-method',
}
: {
desc: 'Find properties based on what we think is the easiest method to get legal access to them, based on the data available to us. ',
linkFragment: 'access-method',
};

// text-gray-500, 600 ? or #586266 (figma)?
return (
Expand All @@ -162,20 +207,24 @@ const DimensionFilter: FC<DimensionFilterProps> = ({
<h2 className="heading-lg" id={filterLabelID}>
{display}
</h2>
{(property === 'get_access' || property === 'priority_level') && (
{(property === 'get_access' ||
property === 'priority_level' ||
property === 'market_value') && (
<p className="body-sm text-gray-500 w-[90%] my-1">
{filterDescription.desc}
<a
href={`/methodology/#${filterDescription.linkFragment}`}
className="link"
aria-label={`Learn more about ${
property === 'priority_level'
? 'priority level'
: 'access process'
} from our Methodology Page`}
>
Learn more{' '}
</a>
{(property === 'get_access' || property === 'priority_level') && (
<a
href={`/methodology/#${filterDescription.linkFragment}`}
className="link"
aria-label={`Learn more about ${
property === 'priority_level'
? 'priority level'
: 'access process'
} from our Methodology Page`}
>
Learn more{' '}
</a>
)}
</p>
)}
</div>
Expand Down
71 changes: 71 additions & 0 deletions src/components/Filters/RangedInputs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'use client';

import React, { FC } from 'react';
import Autocomplete from './AutoComplete';

type RangedInputsProps = {
display: string;
options: string[];
selectedRanges:
| {
min: string | React.ChangeEvent<HTMLSelectElement>;
max: string | React.ChangeEvent<HTMLSelectElement>;
}
| undefined;
setSelectedRanges: (prev: any) => void;
aria_describedby_label?: string;
handleSelectionChange: (selection: string) => void;
};

const RangedInputs: FC<RangedInputsProps> = ({
display,
options,
selectedRanges,
setSelectedRanges,
aria_describedby_label,
handleSelectionChange,
}) => {
return (
<div className="flex items-center min-h-[40px]">
<div className="flex flex-col">
<label
htmlFor="max"
className="body-sm font-medium text-gray-700"
aria-describedby={aria_describedby_label}
>
Min
</label>
<Autocomplete
display={display}
limitType={'min'}
selectedRange={selectedRanges?.min ?? ''}
setSelectedRange={setSelectedRanges}
options={options}
aria_describedby_label=""
handleSelectionChange={handleSelectionChange}
/>
</div>
<div className="flex items-center justify-center mt-6 h-0.5 w-5 bg-gray-400"></div>
<div className="flex flex-col">
<label
htmlFor="max"
className="body-sm font-medium text-gray-700"
aria-describedby={aria_describedby_label}
>
Max
</label>
<Autocomplete
display={display}
limitType={'max'}
selectedRange={selectedRanges?.max ?? ''}
setSelectedRange={setSelectedRanges}
options={options}
aria_describedby_label=""
handleSelectionChange={handleSelectionChange}
/>
</div>
</div>
);
};

export default RangedInputs;
9 changes: 9 additions & 0 deletions src/components/Filters/filterOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,15 @@ export const neighborhoods: string[] = [
'Yorktown',
];

export const marketValues: string[] = [
'$10,000',
'$20,000',
'$30,000',
'$40,000',
'$50,000',
'$60,000',
];

export const rcos: string[] = [
'10th Democratic Ward',
'12th Ward Democratic Committee',
Expand Down
Loading
Loading