[question][feat] add autocomplete to filters

pull/411/head
Jeff Sieu 3 years ago
parent 58ee0ba349
commit d9f126af05

@ -1,8 +1,11 @@
import { MagnifyingGlassIcon } from '@heroicons/react/24/outline'; import { useMemo } from 'react';
import { CheckboxInput, Collapsible, RadioList, TextInput } from '@tih/ui'; import type { UseFormRegisterReturn } from 'react-hook-form';
import { useForm } from 'react-hook-form';
import { CheckboxInput, Collapsible, RadioList } from '@tih/ui';
export type FilterOption<V extends string = string> = { export type FilterOption<V extends string = string> = {
checked: boolean; checked: boolean;
id: string;
label: string; label: string;
value: V; value: V;
}; };
@ -30,37 +33,66 @@ export type FilterSectionProps<FilterOptions extends Array<FilterOption>> =
options: FilterOptions; options: FilterOptions;
} & ( } & (
| { | {
searchPlaceholder: string; renderInput: (props: {
field: UseFormRegisterReturn<'search'>;
onOptionChange: FilterSectionType<FilterOptions>['onOptionChange'];
options: FilterOptions;
}) => React.ReactNode;
showAll?: never; showAll?: never;
} }
| { | {
searchPlaceholder?: never; renderInput?: never;
showAll: true; showAll: true;
} }
); );
export type FilterSectionFormData = {
search: string;
};
export default function FilterSection< export default function FilterSection<
FilterOptions extends Array<FilterOption>, FilterOptions extends Array<FilterOption>,
>({ >({
label, label,
options, options,
searchPlaceholder,
showAll, showAll,
onOptionChange, onOptionChange,
isSingleSelect, isSingleSelect,
renderInput,
}: FilterSectionProps<FilterOptions>) { }: FilterSectionProps<FilterOptions>) {
const { register, reset } = useForm<FilterSectionFormData>();
const registerSearch = register('search');
const field: UseFormRegisterReturn<'search'> = {
...registerSearch,
onChange: async (event) => {
await registerSearch.onChange(event);
reset();
},
};
const autocompleteOptions = useMemo(() => {
return options.filter((option) => !option.checked) as FilterOptions;
}, [options]);
return ( return (
<div className="mx-2"> <div className="mx-2">
<Collapsible defaultOpen={true} label={label}> <Collapsible defaultOpen={true} label={label}>
<div className="-mx-2 flex flex-col items-stretch gap-2"> <div className="-mx-2 flex flex-col items-stretch gap-2">
{!showAll && ( {!showAll && (
<TextInput <div className="z-10">
isLabelHidden={true} {renderInput({
label={label} field,
placeholder={searchPlaceholder} onOptionChange: async (
startAddOn={MagnifyingGlassIcon} optionValue: FilterOptions[number]['value'],
startAddOnType="icon" ) => {
/> reset();
return onOptionChange(optionValue, true);
},
options: autocompleteOptions,
})}
</div>
)} )}
{isSingleSelect ? ( {isSingleSelect ? (
<div className="px-1.5"> <div className="px-1.5">
@ -81,16 +113,18 @@ export default function FilterSection<
</div> </div>
) : ( ) : (
<div className="px-1.5"> <div className="px-1.5">
{options.map((option) => ( {options
<CheckboxInput .filter((option) => showAll || option.checked)
key={option.value} .map((option) => (
label={option.label} <CheckboxInput
value={option.checked} key={option.value}
onChange={(checked) => { label={option.label}
onOptionChange(option.value, checked); value={option.checked}
}} onChange={(checked) => {
/> onOptionChange(option.value, checked);
))} }}
/>
))}
</div> </div>
)} )}
</div> </div>

@ -3,7 +3,7 @@ import Router, { useRouter } from 'next/router';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { NoSymbolIcon } from '@heroicons/react/24/outline'; import { NoSymbolIcon } from '@heroicons/react/24/outline';
import type { QuestionsQuestionType } from '@prisma/client'; import type { QuestionsQuestionType } from '@prisma/client';
import { SlideOut } from '@tih/ui'; import { SlideOut, Typeahead } from '@tih/ui';
import QuestionOverviewCard from '~/components/questions/card/QuestionOverviewCard'; import QuestionOverviewCard from '~/components/questions/card/QuestionOverviewCard';
import ContributeQuestionCard from '~/components/questions/ContributeQuestionCard'; import ContributeQuestionCard from '~/components/questions/ContributeQuestionCard';
@ -188,7 +188,18 @@ export default function QuestionsHomePage() {
<FilterSection <FilterSection
label="Company" label="Company"
options={companyFilterOptions} options={companyFilterOptions}
searchPlaceholder="Add company filter" renderInput={({ onOptionChange, options, field }) => (
<Typeahead
{...field}
label=""
options={options}
// eslint-disable-next-line @typescript-eslint/no-empty-function
onQueryChange={() => {}}
onSelect={({ value }) => {
onOptionChange(value, true);
}}
/>
)}
onOptionChange={(optionValue, checked) => { onOptionChange={(optionValue, checked) => {
if (checked) { if (checked) {
setSelectedCompanies([...selectedCompanies, optionValue]); setSelectedCompanies([...selectedCompanies, optionValue]);
@ -227,7 +238,18 @@ export default function QuestionsHomePage() {
<FilterSection <FilterSection
label="Location" label="Location"
options={locationFilterOptions} options={locationFilterOptions}
searchPlaceholder="Add location filter" renderInput={({ onOptionChange, options, field }) => (
<Typeahead
{...field}
label=""
options={options}
// eslint-disable-next-line @typescript-eslint/no-empty-function
onQueryChange={() => {}}
onSelect={({ value }) => {
onOptionChange(value, true);
}}
/>
)}
onOptionChange={(optionValue, checked) => { onOptionChange={(optionValue, checked) => {
if (checked) { if (checked) {
setSelectedLocations([...selectedLocations, optionValue]); setSelectedLocations([...selectedLocations, optionValue]);

@ -4,10 +4,12 @@ import type { FilterChoices } from '~/components/questions/filter/FilterSection'
export const COMPANIES: FilterChoices = [ export const COMPANIES: FilterChoices = [
{ {
id: 'Google',
label: 'Google', label: 'Google',
value: 'Google', value: 'Google',
}, },
{ {
id: 'Meta',
label: 'Meta', label: 'Meta',
value: 'Meta', value: 'Meta',
}, },
@ -16,14 +18,17 @@ export const COMPANIES: FilterChoices = [
// Code, design, behavioral // Code, design, behavioral
export const QUESTION_TYPES: FilterChoices<QuestionsQuestionType> = [ export const QUESTION_TYPES: FilterChoices<QuestionsQuestionType> = [
{ {
id: 'CODING',
label: 'Coding', label: 'Coding',
value: 'CODING', value: 'CODING',
}, },
{ {
id: 'SYSTEM_DESIGN',
label: 'Design', label: 'Design',
value: 'SYSTEM_DESIGN', value: 'SYSTEM_DESIGN',
}, },
{ {
id: 'BEHAVIORAL',
label: 'Behavioral', label: 'Behavioral',
value: 'BEHAVIORAL', value: 'BEHAVIORAL',
}, },
@ -33,18 +38,22 @@ export type QuestionAge = 'all' | 'last-6-months' | 'last-month' | 'last-year';
export const QUESTION_AGES: FilterChoices<QuestionAge> = [ export const QUESTION_AGES: FilterChoices<QuestionAge> = [
{ {
id: 'last-month',
label: 'Last month', label: 'Last month',
value: 'last-month', value: 'last-month',
}, },
{ {
id: 'last-6-months',
label: 'Last 6 months', label: 'Last 6 months',
value: 'last-6-months', value: 'last-6-months',
}, },
{ {
id: 'last-year',
label: 'Last year', label: 'Last year',
value: 'last-year', value: 'last-year',
}, },
{ {
id: 'all',
label: 'All', label: 'All',
value: 'all', value: 'all',
}, },
@ -52,22 +61,27 @@ export const QUESTION_AGES: FilterChoices<QuestionAge> = [
export const LOCATIONS: FilterChoices = [ export const LOCATIONS: FilterChoices = [
{ {
id: 'Singapore',
label: 'Singapore', label: 'Singapore',
value: 'Singapore', value: 'Singapore',
}, },
{ {
id: 'Menlo Park',
label: 'Menlo Park', label: 'Menlo Park',
value: 'Menlo Park', value: 'Menlo Park',
}, },
{ {
id: 'california',
label: 'California', label: 'California',
value: 'california', value: 'california',
}, },
{ {
id: 'california',
label: 'Hong Kong', label: 'Hong Kong',
value: 'Hong Kong', value: 'Hong Kong',
}, },
{ {
id: 'Taiwan',
label: 'Taiwan', label: 'Taiwan',
value: 'Taiwan', value: 'Taiwan',
}, },

Loading…
Cancel
Save