diff --git a/package-lock.json b/package-lock.json index 6e08a314db..c4436bc9ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3799,9 +3799,9 @@ } }, "node_modules/@storybook/core-common/node_modules/@babel/helper-define-polyfill-provider/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -5217,9 +5217,9 @@ } }, "node_modules/@babel/helper-member-expression-to-functions/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -6357,9 +6357,9 @@ } }, "node_modules/@babel/helper-remap-async-to-generator/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -6469,9 +6469,9 @@ } }, "node_modules/@storybook/mdx1-csf/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -7075,9 +7075,9 @@ "license": "MIT" }, "node_modules/@babel/plugin-transform-modules-systemjs/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -7124,9 +7124,9 @@ } }, "node_modules/@babel/helper-simple-access/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -7262,9 +7262,9 @@ } }, "node_modules/proxy-agent/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "license": "MIT", "dependencies": { @@ -7789,9 +7789,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", + "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -8188,9 +8188,9 @@ "license": "Unlicense" }, "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -8958,12 +8958,12 @@ "license": "ISC" }, "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", + "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.3" }, "bin": { "parser": "bin/babel-parser.js" @@ -9013,9 +9013,9 @@ } }, "node_modules/pac-proxy-agent/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "license": "MIT", "dependencies": { @@ -12202,9 +12202,9 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -15010,9 +15010,9 @@ "license": "MIT" }, "node_modules/@babel/helper-module-imports/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -17710,13 +17710,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", - "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", + "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.2", - "@babel/types": "^7.26.0", + "@babel/parser": "^7.26.3", + "@babel/types": "^7.26.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -19669,9 +19669,9 @@ } }, "node_modules/@lhci/server/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "license": "MIT", "dependencies": { @@ -21953,16 +21953,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "version": "7.26.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", + "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.3", + "@babel/parser": "^7.26.3", "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/types": "^7.26.3", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -28309,9 +28309,9 @@ } }, "node_modules/socks-proxy-agent/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "license": "MIT", "dependencies": { @@ -32211,9 +32211,9 @@ } }, "node_modules/@babel/core/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" diff --git a/src/_scss/elements/filters/_selectedFilterBtn.scss b/src/_scss/elements/filters/_selectedFilterBtn.scss index 5ba0086c76..d8ff4643d8 100644 --- a/src/_scss/elements/filters/_selectedFilterBtn.scss +++ b/src/_scss/elements/filters/_selectedFilterBtn.scss @@ -2,19 +2,20 @@ .shown-filter-button { background-color: $color-white; border: 1px solid $color-gray-lighter; - border-radius: rem(4); + border-radius: rem(2); color: $color-gray; font-size: $smallest-font-size; - font-weight: normal; - min-height: rem(31); + font-weight: $font-semibold; + min-height: rem(22); height: auto; - padding: rem(6) rem(10); + padding: rem(2) rem(4); text-align: left; width: auto; max-width: 90%; line-height: 1.3; - margin-bottom: rem(2); margin-top: rem(2); + margin-bottom: 0; + margin-right: rem(8); @include display(flex); @include align-items(center); @@ -30,7 +31,7 @@ .close { @include display(flex); font-size: $small-font-size; - padding: 0 rem(5) rem(1) rem(15); + padding: 0 0 0 rem(4); } &:hover { background: lighten($color-cool-blue-lightest, 10%); diff --git a/src/_scss/pages/recipient/_contentWrap.scss b/src/_scss/pages/recipient/_contentWrap.scss index 54d41ff0d1..78a6cc084f 100644 --- a/src/_scss/pages/recipient/_contentWrap.scss +++ b/src/_scss/pages/recipient/_contentWrap.scss @@ -3,8 +3,6 @@ color: $color-base; .recipient-section { - padding: rem(30); - h3.recipient-section__title { margin: 0; font-size: $h3-font-size; @@ -79,7 +77,7 @@ @include media($tablet-screen) { @include flex(1 1 50%); } - + // this is all to make the borders gray and not doubled // it's not ideal, but here we are .recipient-section__details-table { @@ -140,6 +138,10 @@ } } } + + .recipient-section__viz.details { + margin-left: 0; + } } } diff --git a/src/_scss/pages/recipient/_overview.scss b/src/_scss/pages/recipient/_overview.scss index 3fd1fb06b6..235e24979c 100644 --- a/src/_scss/pages/recipient/_overview.scss +++ b/src/_scss/pages/recipient/_overview.scss @@ -25,6 +25,7 @@ } } .recipient-overview__content { + width:100%; h3.recipient-overview__heading { @include h4; font-size: rem(19); diff --git a/src/_scss/pages/recipient/_positioning.scss b/src/_scss/pages/recipient/_positioning.scss index a9fb201632..064ec877ce 100644 --- a/src/_scss/pages/recipient/_positioning.scss +++ b/src/_scss/pages/recipient/_positioning.scss @@ -1,23 +1,14 @@ .recipient-content-wrapper { - @include display(flex); - @include align-items(flex-start); width: 100%; - - .recipient-sidebar { - display: none; - margin-top: 0; - // Evenly distribute the 30px space between the sidebar & content - margin-right: rem(15); - @include media($medium-screen) { - display: block; - @include flex(0 0 25%); - } + padding: ($global-spacing-unit * 5) ($global-spacing-unit * 4) ($global-spacing-unit * 4) ($global-spacing-unit * 4); + + @media (max-width: $tablet-screen) { + padding: ($global-spacing-unit * 5) ($global-spacing-unit * 2) ($global-spacing-unit * 2) ($global-spacing-unit * 2); } .recipient-content { width: 100%; - @include media($medium-screen) { - @include flex(1 1 75%); - } + max-width: rem(1400); + margin: auto; } } diff --git a/src/_scss/pages/recipient/recipientPage.scss b/src/_scss/pages/recipient/recipientPage.scss index 8c59618d53..a50a2f8eff 100644 --- a/src/_scss/pages/recipient/recipientPage.scss +++ b/src/_scss/pages/recipient/recipientPage.scss @@ -30,8 +30,6 @@ } .main-content { - @import "../../mixins/fullSectionWrap"; - @include fullSectionWrap(0,0); padding: 0; @import "./_positioning"; @import "../../components/pageLoading"; diff --git a/src/_scss/pages/search/_collapsibleSidebar.scss b/src/_scss/pages/search/_collapsibleSidebar.scss index 363107b06b..6ed7d45903 100644 --- a/src/_scss/pages/search/_collapsibleSidebar.scss +++ b/src/_scss/pages/search/_collapsibleSidebar.scss @@ -1,23 +1,20 @@ @import '_searchFilter'; -$sidebar-width: 354px; -$sidebar-width-closed: 32px; - @keyframes collapsible-sidebar-slideopen { 0% { - width: $sidebar-width-closed; + width: 0%; } 100% { - width: $sidebar-width; + width: 100%; } } @keyframes collapsible-sidebar-slideclose { 0% { - width: $sidebar-width; + width: 100%; } 100% { - width: $sidebar-width-closed; + width: 0%; } } @@ -34,11 +31,10 @@ $sidebar-width-closed: 32px; } .search-collapsible-sidebar-container { - position: sticky; + position: fixed; background-color: $color-white; box-shadow: unset; border-radius: unset; - width: $sidebar-width; .chevron { margin: auto; @@ -48,8 +44,9 @@ $sidebar-width-closed: 32px; } .collapsible-sidebar--toggle { + position: relative; float: right; - margin-right: -42px; + margin-right: -12px; margin-top: 16px; border-radius: 12px; width: 24px; @@ -61,38 +58,42 @@ $sidebar-width-closed: 32px; align-items: center; justify-content: center; cursor: pointer; + z-index: 2; } .collapsible-sidebar { - width: $sidebar-width; margin-top: -40px !important; // TODO Keep here until we remove the legacy panel border: 1px solid $gray-cool-10; box-shadow: none; border-radius: unset; - padding: 0 rem(32); &.opened:not(.is-initial-loaded) { - -webkit-animation: collapsible-sidebar-slideopen 0.25s forwards; - animation: collapsible-sidebar-slideopen 0.25s forwards; + // TODO: Adjust animations next sprint + //-webkit-animation: collapsible-sidebar-slideopen 0.25s forwards; + //animation: collapsible-sidebar-slideopen 0.25s forwards; } &:not(.opened) { - animation: collapsible-sidebar-slideclose 0.25s forwards; - -webkit-animation: collapsible-sidebar-slideclose 0.25s forwards; + // TODO: Adjust animations next sprint + //animation: collapsible-sidebar-slideclose 0.25s forwards; + //-webkit-animation: collapsible-sidebar-slideclose 0.25s forwards; - .collapsible-sidebar-drilldown, .collapsible-sidebar--main-menu { - overflow: hidden; - white-space: nowrap; + .sidebar-bottom-submit, .search-filter__container, .collapsible-sidebar--header, .collapsible-sidebar--drilldown, .collapsible-sidebar--main-menu { + display: none; } } + .categories-list, .category-filter { + overflow-y: auto; + } + .collapsible-sidebar--header { - margin: rem(16) 0; - height: 24px; + padding: rem(16) rem(32); font-size: 16px; font-weight: 600; line-height: 1.5; color: $gray-90; + border-bottom: $gray-cool-10 1px solid; } .sidebar-bottom-submit { @@ -124,6 +125,7 @@ $sidebar-width-closed: 32px; .collapsible-sidebar--back-btn { color: $blue-50; cursor: pointer; + .chevron { width: 6px; height: 10px; @@ -131,13 +133,28 @@ $sidebar-width-closed: 32px; } } + .categories-list-category-type { + color: $gray-90; + font-size: rem(10); + line-height: 1.5; + padding: rem(2) rem(6); + background-color: $gray-cool-5; + border-radius: rem(4); + width: fit-content; + margin-top: rem(32); + + &:first-of-type { + margin-top: 0; + } + } + .categories-list-item-container { margin-top: 16px; padding-bottom: 4px; min-height: 26px; line-height: 1.5; align-items: center; - font-size: 12px; + font-size: $smallest-font-size; color: $gray-90; font-weight: $font-semibold; border-bottom: solid 1px $gray-cool-10; @@ -147,13 +164,47 @@ $sidebar-width-closed: 32px; overflow-y: auto; } - .search-filter__content { - margin: unset; - } - .location-filter { padding: 0 0 2rem 0; } } + + .sidebar-bottom-submit { + position: absolute; + bottom: 0; + width: 100%; + } } -} \ No newline at end of file +} + +.search-contents.v2 { + .collapsible-sidebar { + @media(min-width: $large-screen) { + width: 350px; + } + + @media(min-width: $medium-screen) and (max-width: $large-screen - 1) { + width: 300px; + } + } + + .full-search-sidebar { + display: none; + margin-right: rem(16); + @media(min-width: $large-screen) { + display: flex; + flex-basis: 350px; + } + + @media(min-width: $medium-screen) and (max-width: $large-screen - 1) { + display: flex; + flex-basis: 300px; + } + } + + .search-results-view-container { + width: 100%; + min-width: 0; + } +} + diff --git a/src/_scss/pages/search/_searchFilter.scss b/src/_scss/pages/search/_searchFilter.scss index 07b831f1e1..3bf5855b8a 100644 --- a/src/_scss/pages/search/_searchFilter.scss +++ b/src/_scss/pages/search/_searchFilter.scss @@ -1,14 +1,15 @@ .search-filter__container { - border-top: solid 1px $gray-cool-10; - margin-bottom: $global-margin * 2; + cursor: pointer; + &:not(:first-child) { + border-top: solid 1px $gray-cool-10; + } + padding: $global-margin * 2 $global-margin * 4; .search-filter__content { - margin: rem(16) 0; height: rem(91); .search-filter__top-row { display: flex; - margin-top: $global-margin * 2; .search-filter__top-row-icon-container { display: flex; @@ -34,7 +35,6 @@ font-weight: $font-semibold; line-height: 1.5; color: $theme-color-headline; - cursor: pointer; } .search-filter__top-row-selected-container { diff --git a/src/_scss/pages/search/_searchSubmit.scss b/src/_scss/pages/search/_searchSubmit.scss index 2e6f13458f..6eb1d9b3c1 100644 --- a/src/_scss/pages/search/_searchSubmit.scss +++ b/src/_scss/pages/search/_searchSubmit.scss @@ -1,5 +1,5 @@ .sidebar-submit { - padding: rem(15) rem(25); + padding: rem(8) rem(32); .screen-reader-submit-header { position:absolute; diff --git a/src/_scss/pages/search/_searchv2.scss b/src/_scss/pages/search/_searchv2.scss index cf11e92925..6beddba456 100644 --- a/src/_scss/pages/search/_searchv2.scss +++ b/src/_scss/pages/search/_searchv2.scss @@ -1,5 +1,4 @@ .usa-da-search-page.v2 #main-content .search-contents { - width: 1620px !important; //TODO: Remove after search 2.0 is released - max-width: 1620px !important; //TODO: Remove after search 2.0 is released + max-width: 1600px !important; //TODO: Remove after search 2.0 is released } \ No newline at end of file diff --git a/src/_scss/pages/search/filters/recipient/recipient.scss b/src/_scss/pages/search/filters/recipient/recipient.scss index b86c2c47f3..e8c3563da9 100644 --- a/src/_scss/pages/search/filters/recipient/recipient.scss +++ b/src/_scss/pages/search/filters/recipient/recipient.scss @@ -97,6 +97,16 @@ } } + .recipient-filter-message-container { + font-size: rem(30); + text-align: center; + margin-top: $global-margin; + .recipient-filter-message-container__text { + font-size: 2rem; + padding-bottom: 2rem; + } + } + .recipient-filter-item-wrap { padding: 0; diff --git a/src/_scss/pages/search/filters/timePeriod/timePeriodNew.scss b/src/_scss/pages/search/filters/timePeriod/timePeriodNew.scss index 206f52384f..ef6b0ffe5e 100644 --- a/src/_scss/pages/search/filters/timePeriod/timePeriodNew.scss +++ b/src/_scss/pages/search/filters/timePeriod/timePeriodNew.scss @@ -294,6 +294,7 @@ .selected-filters { @include selected-filter-wrap; + display: flex; } } } diff --git a/src/_scss/pages/state/_contentWrap.scss b/src/_scss/pages/state/_contentWrap.scss index feb20d235e..be2f33a8d2 100644 --- a/src/_scss/pages/state/_contentWrap.scss +++ b/src/_scss/pages/state/_contentWrap.scss @@ -3,8 +3,6 @@ color: $color-base; .state-section { - padding: rem(30); - h3.state-section__title { margin: 0; font-size: $h3-font-size; diff --git a/src/_scss/pages/state/_positioning.scss b/src/_scss/pages/state/_positioning.scss index d17ccff628..7011fe0b12 100644 --- a/src/_scss/pages/state/_positioning.scss +++ b/src/_scss/pages/state/_positioning.scss @@ -1,23 +1,14 @@ .state-content-wrapper { - @include display(flex); - @include align-items(flex-start); width: 100%; + padding: ($global-spacing-unit * 5) ($global-spacing-unit * 4) ($global-spacing-unit * 4) ($global-spacing-unit * 4); - .state-sidebar { - display: none; - margin-top: 0; - // Evenly distribute the 30px space between the sidebar & content - margin-right: rem(15); - @include media($medium-screen) { - display: block; - @include flex(0 0 25%); - } + @media (max-width: $tablet-screen) { + padding: ($global-spacing-unit * 5) ($global-spacing-unit * 2) ($global-spacing-unit * 2) ($global-spacing-unit * 2); } .state-content { width: 100%; - @include media($medium-screen) { - @include flex(1 1 75%); - } + max-width: rem(1400); + margin: auto; } -} \ No newline at end of file +} diff --git a/src/_scss/pages/state/statePage.scss b/src/_scss/pages/state/statePage.scss index dec95959ca..b55b786a4c 100644 --- a/src/_scss/pages/state/statePage.scss +++ b/src/_scss/pages/state/statePage.scss @@ -26,8 +26,8 @@ } .main-content { - @import "../../mixins/fullSectionWrap"; - @include fullSectionWrap(0,0); + //@import "../../mixins/fullSectionWrap"; + //@include fullSectionWrap(0,0); padding: 0; @import "./_positioning"; diff --git a/src/js/components/recipient/RecipientOverview.jsx b/src/js/components/recipient/RecipientOverview.jsx index 7aff54a1db..10ba35377a 100644 --- a/src/js/components/recipient/RecipientOverview.jsx +++ b/src/js/components/recipient/RecipientOverview.jsx @@ -7,7 +7,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Set } from 'immutable'; import { isCancel } from 'axios'; -import { TooltipWrapper, SectionHeader } from 'data-transparency-ui'; +import { TooltipWrapper, SectionHeader, FlexGridCol, FlexGridRow } from 'data-transparency-ui'; import { initialState as defaultFilters } from 'redux/reducers/search/searchFiltersReducer'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Link } from 'react-router-dom'; @@ -165,9 +165,10 @@ const RecipientOverview = (props) => { getSelectedHash(recipient.uei); }; return ( -
+

{recipient.name} {viewAlternateNames} @@ -185,7 +186,7 @@ const RecipientOverview = (props) => { {viewChildren}

-
+

Total Awarded Amount @@ -210,9 +211,9 @@ const RecipientOverview = (props) => {
-

+
-
+

Details

@@ -248,10 +249,10 @@ const RecipientOverview = (props) => { -
+
- + ); }; diff --git a/src/js/components/recipient/spendingOverTime/RecipientTimeVisualizationSection.jsx b/src/js/components/recipient/spendingOverTime/RecipientTimeVisualizationSection.jsx index b31a4cfc91..431663d1b2 100644 --- a/src/js/components/recipient/spendingOverTime/RecipientTimeVisualizationSection.jsx +++ b/src/js/components/recipient/spendingOverTime/RecipientTimeVisualizationSection.jsx @@ -7,7 +7,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { throttle } from 'lodash'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { SectionHeader } from "data-transparency-ui"; +import { SectionHeader, FlexGridRow } from "data-transparency-ui"; import TimeVisualizationPeriodButton from 'components/search/newResultsView/time/TimeVisualizationPeriodButton'; import RecipientTimeVisualization from './RecipientTimeVisualization'; @@ -55,7 +55,7 @@ export default class RecipientTimeVisualizationSection extends React.Component { render() { return ( -
-
+ ); } } diff --git a/src/js/components/recipient/topFive/TopFiveSection.jsx b/src/js/components/recipient/topFive/TopFiveSection.jsx index f5118e83ed..2c79e9b61f 100644 --- a/src/js/components/recipient/topFive/TopFiveSection.jsx +++ b/src/js/components/recipient/topFive/TopFiveSection.jsx @@ -8,7 +8,7 @@ import React from 'react'; import { recipientCategories as topCategories } from 'dataMapping/topCategories'; import TopFiveContainer from 'containers/recipient/topFive/TopFiveContainer'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { SectionHeader } from "data-transparency-ui"; +import { SectionHeader, FlexGridRow } from "data-transparency-ui"; const TopFiveSection = () => { const content = topCategories.map((category) => ( @@ -18,7 +18,7 @@ const TopFiveSection = () => { )); return ( -
{
{content}
-
+ ); }; diff --git a/src/js/components/search/SearchPage.jsx b/src/js/components/search/SearchPage.jsx index 1e88a4c5e9..a5e2417070 100644 --- a/src/js/components/search/SearchPage.jsx +++ b/src/js/components/search/SearchPage.jsx @@ -63,7 +63,6 @@ const SearchPage = ({ const [isMobile, setIsMobile] = useState(window.innerWidth < mediumScreen); const [fullSidebar, setFullSidebar] = useState(false); - const dispatch = useDispatch(); const getSlugWithHash = () => { @@ -140,6 +139,7 @@ const SearchPage = ({ useEffect(() => { setFullSidebar(); + dispatch(setSearchViewSubaward(false)); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/js/components/search/collapsibleSidebar/CategoryFilter.jsx b/src/js/components/search/collapsibleSidebar/CategoryFilter.jsx index ecb1363dc0..1eb8b46484 100644 --- a/src/js/components/search/collapsibleSidebar/CategoryFilter.jsx +++ b/src/js/components/search/collapsibleSidebar/CategoryFilter.jsx @@ -4,7 +4,7 @@ // eslint-disable-next-line no-unused-vars import React from "react"; import PropTypes from 'prop-types'; -import SearchFilter from "./SearchFilter"; +import CategoryHeader from "./CategoryHeader"; const propTypes = { component: PropTypes.object @@ -14,14 +14,14 @@ const CategoryFilter = ({ iconBackgroundColor, iconName, iconColor, component, title, description, height }) => ( <> - -
- {component} +
+
{component}
); diff --git a/src/js/components/search/collapsibleSidebar/CategoryHeader.jsx b/src/js/components/search/collapsibleSidebar/CategoryHeader.jsx new file mode 100644 index 0000000000..53fffe1fbc --- /dev/null +++ b/src/js/components/search/collapsibleSidebar/CategoryHeader.jsx @@ -0,0 +1,91 @@ +/** + * CategoryHeader.jsx + * Created by Brian Petway 09/30/2024 + */ + +import React, { useEffect, useState } from 'react'; +import PropTypes from "prop-types"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +const propTypes = { + iconName: PropTypes.string, + iconColor: PropTypes.string, + iconBackgroundColor: PropTypes.string, + title: PropTypes.string, + description: PropTypes.string, + itemCount: PropTypes.number, + selectedItems: PropTypes.array, + selectCategory: PropTypes.func, + isClickable: PropTypes.bool, + showDescription: PropTypes.bool +}; + +const CategoryHeader = ({ + item, + iconName, + iconColor, + iconBackgroundColor, + title, + description, + selectCategory, + isClickable +}) => { + const [content, setContent] = useState(); + + const innerContent = ( +
+
+
+ +
+
+
{title}
+
+ {/*
*/} + {/*
{itemCount} selected
*/} + {/*
*/} +
+
{description}
+ {/* */} + {/* {selectedItems.map((selectedItem) => (*/} + {/*
{selectedItem}
*/} + {/* ))}*/} + {/*
*/} +
+ ); + + const filterButton = ( +
+ { innerContent } +
); + + const clickableFilterButton = ( +
selectCategory(e, item)} + onKeyDown={(e) => (e.key === "Enter" ? selectCategory(e, item) : '')} + tabIndex={0} + role="button"> + { innerContent } + +
); + + useEffect(() => { + if (isClickable) { + setContent(clickableFilterButton); + } + else { + setContent(filterButton); + } + }, [isClickable]); + + + return (<>{ content }); +}; + +CategoryHeader.propTypes = propTypes; +export default CategoryHeader; diff --git a/src/js/components/search/collapsibleSidebar/CateogriesList.jsx b/src/js/components/search/collapsibleSidebar/CateogriesList.jsx index e5ec409944..2c9c9325b8 100644 --- a/src/js/components/search/collapsibleSidebar/CateogriesList.jsx +++ b/src/js/components/search/collapsibleSidebar/CateogriesList.jsx @@ -4,7 +4,7 @@ import React from "react"; import PropTypes from 'prop-types'; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import SearchFilter from "./SearchFilter"; +import CategoryHeader from "./CategoryHeader"; const propTypes = { categories: PropTypes.object, @@ -12,31 +12,77 @@ const propTypes = { }; const CategoriesList = ({ - categories, setLevel3, iconBackgroundColor, iconName, iconColor, title, description, height + categories, + setLevel3, + iconBackgroundColor, + iconName, + iconColor, + title, + description, + height }) => ( -
- + -
- {categories.map((item) => ( -
setLevel3(e, item.component)} - onKeyUp={((e) => (e.key === "Enter" ? setLevel3(e, item.component) : ''))} - role="button" - tabIndex={0}> -
-
{item.title}
-
-
-
- ))} +
+
+ {categories.map((item) => { + if (item?.categoryType) { + return ( + <> +
+ {item.categoryType} +
+ { + item.categories.map((category) => ( +
setLevel3(e, category)} + onKeyUp={ + ((e) => ( + e.key === "Enter" ? setLevel3(e, category.component) : '' + )) + } + role="button" + tabIndex={0}> +
+
{category.title}
+
+
+
+
+ )) + } + + ); + } + + return ( +
setLevel3(e, item)} + onKeyUp={((e) => (e.key === "Enter" ? setLevel3(e, item.component) : ''))} + role="button" + tabIndex={0}> +
+
{item.title}
+
+
+
+
+ ); + })} +
-
+ ); CategoriesList.propTypes = propTypes; diff --git a/src/js/components/search/collapsibleSidebar/CollapsibleSidebar.jsx b/src/js/components/search/collapsibleSidebar/CollapsibleSidebar.jsx new file mode 100644 index 0000000000..df875d9b52 --- /dev/null +++ b/src/js/components/search/collapsibleSidebar/CollapsibleSidebar.jsx @@ -0,0 +1,218 @@ +/** + * CollapsibleSidebar.jsx + * Created by Andrea Blackwell 11/05/2024 + **/ + +import React, { useEffect, useState } from 'react'; +import { FilterCategoryTree } from "dataMapping/search/searchFilterCategories"; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { throttle } from "lodash"; + +import SearchSidebarSubmitContainer from "../../../containers/search/SearchSidebarSubmitContainer"; +import SearchSidebarDrilldown from "./SearchSidebarDrilldown"; +import SearchSidebarMainMenu from "./SearchSidebarMainMenu"; + +const CollapsibleSidebar = () => { + const [isOpened, setIsOpened] = useState(true); + const [drilldown, setDrilldown] = useState(null); + const [isDrilldown, setIsDrilldown] = useState(false); + const [selectedCategory, setSelectedCategory] = useState(null); + const [currentLevel, setCurrentLevel] = useState(1); + const [initialPageLoad, setInitialPageLoad] = useState(true); + const [windowWidth, setWindowWidth] = useState(); + const [windowHeight, setWindowHeight] = useState(); + const [sidebarHeight, setSidebarHeight] = useState(); + + const toggleOpened = (e) => { + e.preventDefault(); + setIsOpened((prevState) => !prevState); + }; + + const setLevel2 = (e, item) => { + e.preventDefault(); + setSelectedCategory(item); + setDrilldown(FilterCategoryTree[item?.categoryKey]); + setIsDrilldown(true); + setCurrentLevel(2); + }; + + const setLevel3 = (e, item) => { + e.preventDefault(); + setDrilldown(item); + setIsDrilldown(true); + setCurrentLevel(3); + }; + + const goBack = (e) => { + if (currentLevel === 2) { + e.preventDefault(); + setDrilldown(null); + setCurrentLevel(1); + setIsDrilldown(false); + } + else if (currentLevel === 3) { + setDrilldown(selectedCategory[FilterCategoryTree[selectedCategory?.categoryKey]]); + setCurrentLevel(2); + } + }; + + useEffect(() => { + if (isOpened) { + if (document.querySelector(".full-search-sidebar")) { + if (windowWidth < 991 && windowWidth < 1200) { + document.querySelector(".full-search-sidebar").style.width = "unset"; + document.querySelector(".full-search-sidebar").style.flexBasis = "282px"; + document.querySelector(".collapsible-sidebar").style.width = "282px"; + } + else if (windowWidth > 1199) { + document.querySelector(".full-search-sidebar").style.width = "unset"; + document.querySelector(".full-search-sidebar").style.flexBasis = "332px"; + document.querySelector(".collapsible-sidebar").style.width = "332px"; + } + } + } + else if (document.querySelector(".full-search-sidebar")) { + document.querySelector(".full-search-sidebar").style.width = "0"; + document.querySelector(".full-search-sidebar").style.flexBasis = "0"; + document.querySelector(".collapsible-sidebar").style.width = "0"; + } + }, [isOpened, windowWidth]); + + useEffect(() => { + if (!isOpened && initialPageLoad) { + setInitialPageLoad(false); + } + }, [initialPageLoad, isOpened]); + + const checkInView = (el) => { + const bbox = el.getBoundingClientRect(); + + const intersection = { + top: Math.max(0, bbox.top), + left: Math.max(0, bbox.left), + bottom: Math.min(window.innerHeight, bbox.bottom), + right: Math.min(window.innerWidth, bbox.right) + }; + + return (intersection.bottom - intersection.top); + }; + + const resizeSidebarWithStickyBar = (footerInView, headingInView, headingPadding, inPanelNonScrollableEls) => { + if (footerInView < 0) { + setWindowHeight((window.innerHeight - headingInView) + headingPadding); + setSidebarHeight(((window.innerHeight - headingInView) - inPanelNonScrollableEls) + headingPadding); + } + else { + // Hide side search by... if only a small part of the sidebar is in view + const newSidebarHeight = (((window.innerHeight - headingInView) - inPanelNonScrollableEls) - footerInView) + headingPadding; + if (newSidebarHeight < 1) { + document.querySelector(".collapsible-sidebar--header").style.display = "none"; + } + else { + document.querySelector(".collapsible-sidebar--header").style.display = "block"; + } + + setWindowHeight(((window.innerHeight - headingInView) - footerInView) + headingPadding); + setSidebarHeight(newSidebarHeight); + } + }; + + const resizeSidebarWithFullHeader = (fullHeader, inPanelNonScrollableEls) => { + setWindowHeight((window.innerHeight - fullHeader) + window.scrollY); + setSidebarHeight(((window.innerHeight - fullHeader) - inPanelNonScrollableEls) + window.scrollY); + }; + + const handleScroll = throttle(() => { + // TODO: Remove upon completion of sidepanel - 581.63 is the height of the footer at 1839 px browser width + const footerEl = document.querySelector("footer"); + const footerInView = checkInView(footerEl) + 48; + const topStickyBarEl = document.querySelector(".usda-page-header"); + const topStickyBarBbox = topStickyBarEl.getBoundingClientRect(); + const top = checkInView(topStickyBarEl) < 0 ? 0 : topStickyBarBbox.bottom; + const headingPadding = 40; + const headingInView = top + headingPadding; + const fullHeader = 148; + const inPanelNonScrollableEls = 172; + + document.querySelector(".search-collapsible-sidebar-container").style.top = `${headingInView}px`; + + if (topStickyBarEl?.classList?.contains("usda-page-header--sticky")) { + resizeSidebarWithStickyBar(footerInView, headingInView, headingPadding, inPanelNonScrollableEls); + } + else if (top !== 0) { + resizeSidebarWithFullHeader(fullHeader, inPanelNonScrollableEls); + } + }, 50); + + const keyHandler = (e, func) => { + e.preventDefault(); + if (e.key === "Enter") { + func(e); + } + }; + + const handleResize = throttle(() => { + const newWidth = window.innerWidth; + if (windowWidth !== newWidth) { + setWindowWidth(newWidth); + } + }, 50); + + useEffect(() => { + handleScroll(); + window.addEventListener('scroll', (e) => handleScroll(e)); + + return () => { + window.removeEventListener('scroll', handleScroll); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + handleResize(); + window.addEventListener('resize', (e) => handleResize(e)); + return () => window.removeEventListener('resize', handleResize); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( +
+
+
toggleOpened(e)} + onKeyDown={(e) => keyHandler(e, toggleOpened)} + role="button" + tabIndex="0"> + {isOpened ? + + : + + } +
+ + + + +
+ +
+
+
+ ); +}; + +export default CollapsibleSidebar; diff --git a/src/js/components/search/collapsibleSidebar/SearchFilter.jsx b/src/js/components/search/collapsibleSidebar/SearchFilter.jsx deleted file mode 100644 index 5a8265e327..0000000000 --- a/src/js/components/search/collapsibleSidebar/SearchFilter.jsx +++ /dev/null @@ -1,62 +0,0 @@ -/** - * SearchFilter.jsx - * Created by Brian Petway 09/30/2024 - */ - -import React from 'react'; -import PropTypes from "prop-types"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; - -const propTypes = { - iconName: PropTypes.string, - iconColor: PropTypes.string, - iconBackgroundColor: PropTypes.string, - title: PropTypes.string, - description: PropTypes.string, - itemCount: PropTypes.number, - selectedItems: PropTypes.array, - selectCategory: PropTypes.func -}; - -const SearchFilter = ({ - item, - iconName, - iconColor, - iconBackgroundColor, - title, - description, - selectCategory -}) => ( -
-
-
-
- -
-
-
selectCategory(e, item)} - OnKeyDown={(e) => (e.key === "Enter" ? selectCategory(e, item) : '')} - tabIndex={0} - role="button">{title} -
-
- {/*
*/} - {/*
{itemCount} selected
*/} - {/*
*/} -
-
{description}
- {/* */} - {/* {selectedItems.map((selectedItem) => (*/} - {/*
{selectedItem}
*/} - {/* ))}*/} - {/*
*/} -
-
-); - -SearchFilter.propTypes = propTypes; -export default SearchFilter; diff --git a/src/js/components/search/collapsibleSidebar/SearchPagev2.jsx b/src/js/components/search/collapsibleSidebar/SearchPagev2.jsx index b258b037f1..c69db1a592 100644 --- a/src/js/components/search/collapsibleSidebar/SearchPagev2.jsx +++ b/src/js/components/search/collapsibleSidebar/SearchPagev2.jsx @@ -6,7 +6,7 @@ import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import { throttle } from 'lodash'; -import { DownloadIconButton, ShareIcon, FlexGridRow, FlexGridCol } from 'data-transparency-ui'; +import { DownloadIconButton, ShareIcon, FlexGridCol } from 'data-transparency-ui'; import { Helmet } from 'react-helmet'; import { handleShareOptionClick, getBaseUrl } from 'helpers/socialShare'; @@ -26,7 +26,7 @@ import SubawardDropdown from "../SubawardDropdown"; import { setSearchViewSubaward } from "../../../redux/actions/search/searchViewActions"; import ResultsView from "../newResultsView/ResultsView"; import Button from "../../sharedComponents/buttons/Button"; -import SearchSidebarv2 from "./SearchSidebarv2"; +import CollapsibleSidebar from "./CollapsibleSidebar"; import PageFeatureFlag from "../../sharedComponents/PageFeatureFlag"; require('pages/search/searchPage.scss'); @@ -142,7 +142,7 @@ const SearchPage = ({ useEffect(() => { setSearchv2(true); - setFullSidebar(); + setFullSidebar(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -172,13 +172,13 @@ const SearchPage = ({ filters={appliedFilters}>
- - +
+
{fullSidebar} {isMobile === false && searchv2 === false ? : ''} - +
{ + const keyHandler = (e, func) => { + e.preventDefault(); + if (e.key === "Enter") { + func(e); + } + }; + + return ( +
+
+
goBack(e)} + onKeyDown={(e) => keyHandler(e, goBack)} + role="button" + tabIndex="0"> + Back +
+
+
+ {list && } + + {filter && } +
+
); +}; + +SearchSidebarDrilldown.propTypes = propTypes; +export default SearchSidebarDrilldown; diff --git a/src/js/components/search/collapsibleSidebar/SearchSidebarMainMenu.jsx b/src/js/components/search/collapsibleSidebar/SearchSidebarMainMenu.jsx new file mode 100644 index 0000000000..c0a6ad8409 --- /dev/null +++ b/src/js/components/search/collapsibleSidebar/SearchSidebarMainMenu.jsx @@ -0,0 +1,35 @@ +import React from "react"; +import PropTypes from 'prop-types'; +import { SearchFilterCategories } from "dataMapping/search/searchFilterCategories"; +import CategoryHeader from "./CategoryHeader"; + +const propTypes = { + isDrilldown: PropTypes.bool, + sidebarHeight: PropTypes.number, + setLevel2: PropTypes.func +}; + +const SearchSidebarMainMenu = ({ isDrilldown, sidebarHeight, setLevel2 }) => ( +
+
+ Search by... +
+
+ {SearchFilterCategories.map((item) => ())} +
+
+); + +SearchSidebarMainMenu.propTypes = propTypes; +export default SearchSidebarMainMenu; diff --git a/src/js/components/search/collapsibleSidebar/SearchSidebarv2.jsx b/src/js/components/search/collapsibleSidebar/SearchSidebarv2.jsx deleted file mode 100644 index 8e7d24570e..0000000000 --- a/src/js/components/search/collapsibleSidebar/SearchSidebarv2.jsx +++ /dev/null @@ -1,172 +0,0 @@ -/** - * SearchSidebarv2.jsx - * Created by Andrea Blackwell 11/05/2024 - **/ - -import React, { useEffect, useState } from 'react'; -import { SearchFilterCategories, FilterCategoryTree } from "dataMapping/search/newSearchFilterCategories"; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { throttle } from "lodash"; - -import SearchFilter from "./SearchFilter"; -import { SearchSidebarSubmitContainer } from "../../../containers/search/SearchSidebarSubmitContainer"; -import CategoriesList from "./CateogriesList"; -import CategoryFilter from "./CategoryFilter"; - -const SearchSidebar = () => { - const [isOpened, setIsOpened] = useState(true); - const [drilldown, setDrilldown] = useState(null); - const [isDrilldown, setIsDrilldown] = useState(false); - const [selectedCategory, setSelectedCategory] = useState(null); - const [currentLevel, setCurrentLevel] = useState(1); - const [initialPageLoad, setInitialPageLoad] = useState(true); - const [windowWidth, setWindowWidth] = useState(); - const [windowHeight, setWindowHeight] = useState(); - const [sidebarHeight, setSidebarHeight] = useState(); - const toggleOpened = (e) => { - e.preventDefault(); - setIsOpened((prevState) => !prevState); - }; - - const setLevel2 = (e, item) => { - e.preventDefault(); - setSelectedCategory(item); - setDrilldown(FilterCategoryTree[item?.categoryKey]); - setIsDrilldown(true); - setCurrentLevel(2); - }; - - const setLevel3 = (e, component) => { - e.preventDefault(); - setDrilldown(component); - setIsDrilldown(true); - setCurrentLevel(3); - }; - - const goBack = (e) => { - if (currentLevel === 2) { - e.preventDefault(); - setDrilldown(null); - setCurrentLevel(1); - setIsDrilldown(false); - } - else if (currentLevel === 3) { - setDrilldown(selectedCategory[FilterCategoryTree[selectedCategory?.categoryKey]]); - setCurrentLevel(2); - } - }; - - const keyHandler = (e, func) => { - e.preventDefault(); - if (e.key === "Enter") { - func(e); - } - }; - - useEffect(() => { - if (!isOpened && initialPageLoad) { - setInitialPageLoad(false); - } - }, [initialPageLoad, isOpened]); - - const handleScroll = throttle(() => { - const element = document.querySelector(".usda-page-header"); - if (element?.classList?.contains("usda-page-header--sticky")) { - setWindowHeight(window.innerHeight - 100); - setSidebarHeight(window.innerHeight - 100 - 178); - } - else { - setWindowHeight(window.innerHeight - 198); - setSidebarHeight(window.innerHeight - 198 - 178); - } - }, 50); - - useEffect(() => { - setWindowHeight(window.innerHeight - 198); - setSidebarHeight(window.innerHeight - 198 - 178); - window.addEventListener('scroll', handleScroll); - return () => window.removeEventListener('scroll', handleScroll); - }, [handleScroll]); - - useEffect(() => { - const handleResize = throttle(() => { - const newWidth = window.innerWidth; - if (windowWidth !== newWidth) { - setWindowWidth(newWidth); - } - }, 50); - window.addEventListener('resize', handleResize); - return () => window.removeEventListener('resize', handleResize); - }, [windowWidth]); - - return ( -
-
-
toggleOpened(e)} - onKeyDown={(e) => keyHandler(e, toggleOpened)} - role="button" - tabIndex="0"> - {isOpened ? - - : - - } -
-
-
-
goBack(e)} - onKeyDown={(e) => keyHandler(e, goBack)} - role="button" - tabIndex="0"> - Back -
-
- {drilldown?.children && } - - {drilldown?.component && } -
- -
-
- Search by... -
-
- {SearchFilterCategories.map((item) => ())} -
-
-
- -
-
-
); -}; - -export default SearchSidebar; diff --git a/src/js/components/search/filters/recipient/RecipientResultsContainer.jsx b/src/js/components/search/filters/recipient/RecipientResultsContainer.jsx index b577529fdf..600eadfb12 100644 --- a/src/js/components/search/filters/recipient/RecipientResultsContainer.jsx +++ b/src/js/components/search/filters/recipient/RecipientResultsContainer.jsx @@ -5,6 +5,7 @@ import React, { useEffect, useState, useRef } from 'react'; import PropTypes from "prop-types"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import * as SearchHelper from 'helpers/searchHelper'; import { EntityDropdownAutocomplete } from "../location/EntityDropdownAutocomplete"; import PrimaryCheckboxType from "../../../sharedComponents/checkbox/PrimaryCheckboxType"; @@ -17,6 +18,7 @@ const propTypes = { const RecipientResultsContainer = ({ selectedRecipients, updateSelectedRecipients }) => { const [recipients, setRecipients] = useState([]); const [searchString, setSearchString] = useState(''); + const [isLoading, setIsLoading] = useState(false); const recipientRequest = useRef(); @@ -43,11 +45,15 @@ const RecipientResultsContainer = ({ selectedRecipients, updateSelectedRecipient recipientRequest.current.cancel(); } - recipientRequest.current = SearchHelper.fetchRecipients(); - + const paramObj = { + limit: 100 + }; + recipientRequest.current = SearchHelper.fetchRecipients(paramObj); + setIsLoading(true); recipientRequest.current.promise .then((res) => { setRecipients(res.data.results); + setIsLoading(false); }); }; @@ -58,14 +64,15 @@ const RecipientResultsContainer = ({ selectedRecipients, updateSelectedRecipient const paramObj = { search_text: term, - limit: 50 + limit: 100 }; recipientRequest.current = SearchHelper.fetchRecipientsAutocomplete(paramObj); - + setIsLoading(true); recipientRequest.current.promise .then((res) => { setRecipients(res.data.results); + setIsLoading(false); }); }; @@ -83,6 +90,13 @@ const RecipientResultsContainer = ({ selectedRecipients, updateSelectedRecipient // eslint-disable-next-line react-hooks/exhaustive-deps }, [searchString]); + const loadingIndicator = ( +
+ +
Loading your data...
+
+ ); + return ( <> -
-
- { recipients.toSorted((a, b) => (a.name?.toUpperCase() < b.name?.toUpperCase() ? -1 : 1)).map((recipient) => ( -
- UEI: {recipient.uei ? recipient.uei : 'Not provided'}
)} - value={{ - name: recipient.name ? recipient.name : recipient.recipient_name, - uei: recipient.uei, - duns: recipient.duns ? recipient.duns : null - }} - key={recipient.uei} - toggleCheckboxType={toggleRecipient} - selectedCheckboxes={selectedRecipients} /> -
-
Legacy DUNS: {recipient.duns ? recipient.duns : 'Not provided'}
-
- {recipient.name || recipient.recipient_name} - {levelMapping[recipient.recipient_level]} + {isLoading ? loadingIndicator : +
+
+ { recipients.toSorted((a, b) => (a.name?.toUpperCase() < b.name?.toUpperCase() ? -1 : 1)).map((recipient) => ( +
+ UEI: {recipient.uei ? recipient.uei : 'Not provided'}
)} + value={{ + name: recipient.name ? recipient.name : recipient.recipient_name, + uei: recipient.uei, + duns: recipient.duns ? recipient.duns : null + }} + key={recipient.uei} + toggleCheckboxType={toggleRecipient} + selectedCheckboxes={selectedRecipients} /> +
+
Legacy DUNS: {recipient.duns ? recipient.duns : 'Not provided'}
+
+ {recipient.name || recipient.recipient_name} + {levelMapping[recipient.recipient_level]} +
-
- ))} -
-
+ ))} +
+
} ); }; diff --git a/src/js/components/search/filters/timePeriod/DateRange.jsx b/src/js/components/search/filters/timePeriod/DateRange.jsx index 37dc130519..a677f46aa2 100644 --- a/src/js/components/search/filters/timePeriod/DateRange.jsx +++ b/src/js/components/search/filters/timePeriod/DateRange.jsx @@ -45,7 +45,7 @@ const DateRange = (props) => { const [dropdownOptionSelected, setDropdownOptionSelected] = useState(false); const [noDates, setNoDates] = useState(false); const prevProps = usePrevious(props); - + const labelArray = []; const onClick = (e) => { setSelectedDropdownOption(e); @@ -71,10 +71,12 @@ const DateRange = (props) => { } }; - const localRemoveDateRange = () => { - setSelectedDropdownOption('select'); - setDropdownOptionSelected(false); - props.removeDateRange(); + const localRemoveDateRange = (e) => { + if (e.type === 'click' || e.key === "Enter") { + setSelectedDropdownOption('select'); + setDropdownOptionSelected(false); + props.removeDateRange(e); + } }; const dropdownOptions = [ @@ -206,35 +208,6 @@ const DateRange = (props) => { const startDateDisabledDays = generateStartDateDisabledDays(earliestDate); const endDateDisabledDays = generateEndDateDisabledDays(earliestDate); - const lastInTimePeriod = props.timePeriod[props.timePeriod.length - 1]; - - let dateLabel = ''; - let hideTags = 'hide'; - - if (props.timePeriod.length > 0) { - hideTags = ''; - let start = null; - let end = null; - - if (lastInTimePeriod.start_date) { - start = dayjs(lastInTimePeriod.start_date, 'YYYY-MM-DD').format('MM/DD/YYYY'); - } - if (lastInTimePeriod.end_date) { - end = dayjs(lastInTimePeriod.end_date, 'YYYY-MM-DD').format('MM/DD/YYYY'); - } - if (start && end) { - dateLabel = `${start} to ${end}`; - } - else if (start) { - dateLabel = `${start} to present`; - } - else if (end) { - dateLabel = `... to ${end}`; - } - else { - hideTags = 'hide'; - } - } const testDates = () => { if (props.startDate === null && props.endDate === null) { @@ -294,6 +267,34 @@ const DateRange = (props) => { /* eslint-disable-next-line react-hooks/exhaustive-deps */ }, [props.errorState, noDates, props.startDate, props.endDate]); + if (props.timePeriod?.size > 0) { + for (const timeinput of props.timePeriod) { + let dateLabel = ''; + let start = null; + let end = null; + + if (timeinput.start_date) { + start = dayjs(timeinput.start_date, 'YYYY-MM-DD').format('MM/DD/YYYY'); + } + if (timeinput.end_date) { + end = dayjs(timeinput.end_date, 'YYYY-MM-DD').format('MM/DD/YYYY'); + } + + if (start && end) { + dateLabel = `${start} to ${end}`; + } + else if (start) { + dateLabel = `${start} to present`; + } + else if (end) { + dateLabel = `... to ${end}`; + } + + if (dateLabel !== '') { + labelArray.push(dateLabel); + } + } + } return (
{
- + {labelArray.map((dateLabel, index) => + ( + + ) + )} +
); diff --git a/src/js/components/search/filters/timePeriod/TimePeriod.jsx b/src/js/components/search/filters/timePeriod/TimePeriod.jsx index 8cc7df6df4..6587506f56 100644 --- a/src/js/components/search/filters/timePeriod/TimePeriod.jsx +++ b/src/js/components/search/filters/timePeriod/TimePeriod.jsx @@ -36,6 +36,7 @@ const propTypes = { timePeriods: PropTypes.array, activeTab: PropTypes.string, updateFilter: PropTypes.func, + removeFilter: PropTypes.func, updateNewAwardsOnlySelected: PropTypes.func, updateNewAwardsOnlyActive: PropTypes.func, updateNaoActiveFromFyOrDateRange: PropTypes.func, @@ -98,8 +99,8 @@ export default class TimePeriod extends React.Component { determineIfNaoIsActive(prevProps, prevState) { if (prevProps.filterTimePeriodFY !== this.props.filterTimePeriodFY) { - this.props.updateNewAwardsOnlyActive(!!this.props.filterTimePeriodFY.size); - this.props.updateNaoActiveFromFyOrDateRange(!!this.props.filterTimePeriodFY.size); + this.props.updateNewAwardsOnlyActive(!!this.props.filterTimePeriodFY?.size); + this.props.updateNaoActiveFromFyOrDateRange(!!this.props.filterTimePeriodFY?.size); } if (this.props.dirtyFilters) { this.props.updateNewAwardsOnlyActive(true); @@ -123,12 +124,10 @@ export default class TimePeriod extends React.Component { // not filtering by a date range return; } - // prepopulate the date pickers with the current filter values (in the event of remounting // or loading from a URL) - const startDate = dayjs(this.props.filterTimePeriodStart, 'YYYY-MM-DD'); - const endDate = dayjs(this.props.filterTimePeriodEnd, 'YYYY-MM-DD'); - + const startDate = dayjs(null, 'YYYY-MM-DD'); + const endDate = dayjs(null, 'YYYY-MM-DD'); if (startDate.isValid() && endDate.isValid()) { this.setState({ startDateUI: startDate, @@ -191,7 +190,7 @@ export default class TimePeriod extends React.Component { }); } - removeDateRange() { + removeDateRange(e) { this.clearHint(true); this.props.updateFilter({ dateType: 'dr', @@ -203,6 +202,16 @@ export default class TimePeriod extends React.Component { startDateUI: null, endDateUI: null }); + + if (this.props.activeTab === 'dr') { + this.props.updateFilter({ + dateType: 'dr', + startDate: null, + endDate: null, + event: e, + removeFilter: true + }); + } } showError(error, message) { diff --git a/src/js/components/search/newResultsView/ResultsView.jsx b/src/js/components/search/newResultsView/ResultsView.jsx index 74d9337823..86883074ed 100644 --- a/src/js/components/search/newResultsView/ResultsView.jsx +++ b/src/js/components/search/newResultsView/ResultsView.jsx @@ -92,7 +92,6 @@ const ResultsView = (props) => { setResultContent(content); // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.noFiltersApplied, hasResults, subaward, waitForCheckForData]); - return (
diff --git a/src/js/components/search/topFilterBar/TopFilterBar.jsx b/src/js/components/search/topFilterBar/TopFilterBar.jsx index 2575b74e44..e947a219a8 100644 --- a/src/js/components/search/topFilterBar/TopFilterBar.jsx +++ b/src/js/components/search/topFilterBar/TopFilterBar.jsx @@ -35,7 +35,6 @@ const TopFilterBar = (props) => { filterBarHeader += 's'; } filterBarHeader += ':'; - const dispatch = useDispatch(); return ( diff --git a/src/js/components/search/topFilterBar/filterGroups/BaseTopFilterGroup.jsx b/src/js/components/search/topFilterBar/filterGroups/BaseTopFilterGroup.jsx index 696929a1a3..b524bdb1f5 100644 --- a/src/js/components/search/topFilterBar/filterGroups/BaseTopFilterGroup.jsx +++ b/src/js/components/search/topFilterBar/filterGroups/BaseTopFilterGroup.jsx @@ -24,7 +24,7 @@ export default class BaseTopFilterGroup extends React.Component { render() { const tags = this.props.tags.map((tag) => ( { + tags.push({ + value: 'dr', + title: value, + id: uniqueId() + }); + }); return tags; } render() { const tags = this.generateTags(); - return ( { }; } - const timePeriodFilter = { - timePeriodStart: params.filters.time_period[0].start_date, - timePeriodEnd: params.filters.time_period[0].end_date, - timePeriodType: 'dr' - }; + const timePeriodFilter = [{ start_date: params.filters.time_period[0].start_date, end_date: params.filters.time_period[0].end_date }]; const filterValue = { filters: { ...defaultFilters, ...categoryFilter, ...locationFilter, - ...timePeriodFilter, + timePeriodType: 'dr', + time_period: timePeriodFilter, ...awardTypeFilter }, version: REQUEST_VERSION diff --git a/src/js/components/state/StateContent.jsx b/src/js/components/state/StateContent.jsx index 9ca0a99c46..c02fa10f31 100644 --- a/src/js/components/state/StateContent.jsx +++ b/src/js/components/state/StateContent.jsx @@ -5,6 +5,8 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { FlexGridCol, FlexGridRow } from "data-transparency-ui"; + import StateTimeVisualizationSectionContainer from 'containers/state/StateTimeVisualizationSectionContainer'; import TopFiveSection from './topFive/TopFiveSection'; import StateOverview from './overview/StateOverview'; @@ -16,16 +18,16 @@ const propTypes = { }; const StateContent = ({ stateProfile }) => ( -
-
+ + -
-
+ + ); StateContent.propTypes = propTypes; diff --git a/src/js/containers/search/SearchContainer.jsx b/src/js/containers/search/SearchContainer.jsx index e686cdd447..662fd7dc3c 100644 --- a/src/js/containers/search/SearchContainer.jsx +++ b/src/js/containers/search/SearchContainer.jsx @@ -155,7 +155,7 @@ const SearchContainer = ({ history }) => { // eslint-disable-next-line no-console console.error('Error fetching filters from hash: ', err); // remove hash since corresponding filter selections aren't retrievable. - history.push('/search'); + history?.push('/search'); request.current = null; } }); diff --git a/src/js/containers/search/SearchSidebarSubmitContainer.jsx b/src/js/containers/search/SearchSidebarSubmitContainer.jsx index 3947bbfdad..7076ba8c80 100644 --- a/src/js/containers/search/SearchSidebarSubmitContainer.jsx +++ b/src/js/containers/search/SearchSidebarSubmitContainer.jsx @@ -3,7 +3,7 @@ * Created by Kevin Li 12/21/17 */ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; @@ -18,6 +18,7 @@ import { sendAnalyticEvents, sendFieldCombinations } from './helpers/searchAnalytics'; +import { usePrevious } from "../../helpers"; const combinedActions = Object.assign( {}, @@ -38,90 +39,77 @@ const propTypes = { resetAppliedFilters: PropTypes.func }; -export class SearchSidebarSubmitContainer extends React.Component { - constructor(props) { - super(props); +const SearchSidebarSubmitContainer = (props) => { + const [filtersChanged, setFiltersChanged] = useState(false); + const prevProps = usePrevious(props); - this.state = { - filtersChanged: false - }; + const areStagedFiltersEmpty = () => areFiltersEqual(props.stagedFilters, initialState); - this.resetFilters = this.resetFilters.bind(this); - this.applyStagedFilters = this.applyStagedFilters.bind(this); - } - - componentDidUpdate(prevProps) { - const areStagedAndAppliedFiltersEquivalent = ( - areFiltersEqual(this.props.stagedFilters, prevProps.stagedFilters) && - areFiltersEqual(this.props.appliedFilters, prevProps.appliedFilters) - ); - if (!areStagedAndAppliedFiltersEquivalent) { - this.stagingChanged(); - } - } - - areStagedFiltersEmpty = () => areFiltersEqual(this.props.stagedFilters, initialState); - - compareStores() { + const compareStores = () => { // we need to do a deep equality check by comparing every store key - const storeKeys = Object.keys(this.props.stagedFilters); - if (storeKeys.length !== Object.keys(this.props.appliedFilters).length) { + const storeKeys = Object.keys(props.stagedFilters); + if (storeKeys.length !== Object.keys(props.appliedFilters).length) { // key lengths do not match, there's a difference so fail immediately return false; } - return areFiltersEqual(this.props.stagedFilters, this.props.appliedFilters); - } + return areFiltersEqual(props.stagedFilters, props.appliedFilters); + }; + + const resetFilters = () => { + props.clearStagedFilters(); + props.resetAppliedFilters(); + props.resetMapLegendToggle(); + }; - stagingChanged() { + const stagingChanged = () => { // do a deep equality check between the staged filters and applied filters - if (!this.compareStores()) { - this.setState({ - filtersChanged: true - }); + if (!compareStores()) { + setFiltersChanged(true); } - else if (this.state.filtersChanged) { - this.setState({ - filtersChanged: false - }); + else if (filtersChanged) { + setFiltersChanged(false); } - } + }; - applyStagedFilters() { - this.props.setAppliedFilterCompletion(false); + const applyStagedFilters = () => { + props.setAppliedFilterCompletion(false); - if (areFiltersEqual(this.props.stagedFilters)) { - this.resetFilters(); + if (areFiltersEqual(props.stagedFilters)) { + resetFilters(); } else { - this.props.applyStagedFilters(this.props.stagedFilters); - this.props.setAppliedFilterCompletion(true); + props.applyStagedFilters(props.stagedFilters); + props.setAppliedFilterCompletion(true); } - this.setState({ - filtersChanged: false - }); + setFiltersChanged(false); - const events = convertFiltersToAnalyticEvents(this.props.stagedFilters); + const events = convertFiltersToAnalyticEvents(props.stagedFilters); sendAnalyticEvents(events); sendFieldCombinations(events); - } - - resetFilters() { - this.props.clearStagedFilters(); - this.props.resetAppliedFilters(); - this.props.resetMapLegendToggle(); - } - - render() { - return ( - + }; + + useEffect(() => { + const areStagedAndAppliedFiltersEquivalent = ( + areFiltersEqual(props.stagedFilters, prevProps?.stagedFilters) && + areFiltersEqual(props.appliedFilters, prevProps?.appliedFilters) ); - } -} + if (!areStagedAndAppliedFiltersEquivalent) { + stagingChanged(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.stagedFilters, props.appliedFilters]); + + return ( + + ); +}; + +SearchSidebarSubmitContainer.propTypes = propTypes; export default connect( (state) => ({ @@ -139,4 +127,3 @@ export default connect( }) )(SearchSidebarSubmitContainer); -SearchSidebarSubmitContainer.propTypes = propTypes; diff --git a/src/js/containers/search/filters/TimePeriodContainer.jsx b/src/js/containers/search/filters/TimePeriodContainer.jsx index 60ff6cac1f..e4db061bd0 100644 --- a/src/js/containers/search/filters/TimePeriodContainer.jsx +++ b/src/js/containers/search/filters/TimePeriodContainer.jsx @@ -18,8 +18,8 @@ import TimePeriod from 'components/search/filters/timePeriod/TimePeriod'; export const startYear = FiscalYearHelper.earliestFiscalYear; const propTypes = { - updateTimePeriod: PropTypes.func, updateTimePeriodArray: PropTypes.func, + setTimePeriodArray: PropTypes.func, filterTimePeriodType: PropTypes.string, filterTimePeriodFY: PropTypes.instanceOf(Set), filterTimePeriodStart: PropTypes.string, @@ -61,33 +61,32 @@ const TimePeriodContainer = (props) => { if (activeTab === 'fy') { newFilters.dateType = 'fy'; - // reset the date range values - newFilters.startDate = null; - newFilters.endDate = null; + props.updateTimePeriod(newFilters); } else { // reset the fiscal year set // start and end dates and datetype are in params newFilters.fy = []; + props.updateTimePeriodArray(newFilters); } + }; - props.updateTimePeriod(newFilters); - - // here is where the time_period array gets updated - props.updateTimePeriodArray(newFilters); + const removeFilter = (toRemove) => { + if (toRemove.target) { + const indexToRemove = toRemove.target.getAttribute('index'); + const timePeriodArray = props.filterTime_Period.toArray(); + timePeriodArray.splice(indexToRemove, 1); + props.setTimePeriodArray(timePeriodArray); + } }; const dirtyFilters = () => { const appliedFields = [ 'timePeriodFY', - 'timePeriodStart', - 'timePeriodEnd', 'time_period' ]; const activeFields = [ 'filterTimePeriodFY', - 'filterTimePeriodStart', - 'filterTimePeriodEnd', 'filterTime_Period' ]; @@ -142,6 +141,7 @@ const TimePeriodContainer = (props) => { activeTab={activeTab} timePeriods={timePeriods} updateFilter={updateFilter} + removeFilter={removeFilter} changeTab={changeTab} /> ); }; @@ -152,9 +152,7 @@ export default connect( (state) => ({ filterTimePeriodType: state.filters.timePeriodType, filterTimePeriodFY: state.filters.timePeriodFY, - filterTimePeriodStart: state.filters.timePeriodStart, filterTime_Period: state.filters.time_period, - filterTimePeriodEnd: state.filters.timePeriodEnd, newAwardsOnlySelected: state.filters.filterNewAwardsOnlySelected, newAwardsOnlyActive: state.filters.filterNewAwardsOnlyActive, naoActiveFromFyOrDateRange: state.filters.filterNaoActiveFromFyOrDateRange, diff --git a/src/js/containers/search/filters/programSource/TASCheckboxTreeContainer.jsx b/src/js/containers/search/filters/programSource/TASCheckboxTreeContainer.jsx index dcb7c2c019..c26cb79dc5 100644 --- a/src/js/containers/search/filters/programSource/TASCheckboxTreeContainer.jsx +++ b/src/js/containers/search/filters/programSource/TASCheckboxTreeContainer.jsx @@ -59,7 +59,12 @@ const propTypes = { nodes: PropTypes.arrayOf(PropTypes.object), searchExpanded: PropTypes.arrayOf(PropTypes.string), counts: PropTypes.arrayOf(PropTypes.shape({})), - filters: PropTypes.object + filters: PropTypes.object, + showInfo: PropTypes.bool +}; + +const defaultProps = { + showInfo: true }; const SearchNote = () => ( @@ -374,7 +379,8 @@ export class TASCheckboxTree extends React.Component { checked, expanded, counts, - searchExpanded + searchExpanded, + showInfo } = this.props; const { isLoading, @@ -386,12 +392,13 @@ export class TASCheckboxTree extends React.Component { } = this.state; return (
+ {showInfo && Search by Agency, Federal Account, or Treasury Account } heading="Find a Treasury Account" /> - + } ({ nodes: state.tas.tas.toJS(), diff --git a/src/js/containers/search/filters/recipient/RecipientTypeContainer.jsx b/src/js/containers/search/filters/recipient/RecipientTypeContainer.jsx index 2c814b2ec6..f565a280f0 100644 --- a/src/js/containers/search/filters/recipient/RecipientTypeContainer.jsx +++ b/src/js/containers/search/filters/recipient/RecipientTypeContainer.jsx @@ -17,7 +17,9 @@ const propTypes = { appliedType: PropTypes.object }; -const RecipientTypeContainer = (props) => { +const RecipientTypeContainer = ({ + toggleRecipientType, recipientType, appliedType +}) => { let justMounted = true; const firstUpdate = useRef(true); @@ -29,12 +31,12 @@ const RecipientTypeContainer = (props) => { justMounted = false; }, []); - const toggleRecipientType = (selection) => { - props.toggleRecipientType(selection); + const toggleRecipientTypeFunc = (selection) => { + toggleRecipientType(selection); }; const dirtyFilters = () => { - if (justMounted || is(props.recipientType, props.appliedType)) { + if (justMounted || is(recipientType, appliedType)) { return null; } return Symbol('dirty recipient type'); @@ -43,8 +45,8 @@ const RecipientTypeContainer = (props) => { return ( + selectedTypes={recipientType} + toggleCheckboxType={toggleRecipientTypeFunc} /> ); }; diff --git a/src/js/containers/search/helpers/searchAnalytics.js b/src/js/containers/search/helpers/searchAnalytics.js index 3c980c8511..53b846fbcf 100644 --- a/src/js/containers/search/helpers/searchAnalytics.js +++ b/src/js/containers/search/helpers/searchAnalytics.js @@ -234,7 +234,7 @@ export const unifyDateFields = (redux) => { clonedRedux.timePeriod = clonedRedux.timePeriodFY; } else { - clonedRedux.timePeriod = [clonedRedux.timePeriodStart, clonedRedux.timePeriodEnd]; + clonedRedux.timePeriod = clonedRedux.time_period; } return clonedRedux; }; diff --git a/src/js/containers/search/topFilterBar/TopFilterBarContainer.jsx b/src/js/containers/search/topFilterBar/TopFilterBarContainer.jsx index 30fcdb1a0c..d35ca24758 100644 --- a/src/js/containers/search/topFilterBar/TopFilterBarContainer.jsx +++ b/src/js/containers/search/topFilterBar/TopFilterBarContainer.jsx @@ -65,7 +65,9 @@ const TopFilterBarContainer = (props) => { */ const prepareTimeFilter = () => { let selected = false; - const filter = {}; + const filter = { + values: [] + }; if (props.filters?.timePeriodType === 'fy') { // check to see if any FYs are selected if (props.filters.timePeriodFY?.size > 0) { @@ -79,26 +81,33 @@ const TopFilterBarContainer = (props) => { } } else if (props.filters?.timePeriodType === 'dr') { - const lastInTimePeriod = props.filters.time_period[props.filters.time_period.length - 1]; - // check to see if any date ranges are selected - if (lastInTimePeriod?.start_date || lastInTimePeriod?.end_date) { + // const lastInTimePeriod = props.filters.time_period[props.filters.time_period.length - 1]; + // // check to see if any date ranges are selected + if (props.filters.time_period.size > 0) { // start and end dates are provided selected = true; filter.code = 'timePeriodDR'; filter.name = 'Time Period'; - - const startString = dayjs(lastInTimePeriod.start_date, 'YYYY-MM-DD') - .format('MM/DD/YYYY'); - const endString = dayjs(lastInTimePeriod.end_date, 'YYYY-MM-DD').format('MM/DD/YYYY'); - filter.values = [`${startString} to ${endString}`]; - - if (!lastInTimePeriod.start_date) { - // open-ended start date - filter.values = [`... to ${endString}`]; - } - else if (!lastInTimePeriod.end_date) { - // open-ended end date - filter.values = [`${startString} to present`]; + for (const period of props.filters.time_period) { + let startString; + let endString; + + if (period.start_date) { + startString = dayjs(period.start_date, 'YYYY-MM-DD').format('MM/DD/YYYY'); + } + + if (period.end_date) { + endString = dayjs(period.end_date, 'YYYY-MM-DD').format('MM/DD/YYYY'); + } + if (period.start_date && period.end_date) { + filter.values.push([`${startString} to ${endString}`]); + } else if (period.start_date) { + // open-ended end date + filter.values.push([`${startString} to present`]); + } else if (period.end_date) { + // open-ended start date + filter.values.push([`... to ${endString}`]); + } } } } diff --git a/src/js/dataMapping/search/newSearchFilterCategories.jsx b/src/js/dataMapping/search/newSearchFilterCategories.jsx deleted file mode 100644 index 8a73bde669..0000000000 --- a/src/js/dataMapping/search/newSearchFilterCategories.jsx +++ /dev/null @@ -1,123 +0,0 @@ -/** - * SearchFilterCategories.jsx - * Created by Andrea Blackwell November 4, 2024 - */ - -import React from 'react'; -import { LocationSectionContainer } from "../../containers/search/filters/location/LocationSectionContainer"; - -export const SearchFilterCategories = [ - { - categoryKey: 'location', - iconName: 'map-marked-alt', - iconColor: '#34a37e', - iconBackgroundColor: '#dbf6ed', - title: 'Location', - description: 'Find awards by recipient location or where work is being done' - }, - { - categoryKey: 'timePeriod', - iconName: 'calendar-alt', - iconColor: '#1A4480', - iconBackgroundColor: '#edf5ff', - title: 'Time Period', - description: 'Find awards by specific date or date range' - }, - { - categoryKey: 'characteristics', - iconName: 'cubes', - iconColor: '#ff580a', - iconBackgroundColor: '#fff3ea', - title: 'Characteristics', - description: 'Find awards by award type, ID, industry code, and more' - }, - { - categoryKey: 'recipients', - iconName: 'building', - iconColor: '#1b2b85', - iconBackgroundColor: '#edf0ff', - title: 'Recipients', - description: 'Find awards by business, nonprofit, other organization, and more' - }, - { - categoryKey: 'sources', - iconName: 'university', - iconColor: '#009ec1', - iconBackgroundColor: '#e5faff', - title: 'Sources', - description: 'Find awards by the source of funding (agency, Treasury Account Symbol, or Disaster Emergency Fund Code)' - } -]; - -export const FilterCategoryTree = { - location: { - title: 'Location', - component: - }, - timePeriod: { - title: 'Time Period', - component: null - }, - characteristics: { - children: [ - { - title: 'Award Description' - }, - { - title: 'Award ID' - }, - { - title: 'Spending Amount' - }, - { - title: 'Contract Award Type' - }, - { - title: 'North American Industry Classification System (NAICS)' - }, - { - title: 'Product and Service Code (PSC)' - }, - { - title: 'Type of Contract Pricing' - }, - { - title: 'Type of Set Aside' - }, - { - title: 'Extent Competed' - }, - { - title: 'Financial Assistance Award Type' - } - - ] - - }, - recipient: { - children: [ - { - title: 'Recipient' - }, - { - title: 'Recipient Type' - } - ] - }, - sources: { - children: [ - { - title: 'Agency' - }, - { - title: 'Treasury Account Symbol (TAS)' - }, - { - title: 'COVID-19 Spending' - }, - { - title: 'Infrastructure Spending' - } - ] - } -}; diff --git a/src/js/dataMapping/search/searchFilterCategories.jsx b/src/js/dataMapping/search/searchFilterCategories.jsx new file mode 100644 index 0000000000..655ec7b9eb --- /dev/null +++ b/src/js/dataMapping/search/searchFilterCategories.jsx @@ -0,0 +1,161 @@ +/** + * SearchFilterCategories.jsx + * Created by Andrea Blackwell November 4, 2024 + */ + +import React from 'react'; +import { LocationSectionContainer } from "../../containers/search/filters/location/LocationSectionContainer"; +import TimePeriodContainer from "../../containers/search/filters/TimePeriodContainer"; +import AwardIDSearchContainer from "../../containers/search/filters/awardID/AwardIDSearchContainer"; +import AgencyContainer from "../../containers/search/filters/AgencyContainer"; +import TASCheckboxTreeContainer from "../../containers/search/filters/programSource/TASCheckboxTreeContainer"; +import RecipientSearchContainer from "../../containers/search/filters/recipient/RecipientSearchContainer"; +import RecipientTypeContainer from "../../containers/search/filters/recipient/RecipientTypeContainer"; + +export const SearchFilterCategories = [ + { + categoryKey: 'location', + iconName: 'map-marked-alt', + iconColor: '#34a37e', + iconBackgroundColor: '#dbf6ed', + title: 'Location', + description: 'Find awards by recipient location or where work is being done' + }, + { + categoryKey: 'timePeriod', + iconName: 'calendar-alt', + iconColor: '#1A4480', + iconBackgroundColor: '#edf5ff', + title: 'Time Period', + description: 'Find awards by specific date or date range' + }, + { + categoryKey: 'characteristics', + iconName: 'cubes', + iconColor: '#ff580a', + iconBackgroundColor: '#fff3ea', + title: 'Characteristics', + description: 'Find awards by award type, ID, industry code, and more' + }, + { + categoryKey: 'recipients', + iconName: 'building', + iconColor: '#1b2b85', + iconBackgroundColor: '#edf0ff', + title: 'Recipients', + description: 'Find awards by business, nonprofit, other organization, and more' + }, + { + categoryKey: 'sources', + iconName: 'university', + iconColor: '#009ec1', + iconBackgroundColor: '#e5faff', + title: 'Sources', + description: 'Find awards by the source of funding (agency, Treasury Account Symbol, or Disaster Emergency Fund Code)' + } +]; + +export const FilterCategoryTree = { + location: { + title: 'Location', + component: + }, + timePeriod: { + title: 'Time Period', + component: + }, + characteristics: { + children: [ + { + categoryType: 'ALL', + categories: [ + { + title: 'Award Description' + }, + { + title: 'Award ID', + component: + }, + { + title: 'Spending Amount' + } + ] + }, + { + categoryType: 'CONTRACTS', + categories: [ + { + title: 'Contract Award Type' + }, + { + title: 'North American Industry Classification System (NAICS)' + }, + { + title: 'Product and Service Code (PSC)' + }, + { + title: 'Type of Contract Pricing' + }, + { + title: 'Type of Set Aside' + }, + { + title: 'Extent Competed' + } + ] + }, + { + categoryType: 'FINANCIAL ASSISTANCE', + categories: [ + { + title: 'Financial Assistance Award Type' + }, + { + title: 'Assistance Listing' + } + ] + } + ] + + }, + recipients: { + children: [ + { + title: 'Recipient', + component: + }, + { + title: 'Recipient Type', + component: + } + ] + }, + sources: { + children: [ + { + categoryType: 'doNotDisplay', + categories: [ + { + title: 'Agency', + component: + }, + { + title: 'Treasury Account Symbol (TAS)', + component: + } + ] + }, + { + categoryType: 'DISASTER EMERGENCY FUND CODE (DEFC)', + categories: [ + { + title: 'COVID-19 Spending' + }, + { + title: 'Infrastructure Spending' + } + ] + } + ] + } +}; diff --git a/src/js/helpers/searchHelper.js b/src/js/helpers/searchHelper.js index 0e439f8d1e..aae7b03903 100644 --- a/src/js/helpers/searchHelper.js +++ b/src/js/helpers/searchHelper.js @@ -82,9 +82,10 @@ export const fetchAwardV2 = (awardId) => apiRequest({ url: `v2/awards/${awardId}/` }); -export const fetchRecipients = () => apiRequest({ +export const fetchRecipients = (req) => apiRequest({ url: 'v2/recipient/', - method: 'post' + method: 'post', + data: req }); // Recipient search for autocomplete @@ -180,10 +181,8 @@ export const areFiltersEqual = (filters = initialState, filterReference = initia if (referenceObject.timePeriodType === 'fy') { // if the time period is fiscal year, we don't care about the date range values, even // if they're provided because the date range tab isn't selected - delete comparisonObject.timePeriodStart; - delete comparisonObject.timePeriodEnd; - delete referenceObject.timePeriodStart; - delete referenceObject.timePeriodEnd; + delete comparisonObject.time_period; + delete referenceObject.time_period; } else if (referenceObject.timePeriodEnd === 'dr') { // if the time period is date range, we don't care about the fiscal year values, even diff --git a/src/js/models/v1/search/SearchAwardsOperation.js b/src/js/models/v1/search/SearchAwardsOperation.js index 8f34ca9826..58a58b9909 100644 --- a/src/js/models/v1/search/SearchAwardsOperation.js +++ b/src/js/models/v1/search/SearchAwardsOperation.js @@ -55,14 +55,10 @@ class SearchAwardsOperation { fromState(state) { this.keyword = state.keyword.toArray(); - this.time_period = state.time_period; + this.time_period = state.time_period.toArray(); this.timePeriodFY = state.timePeriodFY.toArray(); this.timePeriodRange = []; this.timePeriodType = state.timePeriodType; - if (state.timePeriodType === 'dr' && (state.timePeriodStart || state.timePeriodEnd)) { - this.timePeriodRange = [state.timePeriodStart, state.timePeriodEnd]; - this.timePeriodFY = []; - } this.dateType = state.filterNewAwardsOnlySelected; @@ -109,18 +105,13 @@ class SearchAwardsOperation { } toParams() { - // Convert the search operation into JS objects + // Convert the search operation into JS objects const filters = {}; // Add keyword if (this.keyword?.length > 0) { filters[rootKeys.keywords] = this.keyword; } - // add new time_period - if (this.time_period?.length > 0) { - filters[rootKeys.time_period] = this.time_period; - } - // Add Time Period if (this.timePeriodFY?.length > 0 || this.time_period?.length > 0) { if (this.timePeriodType === 'fy' && this.timePeriodFY?.length > 0) { @@ -134,29 +125,31 @@ class SearchAwardsOperation { }); } else if (this.timePeriodType === 'dr' && this.time_period?.length > 0) { - const lastInTimePeriod = this.time_period[this.time_period?.length - 1]; - - let start = lastInTimePeriod.start_date; - let end = lastInTimePeriod.end_date; - // if no start or end date is provided, use the 2008-present date range to fill out // the missing dates const initialYear = FiscalYearHelper.earliestFiscalYear; const currentYear = FiscalYearHelper.currentFiscalYear(); + const values = []; + this.time_period.forEach((time) => { + let start = time.start_date; + let end = time.end_date; - if (!start) { - start = FiscalYearHelper.convertFYToDateRange(initialYear)[0]; - } - if (!end) { - end = FiscalYearHelper.convertFYToDateRange(currentYear)[1]; - } - - filters[rootKeys.timePeriod] = [ - { - [timePeriodKeys.startDate]: start, - [timePeriodKeys.endDate]: end + if (!start) { + start = FiscalYearHelper.convertFYToDateRange(initialYear)[0]; + } + if (!end) { + end = FiscalYearHelper.convertFYToDateRange(currentYear)[1]; } - ]; + + values.push( + { + [timePeriodKeys.startDate]: start, + [timePeriodKeys.endDate]: end + } + ); + }); + + filters[rootKeys.timePeriod] = values; } } diff --git a/src/js/redux/actions/search/searchFilterActions.js b/src/js/redux/actions/search/searchFilterActions.js index df47db1dc3..a829f05573 100644 --- a/src/js/redux/actions/search/searchFilterActions.js +++ b/src/js/redux/actions/search/searchFilterActions.js @@ -27,7 +27,9 @@ export const updateTimePeriodArray = (state) => ({ dateType: state.dateType, fy: state.fy, start: state.startDate, - end: state.endDate + end: state.endDate, + removeFilter: state.removeFilter, + event: state.event }); export const updateNewAwardsOnlySelected = (state) => ({ diff --git a/src/js/redux/reducers/search/filters/timePeriodFilterFunctions.js b/src/js/redux/reducers/search/filters/timePeriodFilterFunctions.js index 760a1e2d69..2befd908cf 100644 --- a/src/js/redux/reducers/search/filters/timePeriodFilterFunctions.js +++ b/src/js/redux/reducers/search/filters/timePeriodFilterFunctions.js @@ -5,14 +5,26 @@ /* eslint-disable import/prefer-default-export */ // We only have one export but want to maintain consistency with other query modules -export const updateSelectedDates = (currentDates, date) => { - if (date.start || date.end) { - currentDates.push({ +export const updateDRs = (currentDates, date) => { + let updatedSet = currentDates; + // remove clicked index + if (date.removeFilter && date.event.target.getAttribute("index")) { + let i = 0; + for (const item of updatedSet) { + if (i === parseInt(date.event.target.getAttribute("index"), 10)) { + updatedSet = currentDates.delete(item); + } + i++; + } + } else if (date.start || date.end) { + updatedSet = updatedSet.add({ start_date: date.start, end_date: date.end }); } - return currentDates; + return updatedSet; }; + +export const setDR = (newDR) => newDR; /* eslint-enable import/prefer-default-export */ diff --git a/src/js/redux/reducers/search/searchFiltersReducer.js b/src/js/redux/reducers/search/searchFiltersReducer.js index 6d6ca2012f..353b54427d 100644 --- a/src/js/redux/reducers/search/searchFiltersReducer.js +++ b/src/js/redux/reducers/search/searchFiltersReducer.js @@ -14,7 +14,7 @@ import * as AwardAmountFilterFunctions from './filters/awardAmountFilterFunction import * as OtherFilterFunctions from './filters/OtherFilterFunctions'; import * as ContractFilterFunctions from './filters/contractFilterFunctions'; import * as ProgramSourceFilterFunctions from './filters/programSourceFilterFunctions'; - +import * as TimePeriodFilterFunctions from './filters/timePeriodFilterFunctions'; // update this version when changes to the reducer structure are made // frontend will reject inbound hashed search filter sets with different versions because the // data structures may have changed @@ -32,6 +32,7 @@ export const requiredTypes = { selectedFundingAgencies: OrderedMap, selectedAwardingAgencies: OrderedMap, selectedRecipients: Set, + time_period: Set, recipientType: Set, selectedRecipientLocations: OrderedMap, awardType: Set, @@ -52,9 +53,7 @@ export const initialState = { keyword: OrderedMap(), timePeriodType: 'dr', timePeriodFY: Set(), - time_period: [], - timePeriodStart: null, - timePeriodEnd: null, + time_period: Set(), filterNewAwardsOnlySelected: false, filterNewAwardsOnlyActive: false, filterNaoActiveFromFyOrDateRange: false, @@ -95,19 +94,16 @@ const searchFiltersReducer = (state = initialState, action) => { // FY time period is stored as an ImmutableJS set return Object.assign({}, state, { timePeriodType: action.dateType, - timePeriodStart: action.start, - timePeriodEnd: action.end, timePeriodFY: new Set(action.fy) }); } - // New Time Period Filter Array + // New Time Period Filter Item case 'ADD_TIME_PERIOD_OBJECT': { return Object.assign({}, state, { - time_period: state.time_period.concat({ - start_date: action.start, - end_date: action.end - }) + timePeriodType: action.dateType, + time_period: TimePeriodFilterFunctions.updateDRs( + state.time_period, action) }); } @@ -308,8 +304,7 @@ const searchFiltersReducer = (state = initialState, action) => { return Object.assign({}, state, { timePeriodType: initialState.timePeriodType, timePeriodFY: initialState.timePeriodFY, - timePeriodStart: initialState.timePeriodStart, - timePeriodEnd: initialState.timePeriodEnd + time_period: initialState.time_period }); } case 'RESET_TIME_PERIOD_OBJECT': { diff --git a/tests/containers/search/helpers/searchAnalytics-test.js b/tests/containers/search/helpers/searchAnalytics-test.js index 474eca099d..0af9ca608f 100644 --- a/tests/containers/search/helpers/searchAnalytics-test.js +++ b/tests/containers/search/helpers/searchAnalytics-test.js @@ -201,11 +201,10 @@ describe('searchAnalytics', () => { it('should set the `timePeriod` field to an array of `timePeriodStart` and `timePeriodEnd` filters when a date range is selected', () => { const filters = { timePeriodType: 'dr', - timePeriodStart: '1900-01-01', - timePeriodEnd: '1900-02-01' + time_period: new Set([{ start_date: '1900-01-01', end_date: '1900-02-01' }]) }; const redux = searchAnalytics.unifyDateFields(filters); - expect(redux.timePeriod).toEqual(['1900-01-01', '1900-02-01']); + expect(redux.timePeriod).toEqual(new Set([{ start_date: '1900-01-01', end_date: '1900-02-01' }])); }); }); diff --git a/tests/containers/search/mockSearchHashes.js b/tests/containers/search/mockSearchHashes.js index b2374c7319..229888eb59 100644 --- a/tests/containers/search/mockSearchHashes.js +++ b/tests/containers/search/mockSearchHashes.js @@ -18,14 +18,13 @@ export const mockFilters = { selectedLocations: {}, recipientType: [], timePeriodFY: [`${FiscalYearHelper.currentFiscalYear()}`], + time_period: [], timePeriodType: "fy", - timePeriodStart: null, selectedAwardingAgencies: {}, awardType: [], recipientDomesticForeign: "all", selectedRecipientLocations: {}, awardAmounts: {}, - timePeriodEnd: null, selectedCFDA: {}, pricingType: [], setAside: [], diff --git a/tests/redux/reducers/search/searchFiltersReducer-test.js b/tests/redux/reducers/search/searchFiltersReducer-test.js index daabeeaf65..7673ba2bdc 100644 --- a/tests/redux/reducers/search/searchFiltersReducer-test.js +++ b/tests/redux/reducers/search/searchFiltersReducer-test.js @@ -109,26 +109,45 @@ describe('searchFiltersReducer', () => { const action = { type: 'UPDATE_SEARCH_FILTER_TIME_PERIOD', dateType: 'fy', - fy: ['1778', '1777', '1775'], - start: null, - end: null + fy: ['1778', '1777', '1775'] }; const expected = { timePeriodType: 'fy', - timePeriodFY: new Set(['1778', '1777', '1775']), - timePeriodStart: null, - timePeriodEnd: null + timePeriodFY: new Set(['1778', '1777', '1775']) }; - const updatedState = searchFiltersReducer(undefined, action); + const updatedState = searchFiltersReducer(null, action); + + Object.keys(expected).forEach((key) => { + expect(updatedState[key]).toEqual(expected[key]); + }); + }); + }); + + describe('ADD_TIME_PERIOD_OBJECT', () => { + it('should set the time period value to the provided action date', () => { + const startingState = Object.assign({}, initialState); + const action = { + type: 'ADD_TIME_PERIOD_OBJECT', + dateType: 'dr', + start: '1776-01-01', + end: '1776-12-31' + }; + + const expected = { + timePeriodType: 'dr', + time_period: new Set([{ start_date: '1776-01-01', end_date: '1776-12-31' }]) + }; + const updatedState = searchFiltersReducer(startingState, action); Object.keys(expected).forEach((key) => { expect(updatedState[key]).toEqual(expected[key]); }); }); }); + describe('UPDATE_SEARCH_FILTER_NEW_AWARDS_ONLY_SELECTED', () => { it('should set the filterNewAwardsOnlySelected value to the provided action data', () => { const action = { @@ -419,6 +438,7 @@ describe('searchFiltersReducer', () => { describe('UPDATE_RECIPIENT_DOMESTIC_FORIEGN', () => { it( + // eslint-disable-next-line no-useless-concat 'should set the Recipient domestic/foreign filter ' + 'scope to the input string', () => { const action = { @@ -824,8 +844,7 @@ describe('searchFiltersReducer', () => { type: 'UPDATE_SEARCH_FILTER_TIME_PERIOD', dateType: 'fy', fy: ['1778', '1777', '1775'], - start: null, - end: null + time_period: new Set() }; const resetAction = { @@ -834,16 +853,12 @@ describe('searchFiltersReducer', () => { const expectedFirst = { timePeriodType: 'fy', - timePeriodFY: new Set(['1778', '1777', '1775']), - timePeriodStart: null, - timePeriodEnd: null + timePeriodFY: new Set(['1778', '1777', '1775']) }; const expectedSecond = { timePeriodType: 'dr', - timePeriodFY: new Set(), - timePeriodStart: null, - timePeriodEnd: null + timePeriodFY: new Set() }; // perform the first action to change the time period filter values @@ -870,8 +885,7 @@ describe('searchFiltersReducer', () => { type: 'UPDATE_SEARCH_FILTER_TIME_PERIOD', dateType: 'dr', fy: [], - start: '1776-01-01', - end: '1776-12-31' + time_period: new Set([{ start_date: '1776-01-01', end_date: '1776-12-31' }]) }; const resetAction = { @@ -881,15 +895,13 @@ describe('searchFiltersReducer', () => { const expectedFirst = { timePeriodType: 'dr', timePeriodFY: new Set(), - timePeriodStart: '1776-01-01', - timePeriodEnd: '1776-12-31' + time_period: new Set() }; const expectedSecond = { timePeriodType: 'dr', timePeriodFY: new Set(), - timePeriodStart: null, - timePeriodEnd: null + time_period: new Set() }; // perform the first action to change the time period filter values @@ -948,17 +960,13 @@ describe('searchFiltersReducer', () => { const secondAction = { type: 'UPDATE_SEARCH_FILTER_TIME_PERIOD', dateType: 'fy', - fy: ['1778', '1777', '1775'], - start: null, - end: null + fy: ['1778', '1777', '1775'] }; const firstExpected = 'domestic'; const secondExpected = { timePeriodType: 'fy', - timePeriodFY: new Set(['1778', '1777', '1775']), - timePeriodStart: null, - timePeriodEnd: null + timePeriodFY: new Set(['1778', '1777', '1775']) }; // perform the first action that updates the domestic/foreign scope diff --git a/tests/testResources/defaultReduxFilters.js b/tests/testResources/defaultReduxFilters.js index b32a958d58..af3d2b8697 100644 --- a/tests/testResources/defaultReduxFilters.js +++ b/tests/testResources/defaultReduxFilters.js @@ -14,11 +14,9 @@ import * as FiscalYearHelper from '../../src/js/helpers/fiscalYearHelper'; export const defaultFilters = { keyword: new OrderedMap(), awardType: new Set(), - time_period: [], + time_period: new Set(), timePeriodType: 'dr', timePeriodFY: new Set([`${FiscalYearHelper.currentFiscalYear()}`]), - timePeriodStart: null, - timePeriodEnd: null, selectedLocations: new OrderedMap(), locationDomesticForeign: 'all', budgetFunctions: new OrderedMap(),