From 239e4486200bc9af867f89df7fc2432681892d80 Mon Sep 17 00:00:00 2001 From: Rob Mitchell <40571882+robertandremitchell@users.noreply.github.com> Date: Mon, 9 Sep 2024 13:58:55 -0400 Subject: [PATCH] TEFCA query customization page UI (#2363) * Initializing TEFCA Custom Query Design Work * getting closer * fixing up formatting * trying to add author/system * continuing messing up styles... * saving changes * trying to fix the dang checkmark * fixing mode * Fix TEFCA Customize Query Nav Tab Styling (#2375) * TEFCA Customize Query Nav Tab Styling * fix spacing around include all * cleaning up comments * updating hr line * mode * mode 2 * fixing github action errors * fixing spacing, updating include all styling * updating customize query padding * switching from

to

* fixing gap between side nav * Customized query accordion component (#2404) * add tester accordion component * customizedQueryAccordion generic * move to more generic accordion * change to ValueSet * genericize accordion * add labs to interfaces * minor beautification * organize components * add new custom styles * add accordion component as a CSS grid * modify linting to prevent us from using any * rename styles to be unique * [pre-commit.ci] auto fixes from pre-commit hooks * temp remove linting for any * remove trailing comma * remove logging statement * add toggle to flip accordion directional expander * remove unused styles * [pre-commit.ci] auto fixes from pre-commit hooks * design fixes * [pre-commit.ci] auto fixes from pre-commit hooks * remove styling comments --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * TEFCA Customize UI fix title and subtitle (#2394) * Fixing TEFCA Customize Query Header * fixing return to search nav * TEFCA add customize query button to query form (#2392) * TEFCA Query adding customize query button * updating customize page back option * adding back font size * standardizing /customize to work in both npm and docker * standardizing code with existing functions * trying to get setMode set up * reverting to try again... * hacky fix to the 'back' button * getting queryType, useCase to carry over * add dummy customize query mode * [pre-commit.ci] auto fixes from pre-commit hooks * transitioning from /customize to a component page for customize query * fixing build * adding type * removing customize page, updating test params * removing setMode from CustomizeQuery * adding documentation * more docs * updating doc * fixing merge --------- Co-authored-by: m-goggins Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Fix TEFCA CustomizeQuery Checkboxes (#2432) * Fixing TEFCA CustomizeQuery Checkboxes * comment * changing checkbox sizes * adding correct checkbox * add indeterminate state * fixing spacing of sub-checkboxes * fixing the none selected checkbox formatting * fixing isExpanded * updating background color, button color and copy * changing background color * fixing styling on hr line * TEFCA Customize Query Accordion Updates (#2459) * TEFCA CustomizeQuery UI Accordion Formatting * fixing row lines * fixing final row, reverting to original hr * making ExpandMore/Less button sync with accordion click * fixing height of expandmore/less to be flush with selected * fixing checkboxes to not expand/close accordion * Begin populating codes in the TEFCA query customization page (#2488) * saving tefca changes * fixing customize-query in searchform * removing console logs, fixing customize query redirect * building out loading view --------- Co-authored-by: Marcelle <53578688+m-goggins@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: m-goggins Co-authored-by: bamader --- containers/tefca-viewer/.eslintrc.json | 1 + .../tefca-viewer/public/USWDS-checkmark.svg | 9 + containers/tefca-viewer/src/app/constants.ts | 83 +++- .../app/query/components/CustomizeQuery.tsx | 326 +++++++++++++ .../src/app/query/components/LoadingView.tsx | 28 ++ .../src/app/query/components/SearchForm.tsx | 175 ++++--- .../tefca-viewer/src/app/query/page.tsx | 53 ++- .../tefca-viewer/src/app/query/test/page.tsx | 9 +- .../src/styles/custom-styles.scss | 447 +++++++++++++++++- 9 files changed, 1046 insertions(+), 85 deletions(-) create mode 100644 containers/tefca-viewer/public/USWDS-checkmark.svg create mode 100644 containers/tefca-viewer/src/app/query/components/CustomizeQuery.tsx create mode 100644 containers/tefca-viewer/src/app/query/components/LoadingView.tsx diff --git a/containers/tefca-viewer/.eslintrc.json b/containers/tefca-viewer/.eslintrc.json index 0ee79dc82d..64d3fa6167 100644 --- a/containers/tefca-viewer/.eslintrc.json +++ b/containers/tefca-viewer/.eslintrc.json @@ -40,6 +40,7 @@ "publicOnly": true } ] + // "@typescript-eslint/no-explicit-any": "error" }, "overrides": [ { diff --git a/containers/tefca-viewer/public/USWDS-checkmark.svg b/containers/tefca-viewer/public/USWDS-checkmark.svg new file mode 100644 index 0000000000..64ce613f00 --- /dev/null +++ b/containers/tefca-viewer/public/USWDS-checkmark.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/containers/tefca-viewer/src/app/constants.ts b/containers/tefca-viewer/src/app/constants.ts index d5791f53d9..832a3fcb8f 100644 --- a/containers/tefca-viewer/src/app/constants.ts +++ b/containers/tefca-viewer/src/app/constants.ts @@ -272,4 +272,85 @@ export type Mode = | "results" | "multiple-patients" | "no-patients" - | "multiple-patients-results"; + | "multiple-patients-results" + | "customize-queries"; + +/*Type to specify the expected components for each item in a value set that will be +displayed in the CustomizeQuery component*/ +export interface ValueSetItem { + code: string; + display: string; + system: string; + include: boolean; + author: string; +} + +/*Type to specify the expected expected types of valueset items that will be displayed +as separate tabs in the CusomizeQuery component*/ +export interface ValueSet { + labs: ValueSetItem[]; + medications: ValueSetItem[]; + conditions: ValueSetItem[]; +} + +// Constants for the customize query to do UI work -- TODO: REMOVE ONCE COMPLETE +export const dummyLabs = [ + { + code: "24111-7", + display: + "Neisseria gonorrhoeae DNA [Presence] in Specimen by NAA with probe detection", + system: "LOINC", + include: true, + author: "CSTE Steward", + }, + { + code: "72828-7", + display: + "Chlamydia trachomatis and Neisseria gonorrhoeae DNA panel - Specimen", + system: "LOINC", + include: true, + author: "CSTE Steward", + }, + { + code: "21613-5", + display: + "Chlamydia trachomatis DNA [Presence] in Specimen by NAA with probe detection", + system: "LOINC", + include: true, + author: "CSTE Steward", + }, +]; + +export const dummyMedications = [ + { + code: "12345-6", + display: "Medication A", + system: "LOINC", + include: true, + author: "Author A", + }, + { + code: "67890-1", + display: "Medication B", + system: "LOINC", + include: true, + author: "Author B", + }, +]; + +export const dummyConditions = [ + { + code: "11111-1", + display: "Condition A", + system: "LOINC", + include: true, + author: "Author A", + }, + { + code: "22222-2", + display: "Condition B", + system: "LOINC", + include: true, + author: "Author B", + }, +]; diff --git a/containers/tefca-viewer/src/app/query/components/CustomizeQuery.tsx b/containers/tefca-viewer/src/app/query/components/CustomizeQuery.tsx new file mode 100644 index 0000000000..2001d427b8 --- /dev/null +++ b/containers/tefca-viewer/src/app/query/components/CustomizeQuery.tsx @@ -0,0 +1,326 @@ +"use client"; + +import React, { useMemo, useState, useEffect } from "react"; +import { Accordion, Button, Icon } from "@trussworks/react-uswds"; +import { AccordianSection } from "../../query/component-utils"; +import { ValueSet } from "../../constants"; +import { AccordionItemProps } from "@trussworks/react-uswds/lib/components/Accordion/Accordion"; +import { UseCaseQueryResponse } from "../../query-service"; +import LoadingView from "./LoadingView"; + +interface CustomizeQueryProps { + useCaseQueryResponse: UseCaseQueryResponse; + queryType: string; + ValueSet: ValueSet; + goBack: () => void; +} + +/** + * CustomizeQuery component for displaying and customizing query details. + * @param root0 - The properties object. + * @param root0.useCaseQueryResponse - The response from the query service. + * @param root0.queryType - The type of the query. + * @param root0.ValueSet - The value set of labs, conditions, and medications. + * @param root0.goBack - Back button to go from "customize-queries" to "search" component. + * @returns The CustomizeQuery component. + */ +const CustomizeQuery: React.FC = ({ + useCaseQueryResponse, + queryType, + ValueSet, + goBack, +}) => { + const [activeTab, setActiveTab] = useState("labs"); + + const [valueSetState, setValueSetState] = useState(ValueSet); + const [isExpanded, setIsExpanded] = useState(true); + + if (!useCaseQueryResponse) { + return ; + } + + // Keeps track of whether the accordion is expanded to change the direction of the arrow + const handleToggleExpand = () => { + setIsExpanded(!isExpanded); + }; + + // Keeps track of which side nav tab to display to users + const handleTabChange = (tab: keyof ValueSet) => { + setActiveTab(tab); + }; + + // Allows all items to be selected within an accordion section + const handleSelectAllChange = ( + items: any[], + setItems: React.Dispatch>, + checked: boolean, + ) => { + const updatedItems = items.map((item) => ({ ...item, include: checked })); + setItems(updatedItems); + }; + + const handleIncludeAll = ( + setValueSet: React.Dispatch>, + key: keyof ValueSet, + include: boolean, + ) => { + setValueSet((prevValueSet) => ({ + ...prevValueSet, + [key]: prevValueSet[key].map((item) => ({ ...item, include })), + })); + }; + + // Handles the toggle of the 'include' state for individual items + const toggleInclude = (index: number) => { + const updatedItems = [...valueSetState[activeTab as keyof ValueSet]]; + updatedItems[index].include = !updatedItems[index].include; + setValueSetState((prevState) => ({ + ...prevState, + [activeTab]: updatedItems, + })); + }; + + // Will eventually be the json object storing the parsed data to return on the results page + const handleApplyChanges = () => { + const selectedItems = Object.keys(valueSetState).reduce((acc, key) => { + const items = valueSetState[key as keyof ValueSet]; + acc[key as keyof ValueSet] = items.filter((item) => item.include); + return acc; + }, {} as ValueSet); + goBack(); + }; + + useEffect(() => { + const items = valueSetState[activeTab as keyof ValueSet]; + const selectedCount = items.filter((item) => item.include).length; + const topCheckbox = document.getElementById( + "select-all", + ) as HTMLInputElement; + if (topCheckbox) { + topCheckbox.indeterminate = + selectedCount > 0 && selectedCount < items.length; + } + }, [valueSetState, activeTab]); + + const accordionItems: AccordionItemProps[] = useMemo(() => { + const items = valueSetState[activeTab as keyof ValueSet]; + const selectedCount = items.filter((item) => item.include).length; + return items.length + ? [ + { + title: ( +
+
{ + e.stopPropagation(); + handleSelectAllChange( + items, + (updatedItems) => + setValueSetState((prevState) => ({ + ...prevState, + [activeTab]: updatedItems, + })), + selectedCount !== items.length, + ); + }} + > + {selectedCount === items.length && ( + + )} + {selectedCount > 0 && selectedCount < items.length && ( + + )} +
+
+ {`${items[0].display}`} + + + Author: {items[0].author}{" "} + System:{" "} + {items[0].system} + +
+ {`${selectedCount} selected`} +
+ {isExpanded ? ( + + ) : ( + + )} +
+
+ ), + id: items[0].author + ":" + items[0].system, + className: "accordion-item", + content: ( + +
+
+
Include
+
Code
+
Display
+
+
+ {items.map((item, index) => ( +
+
{ + e.stopPropagation(); + toggleInclude(index); + }} + > + {item.include && ( + + )} +
+
{item.code}
+
{item.display}
+
+ ))} +
+
+
+ ), + expanded: true, + headingLevel: "h3", + }, + ] + : []; + }, [valueSetState, activeTab, isExpanded]); + + return ( + + ); +}; + +export default CustomizeQuery; diff --git a/containers/tefca-viewer/src/app/query/components/LoadingView.tsx b/containers/tefca-viewer/src/app/query/components/LoadingView.tsx new file mode 100644 index 0000000000..4a7d53757f --- /dev/null +++ b/containers/tefca-viewer/src/app/query/components/LoadingView.tsx @@ -0,0 +1,28 @@ +"use client"; + +import React from "react"; + +interface LoadingViewProps { + loading: boolean; +} + +/** + * + * @param root0 - Component for loading screen. + * @param root0.loading - Boolean to track loading state. + * @returns The LoadingView component. + */ +const LoadingView: React.FC = ({ loading }) => { + if (loading) { + return ( +
+
+

Loading...

+
+ ); + } else { + return null; + } +}; + +export default LoadingView; diff --git a/containers/tefca-viewer/src/app/query/components/SearchForm.tsx b/containers/tefca-viewer/src/app/query/components/SearchForm.tsx index ae76a6afaa..f10945fc75 100644 --- a/containers/tefca-viewer/src/app/query/components/SearchForm.tsx +++ b/containers/tefca-viewer/src/app/query/components/SearchForm.tsx @@ -23,15 +23,18 @@ import { UseCaseQuery, UseCaseQueryRequest, } from "../../query-service"; - import { FormatPhoneAsDigits } from "@/app/format-service"; import { useSearchParams } from "next/navigation"; +import { useRouter } from "next/navigation"; interface SearchFormProps { setOriginalRequest: (originalRequest: UseCaseQueryRequest) => void; setUseCaseQueryResponse: (UseCaseQueryResponse: UseCaseQueryResponse) => void; setMode: (mode: Mode) => void; setLoading: (loading: boolean) => void; + setUseCase: (useCase: USE_CASES) => void; + setQueryType: (queryType: string) => void; + useCase: USE_CASES; // Pass useCase from parent userJourney: "test" | "demo"; } @@ -41,6 +44,9 @@ interface SearchFormProps { * @param root0.setUseCaseQueryResponse - The function to set the use case query response. * @param root0.setMode - The function to set the mode. * @param root0.setLoading - The function to set the loading state. + * @param root0.setUseCase - The function to set the use case. + * @param root0.setQueryType - The function to set the query type. + * @param root0.useCase - The current use case. * @param root0.userJourney - The user journey. * @returns - The SearchForm component. */ @@ -49,18 +55,28 @@ const SearchForm: React.FC = ({ setUseCaseQueryResponse, setMode, setLoading, - userJourney: userJourney, + setUseCase, + setQueryType, + useCase, + userJourney, }) => { const params = useSearchParams(); + const router = useRouter(); + const [isClient, setIsClient] = useState(false); - // Get the demoOption (initial selection) selected from modal via the URL - const [useCase, setUseCase] = useState( - (params.get("useCase") as USE_CASES) || "cancer", + // Initialize the local useCase state with the value from URL params or the parent component + const [localUseCase, setLocalUseCase] = useState( + (params.get("useCase") as USE_CASES) || useCase, ); - //Set the patient options based on the demoOption + // Keep the local useCase in sync with the parent useCase + useEffect(() => { + setLocalUseCase(useCase); + }, [useCase]); + + // Set the patient options based on the current useCase const [patientOption, setPatientOption] = useState( - patientOptions[useCase]?.[0]?.value || "", + patientOptions[localUseCase]?.[0]?.value || "", ); const [firstName, setFirstName] = useState(""); const [lastName, setLastName] = useState(""); @@ -69,7 +85,7 @@ const SearchForm: React.FC = ({ const [dob, setDOB] = useState(""); const [mrn, setMRN] = useState(""); - const [autofilled, setAutofilled] = useState(false); // boolean indicating if the form was autofilled, changes color if true + const [autofilled, setAutofilled] = useState(false); // Boolean indicating if the form was autofilled, changes color if true // Fills fields with sample data based on the selected patientOption const fillFields = useCallback( @@ -82,11 +98,18 @@ const SearchForm: React.FC = ({ setMRN(data.MRN); setPhone(data.Phone); setFhirServer(data.FhirServer as FHIR_SERVERS); + + setLocalUseCase(data.UseCase as USE_CASES); setUseCase(data.UseCase as USE_CASES); + setQueryType( + demoQueryOptions.find((option) => option.value === data.UseCase) + ?.label || "", + ); + setAutofilled(highlightAutofilled); } }, - [patientOption], + [patientOption, setUseCase, setQueryType], ); // Fills fields if patientOption changes (auto-fill) @@ -97,18 +120,32 @@ const SearchForm: React.FC = ({ fillFields(patientOption as PatientType); }, [fillFields, patientOption, userJourney]); - // Change the selectedDemoOption (the option selected once you are past the modal) and set the patientOption to the first patientOption for the selectedDemoOption + // Handle changes to the demo query selection const handleDemoQueryChange = (selectedDemoOption: string) => { - // setDemoOption(selectedDemoOption); - setPatientOption(patientOptions[selectedDemoOption][0].value); + setPatientOption(patientOptions[selectedDemoOption][0]?.value || ""); + setLocalUseCase(selectedDemoOption as USE_CASES); + setUseCase(selectedDemoOption as USE_CASES); + setQueryType( + demoQueryOptions.find((option) => option.value === selectedDemoOption) + ?.label || "", + ); }; - async function HandleSubmit(event: React.FormEvent) { - if (!useCase || !fhirServer) { + useEffect(() => { + setIsClient(true); + }, []); + + async function HandleSubmit( + event?: React.FormEvent, + customizeQuery = false, + ) { + if (!localUseCase || !fhirServer) { console.error("Use case and FHIR server are required."); return; } - event.preventDefault(); + if (event) { + event.preventDefault(); + } setLoading(true); const originalRequest = { @@ -117,24 +154,34 @@ const SearchForm: React.FC = ({ dob: dob, mrn: mrn, fhir_server: fhirServer, - use_case: useCase, + use_case: localUseCase, phone: FormatPhoneAsDigits(phone), }; setOriginalRequest(originalRequest); const queryResponse = await UseCaseQuery(originalRequest); setUseCaseQueryResponse(queryResponse); - if (!queryResponse.Patient || queryResponse.Patient.length === 0) { - setMode("no-patients"); - } else if (queryResponse.Patient.length === 1) { - setMode("results"); + // Check if it's a customize query or a standard search + if (customizeQuery) { + if (queryResponse) { + setMode("customize-queries"); + } } else { - setMode("multiple-patients"); + // Normal flow: switch modes based on the query response + if (!queryResponse.Patient || queryResponse.Patient.length === 0) { + setMode("no-patients"); + } else if (queryResponse.Patient.length === 1) { + setMode("results"); + } else { + setMode("multiple-patients"); + } } setLoading(false); } + useEffect(() => { window.scrollTo(0, 0); }, []); + return ( <> @@ -157,10 +204,9 @@ const SearchForm: React.FC = ({ id="query" name="query" className="usa-select margin-top-1" - value={useCase} + value={localUseCase} onChange={(event) => { handleDemoQueryChange(event.target.value); - setUseCase(event.target.value as USE_CASES); }} > {demoQueryOptions.map((option) => ( @@ -221,7 +267,7 @@ const SearchForm: React.FC = ({ setPatientOption(event.target.value); }} > - {patientOptions[useCase]?.map((option) => ( + {patientOptions[localUseCase]?.map((option) => ( @@ -245,46 +291,51 @@ const SearchForm: React.FC = ({ )} {userJourney === "demo" && (
- + + Select a query type and a sample patient to populate the form with + sample data for a query. + -
-
- - - -
+
+ +
+ +
)}
diff --git a/containers/tefca-viewer/src/app/query/page.tsx b/containers/tefca-viewer/src/app/query/page.tsx index 8ad3fdb1a4..5268f24b30 100644 --- a/containers/tefca-viewer/src/app/query/page.tsx +++ b/containers/tefca-viewer/src/app/query/page.tsx @@ -5,7 +5,16 @@ import ResultsView from "./components/ResultsView"; import MultiplePatientSearchResults from "./components/MultiplePatientSearchResults"; import SearchForm from "./components/SearchForm"; import NoPatientsFound from "./components/NoPatientsFound"; -import { Mode } from "../constants"; +import { + Mode, + demoQueryOptions, + dummyConditions, + dummyLabs, + dummyMedications, + USE_CASES, +} from "../constants"; +import CustomizeQuery from "./components/CustomizeQuery"; +import LoadingView from "./components/LoadingView"; /** * Parent component for the query page. Based on the mode, it will display the search @@ -18,6 +27,10 @@ const Query: React.FC = () => { const [useCaseQueryResponse, setUseCaseQueryResponse] = useState(); const [originalRequest, setOriginalRequest] = useState(); + const [useCase, setUseCase] = useState("cancer"); + const [queryType, setQueryType] = useState( + demoQueryOptions.find((option) => option.value === useCase)?.label || "", + ); return (
@@ -28,7 +41,10 @@ const Query: React.FC = () => { setLoading={setLoading} setUseCaseQueryResponse={setUseCaseQueryResponse} setOriginalRequest={setOriginalRequest} + setUseCase={setUseCase} + setQueryType={setQueryType} userJourney="demo" + useCase={useCase as USE_CASES} /> )} @@ -60,23 +76,28 @@ const Query: React.FC = () => { )} {/* Show the no patients found view if there are no patients */} {mode === "no-patients" && } - {loading && ( -
-
-
+ + {/* Use LoadingView component for loading state */} + + + {/* Show the customize query view to select and change what is returned in results */} + {mode === "customize-queries" && ( + <> + {useCaseQueryResponse && ( + setMode("search")} + /> + )} + )}
); }; export default Query; -function LoadingView({ loading }: { loading: boolean }) { - if (loading) { - return ( -
-

Loading...

-
- ); - } else { - return null; - } -} diff --git a/containers/tefca-viewer/src/app/query/test/page.tsx b/containers/tefca-viewer/src/app/query/test/page.tsx index 5bd0c102e4..f097c7a815 100644 --- a/containers/tefca-viewer/src/app/query/test/page.tsx +++ b/containers/tefca-viewer/src/app/query/test/page.tsx @@ -8,7 +8,7 @@ import ResultsView from "../components/ResultsView"; import MultiplePatientSearchResults from "../components/MultiplePatientSearchResults"; import SearchForm from "../components/SearchForm"; import NoPatientsFound from "../components/NoPatientsFound"; -import { Mode } from "../../constants"; +import { Mode, USE_CASES } from "../../constants"; /** * Parent component for the query page. Based on the mode, it will display the search @@ -32,6 +32,13 @@ const Query: React.FC = () => { setUseCaseQueryResponse={setUseCaseQueryResponse} setOriginalRequest={setOriginalRequest} userJourney="test" + setUseCase={function (useCase: USE_CASES): void { + throw new Error("Function not implemented."); + }} + setQueryType={function (queryType: string): void { + throw new Error("Function not implemented."); + }} + useCase={"social-determinants"} /> )} diff --git a/containers/tefca-viewer/src/styles/custom-styles.scss b/containers/tefca-viewer/src/styles/custom-styles.scss index de6ca6e145..0e28c445e3 100644 --- a/containers/tefca-viewer/src/styles/custom-styles.scss +++ b/containers/tefca-viewer/src/styles/custom-styles.scss @@ -96,6 +96,11 @@ h4 { } } +.usa-accordion__content { + margin-top: -80px; +} + + .minimize-container { position: absolute; right: 12px; @@ -212,6 +217,7 @@ body { min-height: 100vh; display: flex; flex-direction: column; + background-color: rgba(247, 249, 250, 1); } .main-body { @@ -219,6 +225,7 @@ body { grid-template-columns: 1fr; // Defines a single column justify-content: center; // Centers the column in the container row-gap: 0.1em; // Adjusts the gap between rows/items + background-color: rgba(247, 249, 250, 1); } .home { @@ -254,8 +261,6 @@ body { } } } - - } .no-patients-found { @@ -268,6 +273,7 @@ body { .usa-footer { margin-top: auto; + background-color: green !important; } .next-button { @@ -403,6 +409,13 @@ html { background: #faf3d1; border: 1px solid #fee685; border-radius: 4px; + display: flex; + width: 596px; + flex-direction: column; + align-items: flex-start; + gap: 0px; + padding-top: 20px; + padding-bottom: 20px; .usa-label { margin-top: 2; @@ -419,6 +432,33 @@ html { } } +.query-customize-wrapper { + display: flex; + align-items: flex-start; + gap: 20px; +} + +.customize-query-button { + background-color: white; + margin-top: 0.5rem; + align-self: flex-start; + height: 40px; + width: 158px; + text-align: center; + font-size: 16px; + font-style: normal; +} + +.usa-button--outline:hover, +.usa-button--outline.usa-button--hover { + background-color: white; +} + +.margin-top-1 { + margin-top: 0.5rem; + width: 360px; +} + .custom-alert { color: #2e6276; width: 100%; @@ -510,7 +550,7 @@ html { color: #1a4480; align-self: stretch; margin: 0; - aspect-ratio : 1.27; + aspect-ratio: 1.27; font-family: "Source Sans Pro Web" !important; font-size: 1rem; font-style: normal; @@ -519,11 +559,12 @@ html { padding: 1.6rem; } -#modal-2-description{ +#modal-2-description { font-size: 1rem; font-style: normal; font-weight: 400; - line-height: 162%; /* 25.92px */ + line-height: 162%; + /* 25.92px */ max-width: 100%; margin-bottom: 2.5rem; } @@ -536,8 +577,404 @@ html { border: 2px solid #005ea2; } +/* Styles for the customize page */ +.accordion-title { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 5px; +} + +.accordion-header { + display: flex; + width: 100%; + color: var(--White, #fff); + font-size: 18px; + font-style: normal; + font-weight: 700; + line-height: normal; + gap: 20px; +} + +.accordion-header-cell { + padding: 0px 24px; + align-items: flex-start; + gap: 60px; + align-self: stretch; +} + +.accordion-header-cell.include { + width: 60px; + /* Fixed width */ + display: flex; + justify-content: space-between; + align-items: flex-start; +} + +.accordion-header-cell.code { + display: flex; + width: 100px; + align-items: flex-start; + justify-content: space-between; +} + +.accordion-header-cell.display { + display: flex; + width: 500px; + align-items: flex-start; + justify-content: space-between; +} + +.accordion-subtitle { + display: block; + font-weight: normal; + font-size: 16px; + color: var(--White, #fff); + font-size: 16px; + line-height: normal; +} + +.usa-accordion { + // border: 1px solid #565C65; + border-radius: 4px; + padding: 0px; +} + +.usa-accordion__button { + background: var(--Base-base-dark, #565c65); + color: white; + width: 100%; + padding: 14px 20px; +} + +.accordion-table-header { + display: flex; + justify-content: space-between; + padding: 8px 0; + font-weight: bold; + word-wrap: break-word; + align-items: center; + gap: 20px; + width: 100%; + box-sizing: border-box; + margin-left: 6px; + margin-top: 10px; +} + +.usa-accordion__button:hover, +.usa-accordion__button:focus { + background: var(--Base-base-dark, #565c65); + outline: none; +} + +.accordion-items { + display: flex; + flex-direction: column; + margin-top: 8px; +} + +.accordion-item-row { + display: flex; + padding: 14px 24px; + align-items: center; + gap: 60px; + align-self: stretch; + justify-content: space-between; + border-bottom: 1px solid #a9aeb1; + width: 100%; + box-sizing: border-box; +} + +.accordion-item-row:nth-child(odd) { + background-color: white; +} + +.accordion-item-row:nth-child(even) { + border-bottom: 1px solid var(--Base-base, #71767a); + background: rgba(223, 225, 226, 0.5); +} + +.accordion-item-cell.include { + flex: 0 0 0%; + text-align: left; +} + +.accordion-item-cell.code { + flex: 0 0 0%; + text-align: left; + white-space: nowrap; +} + +.accordion-item-cell.display { + flex: 1 1 100%; + text-align: left; +} + +.accordion-item-cell { + padding-left: 0 24px; + box-sizing: border-box; +} + +.accordion-item { + padding-bottom: 0 !important; + + &>div>div.padding-bottom-3 { + padding-bottom: 0; + } + + &>button { + background-image: none !important; + } + + .striped-row:nth-child(even) td, + .striped-row:nth-child(even) th { + background-color: #f0f0f0; + } +} + +.customize-query-table { + border: none; +} + +.hide-checkbox-label { + background-color: transparent; + display: flex; + justify-content: space-between; + align-items: flex-start; + + &>label { + margin-top: 0; + width: 24px; + height: 24px; + + &::before { + margin-left: 0; + width: 24px; + height: 24px; + background-size: 14px auto !important; + } + + &>.hide-me { + display: none; + } + } +} + +// navigations +.usa-nav { + display: flex; + justify-content: flex-start; + align-items: center; + margin-bottom: 16px; + width: 100%; +} + +.usa-nav__primary { + display: flex; + justify-content: flex-start; + list-style-type: none; + padding: 0; +} + +.usa-nav__primary-item { + margin-right: 8px; +} + +.usa-nav__primary-item a:hover { + color: #565c65; +} + +.usa-current a { + font-weight: bold; +} + +.custom-nav { + display: flex; + justify-content: flex-start; + gap: 32px; + padding-top: 32px; +} + +.custom-nav ul { + list-style-type: none; + padding: 0; + margin: 0; + display: flex; + gap: 20px; + align-items: flex-start; + color: #565c65; + font-family: "Source Sans Pro"; + position: relative; +} + +.custom-nav li { + list-style: none; + padding: 0; + margin: 0; +} + +.custom-nav a { + color: #565c65; + text-decoration: none; + font-weight: normal; + font-size: 18px; + padding: 0; + margin: 0; +} + +.custom-nav a:hover, +.custom-nav a:focus { + color: #565c65; + background-color: transparent; + border: none; + outline: none; +} + +.custom-nav .usa-current a { + position: relative; + font-weight: bold; + font-size: 18px; + color: #565c65; + background-color: transparent; + border: none; + outline: none; + text-decoration: none; +} + +.custom-nav .usa-current a::after { + content: ""; + position: absolute; + bottom: -8px; + left: 0; + width: 100%; + height: 4px; + background-color: #005ea2; +} + +.custom-nav ul::after { + content: ""; + display: block; + width: 100%; + height: 1px; + background-color: #ddd; + position: absolute; + bottom: 0; + left: 0; + right: 0; +} + +.include-all-link { + font-size: 16px; + padding-top: 20px; + padding-bottom: 20px; +} + +.include-all-link:hover, +.include-all-link:focus { + border: none; + outline: none; +} + +.usa-accordion__content { + padding: 0; +} + +.usa-accordion--bordered .usa-accordion__content { + border-bottom: 1px solid #71767A; + border-left: 1px solid #71767A; + border-right: 1px solid #71767A; + border-radius: 4px; +} + +.accordion-content { + margin: 0; + padding: 0; + width: 100%; +} + +.button-container { + display: flex; + justify-content: flex-start; + gap: 10px; + margin-top: 20px; + padding-bottom: 20px; +} + .back-link { display: inline-flex; align-items: center; gap: 4px; } + +.customize-query-container { + max-width: 960px; + margin: 0 auto; +} + +.customize-query-grid-container { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; +} + +.customize-query-grid-header { + display: grid; + grid-template-columns: 130px 160px 1fr; + align-items: center; + padding: 0px 24px; + border-bottom: 1px solid #71767A; + width: 100%; +} + +.customize-query-grid-body { + display: flex; + flex-direction: column; + width: 100%; +} + +.customize-query-grid-row { + display: grid; + grid-template-columns: 160px 160px 1fr; + align-items: center; + padding: 14px 0px; +} + +.customize-query-grid-row:nth-child(even) { + background-color: rgba(223, 225, 226, 0.50); + border-bottom: 1px solid #71767A; + border-top: 1px solid #71767A; +} + +.customize-query-grid-row:last-child { + border-bottom: none; +} + +.customize-query-grid-cell { + display: flex; + align-items: center; + justify-content: center; +} + +.customize-query-grid-cell.code { + justify-content: flex-start; +} + +.customize-query-grid-cell.display { + justify-content: flex-start; +} + +.customize-query-grid-cell.include { + justify-content: center; + width: 60px; +} + +hr.custom-hr { + width: 100%; + height: 0px; + background-color: #ddd; + margin-top: -6px; + margin-bottom: 0; + border: none; + border-top: 1px solid #71767a; +} \ No newline at end of file