diff --git a/cypress/integration/pages/organizations.spec.js b/cypress/integration/pages/organizations.spec.js index 2dcf6313..defc327f 100644 --- a/cypress/integration/pages/organizations.spec.js +++ b/cypress/integration/pages/organizations.spec.js @@ -62,12 +62,16 @@ describe('Organizations Page (using API)', () => { cy.get('[data-cy=organization-search]').type(affiliatedOrg).type('{enter}'); cy.get('[data-cy=affiliated-organizations]').within(() => { cy.get('[data-cy=thumbnail-dropdown]').should('have.length', 1); - cy.get('[data-cy=contributor-thumbnail-text]').contains(affiliatedOrg); + cy.get('[data-cy=thumbnail-dropdown]').eq(0).click().within(() => { + cy.get('[data-cy=contributor-thumbnail-text]').contains(affiliatedOrg); + }); }); cy.get('[data-cy=affiliated-organizations]').within(() => { cy.get('[data-cy=thumbnail-dropdown]').should('have.length', parentOrgCount2); }); - cy.get('[data-cy=organization-search]').clear(); + cy.get('[data-cy=organization-search]').within(() => { + cy.get('input').clear(); + }); }); it('should find affiliatedOrg via partial search', () => { @@ -81,9 +85,13 @@ describe('Organizations Page (using API)', () => { cy.get('[data-cy=organization-search-list]').click(); cy.get('[data-cy=affiliated-organizations]').within(() => { cy.get('[data-cy=thumbnail-dropdown]').should('have.length', 1); - cy.get('[data-cy=contributor-thumbnail-text]').contains(affiliatedOrg); + cy.get('[data-cy=thumbnail-dropdown]').eq(0).click().within(() => { + cy.get('[data-cy=contributor-thumbnail-text]').contains(affiliatedOrg); + }); + }); + cy.get('[data-cy=organization-search]').within(() => { + cy.get('input').clear(); }); - cy.get('[data-cy=organization-search]').clear(); }); it('should visit organizations page from home page link', () => { diff --git a/src/components/Dropdown.js b/src/components/Dropdown.js index 2b2d655f..e1eb6656 100644 --- a/src/components/Dropdown.js +++ b/src/components/Dropdown.js @@ -36,19 +36,20 @@ const useStyles = makeStyles((theme) => ({ })); export const Dropdown = ({ - organization, + checkboxValue, children, dropdownLength, - isOpen, - checkboxValue, - inputValue, filtersActive, + isOpen, + onClick, + organization, }) => { const [openChild, setOpenChild] = useState(isOpen ? true : false); const [colorStyle, setColor] = useState(isOpen ? true : false); const classes = useStyles(); - const handleOpen = () => { + const handleClick = () => { + onClick(organization); setOpenChild(!openChild); setColor(!colorStyle); }; @@ -65,7 +66,7 @@ export const Dropdown = ({ item xs={10} className={clsx(classes.dropdown, { - [classes.open]: colorStyle === true, + [classes.open]: isOpen, })} > @@ -74,7 +75,7 @@ export const Dropdown = ({ filtersActive={filtersActive} checkboxValue={checkboxValue} dropdownLength={dropdownLength} - isOpen={colorStyle} + isOpen={isOpen} isChildThumbnail={false} /> @@ -83,12 +84,11 @@ export const Dropdown = ({ item container className={classes.flexGrid} - onClick={handleOpen} + onClick={handleClick} > @@ -101,7 +101,7 @@ export const Dropdown = ({ /> )} - {openChild && children} + {isOpen && children} ); }; diff --git a/src/pages/Contributors/Affiliated.js b/src/pages/Contributors/Affiliated.js index 05e2eec5..b729a299 100644 --- a/src/pages/Contributors/Affiliated.js +++ b/src/pages/Contributors/Affiliated.js @@ -117,26 +117,28 @@ const useStyles = makeStyles((theme) => ({ }, })); /* eslint complexity: [0, 0]*/ -export const Affiliated = (props) => { - const { - affiliatedCount, - classes, - inputValue, - organizations, - organizationData, - filtersActive, - totalAffiliatedCount, - showIndexContrib, - } = props; +export const Affiliated = ({ + affiliatedCount, + classes, + expandedOrgs, + inputValue, + onOrgClick, + organizations, + organizationData, + filtersActive, + totalAffiliatedCount, + showIndexContrib, +}) => { const classesLocal = useStyles(); const [dropdownOpen, setDropdownOpen] = useState(false); useEffect(() => { - if (filtersActive && organizations.length) { + if ((filtersActive || expandedOrgs.length) && organizations.length) { setDropdownOpen(true); } else { setDropdownOpen(false); } - }, [filtersActive, organizations]) + }, [expandedOrgs, filtersActive, organizations]) + return ( @@ -221,11 +223,13 @@ export const Affiliated = (props) => { ) : ( ))} diff --git a/src/pages/Contributors/AffiliatedOrganizations.js b/src/pages/Contributors/AffiliatedOrganizations.js index 38c55e29..74a11ad3 100644 --- a/src/pages/Contributors/AffiliatedOrganizations.js +++ b/src/pages/Contributors/AffiliatedOrganizations.js @@ -64,11 +64,13 @@ const useStyles = makeStyles((theme) => ({ })); export const AffiliatedOrganizations = ({ - organizations, + expandedOrgs, + filtersActive, inputValue, + onOrgClick, organizationData, + organizations, showIndexContrib, - filtersActive, }) => { const classes = useStyles(); @@ -83,7 +85,7 @@ export const AffiliatedOrganizations = ({ organizations.forEach((org) => { if (org.depth === 3) { org['childNodes'] = []; - org['isOpen'] = false; + org['allChildrenShown'] = false; parentdata.push(org); } if (org.depth === 4) { @@ -99,7 +101,7 @@ export const AffiliatedOrganizations = ({ if (!exist) { mapsearchedChildParent['childNodes'] = []; - mapsearchedChildParent['isOpen'] = false; + mapsearchedChildParent['allChildrenShown'] = false; parentChildobj = mapsearchedChildParent; parentdata.push(mapsearchedChildParent); } else { @@ -115,7 +117,7 @@ export const AffiliatedOrganizations = ({ } } else { org['childNodes'] = []; - org['isOpen'] = false; + org['allChildrenShown'] = false; parentdata.push(org); } } @@ -141,16 +143,16 @@ export const AffiliatedOrganizations = ({ > {currentThumbnails.map((org, i) => { childSort = org.childNodes; - childNode = org.isOpen ? childSort : childSort.slice(0, 6); + childNode = org.allChildrenShown ? childSort : childSort.slice(0, 6); return ( { const data = [...currentThumbnails]; - data[i].isOpen = !data[i].isOpen; + data[i].allChildrenShown = !data[i].allChildrenShown; setCurrentThumbnails(data); }} > - {currentThumbnails[i].isOpen ? 'View Less' : 'View All'} + {currentThumbnails[i].allChildrenShown ? 'View Less' : 'View All'} ) : null} @@ -230,13 +232,13 @@ export const AffiliatedOrganizations = ({ {currentThumbnails.map((org, i) => { return ( {org.childNodes.length === 0 ? ( diff --git a/src/pages/Contributors/index.js b/src/pages/Contributors/index.js index 2f0b6e94..be88d0a1 100644 --- a/src/pages/Contributors/index.js +++ b/src/pages/Contributors/index.js @@ -6,6 +6,7 @@ import React, { useEffect, useState } from 'react'; import axios from 'axios'; import { useLocation } from 'react-router-dom'; import { + ArrayParam, BooleanParam, StringParam, useQueryParam, @@ -55,7 +56,6 @@ export default function Contributors() { const location = useLocation(); const [affiliatedCount, setAffiliatedCount] = useState(0); const [affiliatedOrganizations, setAffiliatedOrganizations] = useState([]); - const [inputValue, setInputValue] = useState(''); const [organizations, setOrganizations] = useState([]); const [organizationNames, setOrganizationNames] = useState([]); const [filtersActive, setFiltersActive] = useState(false); @@ -65,14 +65,22 @@ export default function Contributors() { const [unaffiliatedOrganizations, setUnaffiliatedOrganizations] = useState( [] ); - const [showIndexContrib, setShowIndexContrib] = useQueryParam( - 'contrib', - withDefault(BooleanParam, false) + const [expandedOrgs, setExpandedOrgs] = useQueryParam( + 'opened', + withDefault(ArrayParam, []) ); const [orgStatus, setOrgStatus] = useQueryParam( 'status', withDefault(StringParam, 'any') ); + const [searchQuery, setSearchQuery] = useQueryParam( + 'query', + withDefault(StringParam, '') + ); + const [showIndexContrib, setShowIndexContrib] = useQueryParam( + 'contrib', + withDefault(BooleanParam, false) + ); useEffect(() => { const fetchData = async () => { @@ -124,9 +132,26 @@ export default function Contributors() { } // handle case of user entering bookmarked URL directly } else if (!location.query) { - const queryParams = location.search.replace('?', '').split('&'); - setOrgStatus(queryParams[1].split('=')[1]); - setShowIndexContrib(!!(Number(queryParams[0].split('=')[1]))); + const expanded = []; + const queryParams = {}; + location.search.replace('?', '').split('&').forEach((param) => { + const [key, val] = param.split('='); + if (key === 'opened') { + expanded.push(val); + } else { + queryParams[key] = val; + } + }); + setExpandedOrgs(expanded); + if ('query' in queryParams) { + setSearchQuery(queryParams.query); + } + if ('status' in queryParams) { + setOrgStatus(queryParams.status); + } + if ('contrib' in queryParams) { + setShowIndexContrib(!!Number(queryParams.contrib)); + } } }, [ location.search, @@ -139,7 +164,7 @@ export default function Contributors() { unafflCount = 0; const affiliated = []; const unaffiliated = []; - const input = inputValue.toLowerCase().replace(/\s/g, ''); + const input = searchQuery.toLowerCase().replace(/\s/g, ''); for (const org of organizations) { const orgName = org.name.toLowerCase().replace(/\s/g, ''); if ( @@ -160,7 +185,7 @@ export default function Contributors() { setUnaffiliatedCount(unafflCount); setAffiliatedOrganizations(affiliated); setUnaffiliatedOrganizations(unaffiliated); - }, [inputValue, organizations, showIndexContrib]); + }, [searchQuery, organizations, showIndexContrib]); TabPanel.propTypes = { children: PropTypes.node, @@ -189,6 +214,22 @@ export default function Contributors() { } }; + const handleInputValueChange = (value) => { + setSearchQuery(value); + setExpandedOrgs([]); + }; + + const handleOrgClick = (org) => { + const expanded = [...expandedOrgs]; + const idx = expanded.indexOf(org.id.toString()); + if (idx > -1) { + expanded.splice(idx, 1); + } else { + expanded.push(org.id.toString()); + } + setExpandedOrgs(expanded); + }; + const handleTabValueChange = (value) => { switch (value) { case 0: @@ -222,8 +263,8 @@ export default function Contributors() { @@ -295,15 +336,16 @@ export default function Contributors() { totalUnaffiliatedCount={totalUnaffiliatedCount} /> @@ -316,15 +358,16 @@ export default function Contributors() {