From f2be55e07b69afdc0b9fbafc3da9a29b68c285ca Mon Sep 17 00:00:00 2001 From: Himanshu Aggarwal <aggarwah@mcmaster.ca> Date: Mon, 14 Mar 2022 00:02:13 -0400 Subject: [PATCH] Update components to support applying filters to cryptocurrencies --- .../components/dropdown/FilterDropdown.js | 60 ++++++++++++------- .../components/radio/RadioForm.js | 25 +++++--- src/cryptometrics/pages/index.js | 36 +++++++++-- 3 files changed, 87 insertions(+), 34 deletions(-) diff --git a/src/cryptometrics/components/dropdown/FilterDropdown.js b/src/cryptometrics/components/dropdown/FilterDropdown.js index 3781d9f..a95b636 100644 --- a/src/cryptometrics/components/dropdown/FilterDropdown.js +++ b/src/cryptometrics/components/dropdown/FilterDropdown.js @@ -13,11 +13,7 @@ export function FilterDropdown({ filterOptions, setOpen, addFilter }) { useEffect(() => { if (selectedFilter) { - setRadioValue( - filterOptions[selectedFilter].options[ - Object.keys(filterOptions[selectedFilter].options)[0] - ].name - ); + setRadioValue(Object.keys(filterOptions[selectedFilter].options)[0]); } }, [filterOptions, selectedFilter]); @@ -35,7 +31,7 @@ export function FilterDropdown({ filterOptions, setOpen, addFilter }) { const onFilterAdd = () => { addFilter({ - subject: filterOptions[selectedFilter].name, + subject: selectedFilter, condition: radioValue, value: inputValue, }); @@ -49,18 +45,22 @@ export function FilterDropdown({ filterOptions, setOpen, addFilter }) { "dark:bg-dark-600 w-56 h-max max-h-72 rounded-xl mt-2 transition-all duration-100 p-1 overflow-y-scroll shadow-lg shadow-dark-600" )} > - {Object.keys(filterOptions).map((key) => { - return ( - <FilterDropdownItem - key={"primary_option_" + key} - selected={selectedFilter === key} - id={key} - onClick={onSelectedFilterChange} - > - {filterOptions[key].name} - </FilterDropdownItem> - ); - })} + {Object.keys(filterOptions).length > 0 ? ( + Object.keys(filterOptions).map((key) => { + return ( + <FilterDropdownItem + key={"primary_option_" + key} + selected={selectedFilter === key} + id={key} + onClick={onSelectedFilterChange} + > + {filterOptions[key].name} + </FilterDropdownItem> + ); + }) + ) : ( + <FilterDropdownItem disabled>No options available</FilterDropdownItem> + )} </div> <CSSTransition in={selectedFilter} @@ -79,6 +79,9 @@ export function FilterDropdown({ filterOptions, setOpen, addFilter }) { inputValue={inputValue} onInputChange={onInputChange} onFilterAdd={onFilterAdd} + inputLeftSymbol={filterOptions[selectedFilter]?.symbol_left} + inputRightSymbol={filterOptions[selectedFilter]?.symbol_right} + inputType={filterOptions[selectedFilter]?.input_type} /> </span> ) : ( @@ -98,6 +101,9 @@ export function SecondaryFilterDropdown({ inputValue, onInputChange, onFilterAdd, + inputLeftSymbol, + inputRightSymbol, + inputType, }) { return ( <div @@ -115,6 +121,10 @@ export function SecondaryFilterDropdown({ onRadioChange={onRadioChange} inputValue={inputValue} onInputChange={onInputChange} + inputLeftSymbol={inputLeftSymbol} + inputRightSymbol={inputRightSymbol} + inputType={inputType} + onSubmit={onFilterAdd} /> <div className="flex flex-row space-x-2 mb-2"> <Button @@ -126,6 +136,7 @@ export function SecondaryFilterDropdown({ <Button className="bg-indigo-600 text-white font-semibold w-full" onClick={onFilterAdd} + type="submit" > Filter </Button> @@ -135,17 +146,24 @@ export function SecondaryFilterDropdown({ } // selected: boolean -function FilterDropdownItem({ children, selected, onClick, id }) { +function FilterDropdownItem({ + children, + selected, + onClick, + id, + disabled = false, +}) { return ( <div className={classNames( - "py-3 px-4 rounded-xl dark:text-neutral-100 hover:bg-dark-800 border-1 transition-all duration-100 cursor-pointer", + "py-3 px-4 rounded-xl dark:text-neutral-100 border-1 transition-all duration-100 cursor-pointer", { "border-transparent": !selected, "bg-dark-800 border-indigo-600 font-semibold": selected, + "hover:bg-dark-800": !disabled, } )} - onClick={() => onClick(id)} + onClick={disabled || !onClick ? undefined : () => onClick(id)} > <div className="flex flex-row items-center"> {children} diff --git a/src/cryptometrics/components/radio/RadioForm.js b/src/cryptometrics/components/radio/RadioForm.js index 294d3db..79727e5 100644 --- a/src/cryptometrics/components/radio/RadioForm.js +++ b/src/cryptometrics/components/radio/RadioForm.js @@ -3,31 +3,38 @@ import React from "react"; import Input from "../inputs/Input"; export function RadioInputForm({ + inputLeftSymbol, + inputRightSymbol, + inputType, options, radioValue, onRadioChange, inputValue, onInputChange, + onSubmit, }) { return ( - <div> + <form onSubmit={onSubmit}> <div className="flex flex-col py-2 space-y-2"> {Object.keys(options).map((key) => { return ( - <div key={"radio_option_" + options[key].id}> + <div key={"radio_option_" + key}> <Radio selected={radioValue} - radioValue={options[key].name} - id={options[key].id} + radioValue={key} + radioLabel={options[key].name} + id={key} onChange={onRadioChange} /> <div className="my-1"> - {options[key].name === radioValue && ( + {key === radioValue && ( <Input - type="text" placeholder="Enter a value here..." initialValue={inputValue} onChange={onInputChange} + symbolLeft={inputLeftSymbol} + symbolRight={inputRightSymbol} + type={inputType} /> )} </div> @@ -35,11 +42,11 @@ export function RadioInputForm({ ); })} </div> - </div> + </form> ); } -export function Radio({ selected, radioValue, onChange }) { +export function Radio({ selected, radioValue, radioLabel, onChange }) { return ( <label className="inline-flex items-center w-full"> <input @@ -55,7 +62,7 @@ export function Radio({ selected, radioValue, onChange }) { "dark:text-indigo-500": selected === radioValue, })} > - {radioValue} + {radioLabel} </span> </label> ); diff --git a/src/cryptometrics/pages/index.js b/src/cryptometrics/pages/index.js index ba9aa70..c018b2d 100644 --- a/src/cryptometrics/pages/index.js +++ b/src/cryptometrics/pages/index.js @@ -32,10 +32,23 @@ export default function Home() { const [dropdownOpen, setDropdownOpen] = useState(false); const [searchText, setSearchText] = useState(""); const listOfCoins = useCryptoList("usd", 21, false); - const filteredCoins = listOfCoins.data?.filter((coin) => { + + // Filter coins based on search + let filteredCoins = listOfCoins.data?.filter((coin) => { return coin.name.toLowerCase().includes(searchText.toLowerCase()); }); + // Apply filters + for (let i = 0; i < filters.length; i++) { + const { subject, condition, value } = filters[i]; + filteredCoins = filteredCoins.filter((coin) => { + return filterOptions[subject]?.options[condition]?.function( + coin[subject], + value + ); + }); + } + return ( <div> <Head> @@ -60,9 +73,14 @@ export default function Home() { return ( <Filter key={"filter_" + idx} - subject={filter.subject} - condition={filter.condition} + subject={filterOptions[filter.subject]?.name} + condition={ + filterOptions[filter.subject]?.options[filter.condition] + ?.name + } value={filter.value} + symbolLeft={filterOptions[filter.subject]?.symbol_left} + symbolRight={filterOptions[filter.subject]?.symbol_right} buttonIcon={<XIcon className="w-5 h-5" />} onButtonClick={() => removeFilter(filter)} /> @@ -80,7 +98,17 @@ export default function Home() { <FilterDropdown setOpen={setDropdownOpen} addFilter={addFilter} - filterOptions={filterOptions} + filterOptions={Object.keys(filterOptions) + .filter( + (option) => + filters.filter( + (filter) => filter.subject === option + ).length === 0 + ) + .reduce((obj, key) => { + obj[key] = filterOptions[key]; + return obj; + }, {})} /> )} </div> -- GitLab