From 03ab1644ca868fd58088258bf80e25411c2e5c26 Mon Sep 17 00:00:00 2001 From: lublagg Date: Wed, 13 Sep 2023 12:54:04 -0400 Subject: [PATCH] Create attribute dropdown menu. --- package-lock.json | 100 +++++++++++++++++++-------- package.json | 1 + src/components/app.tsx | 8 +-- src/components/attribute-options.tsx | 52 ++++++++++++++ src/components/constants.ts | 47 ++++++++++++- src/components/dropdown.tsx | 23 +++--- src/components/options.scss | 25 +++++++ src/components/options.tsx | 65 +++++++++++++++++ src/components/place-options.tsx | 94 ++++++------------------- src/components/types.ts | 19 +++++ 10 files changed, 318 insertions(+), 116 deletions(-) create mode 100644 src/components/attribute-options.tsx create mode 100644 src/components/options.tsx create mode 100644 src/components/types.ts diff --git a/package-lock.json b/package-lock.json index 1444d47..4eda71c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "axios": "^1.5.0", "classnames": "^2.3.2", "iframe-phone": "^1.3.1", "react": "^17.0.2", @@ -5717,8 +5718,7 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/at-least-node": { "version": "1.0.0", @@ -5789,14 +5789,33 @@ "dev": true }, "node_modules/axios": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", - "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", - "dev": true, + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", + "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", "dependencies": { - "follow-redirects": "^1.14.7" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" } }, + "node_modules/axios/node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/babel-jest": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", @@ -6714,7 +6733,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -7550,7 +7568,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -9378,7 +9395,6 @@ "version": "1.15.2", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", - "dev": true, "funding": [ { "type": "individual", @@ -14809,7 +14825,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -14818,7 +14833,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "dependencies": { "mime-db": "1.52.0" }, @@ -18681,6 +18695,15 @@ "node": ">=10.0.0" } }, + "node_modules/wait-on/node_modules/axios": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", + "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.14.7" + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -23590,8 +23613,7 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "at-least-node": { "version": "1.0.0", @@ -23631,12 +23653,30 @@ "dev": true }, "axios": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", - "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", - "dev": true, + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", + "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", "requires": { - "follow-redirects": "^1.14.7" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + } } }, "babel-jest": { @@ -24321,7 +24361,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -24971,8 +25010,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, "depd": { "version": "2.0.0", @@ -26350,8 +26388,7 @@ "follow-redirects": { "version": "1.15.2", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", - "dev": true + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" }, "for-each": { "version": "0.3.3", @@ -30435,14 +30472,12 @@ "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "requires": { "mime-db": "1.52.0" } @@ -33338,6 +33373,17 @@ "lodash": "^4.17.21", "minimist": "^1.2.5", "rxjs": "^7.5.4" + }, + "dependencies": { + "axios": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", + "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", + "dev": true, + "requires": { + "follow-redirects": "^1.14.7" + } + } } }, "walker": { diff --git a/package.json b/package.json index 790d284..6b3d26e 100644 --- a/package.json +++ b/package.json @@ -135,6 +135,7 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "axios": "^1.5.0", "classnames": "^2.3.2", "iframe-phone": "^1.3.1", "react": "^17.0.2", diff --git a/src/components/app.tsx b/src/components/app.tsx index 20a2974..6ad8457 100644 --- a/src/components/app.tsx +++ b/src/components/app.tsx @@ -1,14 +1,15 @@ import React, {useState} from "react"; import { Dropdown } from "./dropdown"; -import css from "./app.scss"; import classnames from "classnames"; import { Information } from "./information"; import { defaultSelectedOptions } from "./constants"; +import { IStateOptions } from "./types"; +import css from "./app.scss"; function App() { const [showInfo, setShowInfo] = useState(false); - const [selectedOptions, setSelectedOptions] = useState(defaultSelectedOptions); + const [selectedOptions, setSelectedOptions] = useState(defaultSelectedOptions); const handleSetSelectedOptions = (option: string, value: string | string[]) => { const newSelectedOptions = {...selectedOptions, [option]: value}; @@ -40,21 +41,18 @@ function App() { diff --git a/src/components/attribute-options.tsx b/src/components/attribute-options.tsx new file mode 100644 index 0000000..3b5f603 --- /dev/null +++ b/src/components/attribute-options.tsx @@ -0,0 +1,52 @@ +import React from "react"; +import { Options } from "./options"; +import { attributeOptions } from "./constants"; +import classnames from "classnames"; +import { IStateOptions } from "./types"; + +import css from "./options.scss"; + +interface IProps { + handleSetSelectedOptions: (option: string, value: string|string[]) => void; + selectedOptions: IStateOptions; +} + +export const AttributeOptions: React.FC = (props) => { + const {handleSetSelectedOptions, selectedOptions} = props; + + const commonProps = { + inputType: "checkbox" as "checkbox"|"radio", + handleSetSelectedOptions, + selectedOptions + }; + + const getClasses = (key: string) => { + if (key === "cropUnits" || key === "crops") { + return classnames(css.checkOptions, css.narrow); + } else { + return classnames(css.checkOptions, css.vertical) + } + } + + return ( + <> + { + attributeOptions.map((attr) => { + return ( + <> + {attr.label &&
{attr.label}
} + {attr.instructions &&
{attr.instructions}
} +
+ +
+ + ) + }) + } + + ); +}; diff --git a/src/components/constants.ts b/src/components/constants.ts index 22822be..f0ef1fe 100644 --- a/src/components/constants.ts +++ b/src/components/constants.ts @@ -1,3 +1,5 @@ +import { IAttrOptions, IStateOptions } from "./types"; + export const placeOptions = { label: "Size of area for data", options : ["State", "County"] @@ -59,7 +61,46 @@ export const stateOptions = { ] }; -export const defaultSelectedOptions = { - place: null, - states: [] +const farmerOptions: IAttrOptions = { + key: "farmerDemographics", + label: "Farmer Demographics", + options: ["Total Farmers", "Age", "Race", "Gender"], + instructions: null +}; +const farmOptions: IAttrOptions = { + key: "farmDemographics", + label: "Farm Demographics", + options: ["Total Farms", "Organization Type", "Economic Class", "Acres Operated", "Organic"], + instructions: null +}; +const economicOptions: IAttrOptions = { + key: "economicsAndWages", + label: "Economics & Wages", + options: ["Labor Status", "Wages", "Time Worked"], + instructions: null +}; +const cropUnitOptions: IAttrOptions = { + key: "cropUnits", + label: "Production", + options: ["Area Harvested", "Yield"], + instructions: "(Choose units)" +}; +const cropOptions: IAttrOptions = { + key: "crops", + label: null, + options: ["Corn", "Cotton", "Grapes", "Grasses", "Oats", "Soybeans", "Wheat"], + instructions: "(Choose crops)" +}; + +export const attributeOptions = [farmerOptions, farmOptions, economicOptions, cropUnitOptions, cropOptions]; + +export const defaultSelectedOptions: IStateOptions = { + "geographicLevel": "", + "states": [], + "farmerDemographics": [], + "farmDemographics": [], + "economicsAndWages": [], + "cropUnits": "", + "crops": [], + "years": [] }; diff --git a/src/components/dropdown.tsx b/src/components/dropdown.tsx index 5f4650b..e485d0c 100644 --- a/src/components/dropdown.tsx +++ b/src/components/dropdown.tsx @@ -1,13 +1,14 @@ import React, { useState } from "react"; import classnames from "classnames"; -import css from "./dropdown.scss"; import { PlaceOptions } from "./place-options"; import { defaultSelectedOptions } from "./constants"; +import { AttributeOptions } from "./attribute-options"; + +import css from "./dropdown.scss"; interface IProps { sectionName: string sectionAltText: string - sectionDescription: string handleSetSelectedOptions: (option: string, value: string|string[]) => void selectedOptions: typeof defaultSelectedOptions; } @@ -22,22 +23,28 @@ export const Dropdown: React.FC = (props) => { const renderSummary = () => { if (sectionName === "Place") { - const place = selectedOptions.place || ""; + const place = selectedOptions.geographicLevel || ""; const states = selectedOptions.states.join(`, `); return ( `${place}: ${states}` ); + } else if (sectionName === "Attributes") { + return ( + "" + ) } } + const commonProps = {handleSetSelectedOptions, selectedOptions}; + const renderOptions = () => { if (sectionName === "Place") { return ( - + + ); + } else if (sectionName === "Attributes") { + return ( + ); } }; diff --git a/src/components/options.scss b/src/components/options.scss index 0d0d78e..70b558b 100644 --- a/src/components/options.scss +++ b/src/components/options.scss @@ -51,4 +51,29 @@ .checkOptions { flex-wrap: wrap; display: flex; + &.vertical { + flex-direction: column; + .option { + flex-basis: auto; + padding: 0px; + } + } + &.narrow { + margin: 0px; + .option { + flex-basis: auto; + padding: 3px; + margin-top: 0px; + } + } +} + +.category { + font-weight: 600; +} + +.instructions { + padding-left: 6px; + margin-top: 6px; + } \ No newline at end of file diff --git a/src/components/options.tsx b/src/components/options.tsx new file mode 100644 index 0000000..76b4f19 --- /dev/null +++ b/src/components/options.tsx @@ -0,0 +1,65 @@ +import React from "react"; +import { IStateOptions, OptionKey } from "./types"; + +import css from "./options.scss"; + +export interface IOptions { + options: string[], + selectedOptions: IStateOptions, + inputType: "radio" | "checkbox", + handleSetSelectedOptions: (option: string, value: string|string[]) => void, + optionKey: OptionKey +} + + +export const Options: React.FC = (props) => { + const {options, selectedOptions, inputType, handleSetSelectedOptions, optionKey} = props; + + const isOptionSelected = (option: string) => { + if (Array.isArray(selectedOptions[optionKey])) { + return selectedOptions[optionKey].indexOf(option) > -1; + } else { + return selectedOptions[optionKey] === option; + } + }; + + const handleSelectState = (e: React.ChangeEvent) => { + console.log({optionKey}, "e.target.value", e.target.value); + if (Array.isArray(selectedOptions[optionKey])) { + let newArray = [...selectedOptions[optionKey]]; + if (e.currentTarget.checked) { + newArray.push(e.target.value); + newArray.sort(); + + } else { + if (isOptionSelected(e.target.value)) { + newArray = newArray.filter((o) => o !== e.target.value); + } + } + handleSetSelectedOptions(optionKey, newArray); + } else if (optionKey === "geographicLevel" || optionKey === "cropUnits") { + handleSetSelectedOptions(optionKey, e.target.value); + } + }; + + return ( + <> + {options.map((o) => { + return ( +
+ handleSelectState(e)} + /> + +
+ ); + })} + + ); +}; diff --git a/src/components/place-options.tsx b/src/components/place-options.tsx index 0774649..ed3269b 100644 --- a/src/components/place-options.tsx +++ b/src/components/place-options.tsx @@ -1,91 +1,39 @@ import React from "react"; import { placeOptions, stateOptions } from "./constants"; +import { IStateOptions } from "./types" +import { Options } from "./options"; import css from "./options.scss"; interface IProps { handleSetSelectedOptions: (option: string, value: string|string[]) => void; - selectedPlace: string|null; - selectedStates: string[]; + selectedOptions: IStateOptions; } export const PlaceOptions: React.FC = (props) => { - const {handleSetSelectedOptions, selectedPlace, selectedStates} = props; - - const isStateSelected = (state: string) => { - return selectedStates.indexOf(state) > - 1; - }; - - const handleSelectPlace = (e: React.ChangeEvent) => { - handleSetSelectedOptions("place", e.target.value); - }; - - const handleSelectState = (e: React.ChangeEvent) => { - let newSelectedStates = [...selectedStates]; - if (e.currentTarget.checked) { - newSelectedStates.push(e.target.value); - newSelectedStates.sort(); - - } else { - if (isStateSelected(e.target.value)) { - newSelectedStates = newSelectedStates.filter((s) => s !== e.target.value); - } - } - handleSetSelectedOptions("states", newSelectedStates); - }; - - const createPlaceOptions = (options: string[]) => { - return ( - <> - {options.map((o) => { - return ( -
- - -
- ); - })} - - ); - }; - - const createStateOptions = (options: string[]) => { - return ( - <> - {options.map((o) => { - return ( -
- - -
- ); - })} - - ); - }; + const {handleSetSelectedOptions, selectedOptions} = props; + const commonProps = {selectedOptions, handleSetSelectedOptions} return ( <>
{placeOptions.label}:
-
{createPlaceOptions(placeOptions.options)}
+
+ +
{stateOptions.label}:
-
{createStateOptions(stateOptions.options)}
+
+ +
); }; diff --git a/src/components/types.ts b/src/components/types.ts new file mode 100644 index 0000000..cfa1fe2 --- /dev/null +++ b/src/components/types.ts @@ -0,0 +1,19 @@ +export interface IStateOptions { + "geographicLevel": string, + "states": string[] + "farmerDemographics": string[], + "farmDemographics": string[], + "economicsAndWages": string[], + "cropUnits": string, + "crops": string[] + "years": string[] +} + +export type OptionKey = keyof IStateOptions; + +export interface IAttrOptions { + key: keyof IStateOptions, + label: string|null, + options: string[] + instructions: string|null +} \ No newline at end of file