Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize API calls and Redesign More Filter section of Explore projects #6325

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
036167b
made the main page static and enabled scrolling in the filters component
mtalhabaig3 Jun 21, 2023
88441ea
Solved the height issue of filters menu
mtalhabaig3 Jun 22, 2023
b18a98e
Merge branch 'hotosm:develop' into feature/5844/menu/height
mtalhabaig3 Jun 22, 2023
fde0541
minor changes
mtalhabaig3 Jun 22, 2023
7625f96
added dependency array
mtalhabaig3 Jun 23, 2023
f211ea0
remove the css for hiding scroll bar
mtalhabaig3 Jun 28, 2023
a68367f
Merge branch 'hotosm:develop' into feature/5844/menu/height
mtalhabaig3 Jun 28, 2023
66073a6
Merge branch 'hotosm:develop' into feature/5844/menu/height
mtalhabaig3 Jul 10, 2023
41d270c
Made dropdowns fixed and hide scroll bar
mtalhabaig3 Jul 10, 2023
e2b332a
removed comments
mtalhabaig3 Jul 11, 2023
585d461
Merge branch 'hotosm:develop' into feature/5844/menu/height
mtalhabaig3 Jul 11, 2023
c120198
Merge branch 'hotosm:develop' into feature/5844/menu/height
mtalhabaig3 Aug 17, 2023
8611239
solved the front end test error
mtalhabaig3 Aug 17, 2023
5dc659c
removed comments
mtalhabaig3 Aug 23, 2023
0ff9686
Merge branch 'develop' of github.com:hotosm/tasking-manager into enha…
spnayan Apr 10, 2024
67eee80
Prevent api call before filter apply in `Explore Projects` page
spnayan Apr 10, 2024
85dd23a
Fix z-index issue between select dropdown and toggle button in more f…
spnayan Apr 15, 2024
2309994
Increase padding in more filters footer button in more filter
spnayan Apr 15, 2024
d6e009a
Change More Filter to popover layout in laptop screen
spnayan Apr 15, 2024
39744bb
Add overlay on More Filter popover
spnayan Apr 15, 2024
aa3aea4
Close More Filter popover on outside click
spnayan Apr 15, 2024
982528b
Fix test case for More Filters
spnayan Apr 16, 2024
c733d67
Prevent More Filter popover close on clearing selected filter
spnayan Apr 22, 2024
16e82bc
Fix project page not scrolling on More Filter popover
spnayan Apr 29, 2024
a8bce27
Fix More Filter popover position on page scroll
spnayan Apr 29, 2024
c10fa27
Fix More Filter popover overlay height
spnayan Apr 29, 2024
1a0a32c
Resolve merge conflicts
spnayan May 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion frontend/src/api/projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { remapParamsToAPI } from '../utils/remapParamsToAPI';
import api from './apiClient';
import { UNDERPASS_URL } from '../config';

export const useProjectsQuery = (fullProjectsQuery, action) => {
export const useProjectsQuery = (fullProjectsQuery, action, queryOptions) => {
const token = useSelector((state) => state.auth.token);
const locale = useSelector((state) => state.preferences['locale']);

Expand Down Expand Up @@ -35,6 +35,7 @@ export const useProjectsQuery = (fullProjectsQuery, action) => {
queryKey: ['projects', fullProjectsQuery, action],
queryFn: ({ signal, queryKey }) => fetchProjects(signal, queryKey),
keepPreviousData: true,
...queryOptions,
});
};

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/header/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export const Header = () => {
) : null;

return (
<header className="w-100 bb b--grey-light">
<header id="top-header" className="w-100 bb b--grey-light">
<UpdateDialog />
{checkUserEmail()}
{showOrgBar && (
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/projects/moreFiltersForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export const MoreFiltersForm = (props) => {
/>
</fieldset>
)}
<div className="tr w-100 mt3">
<div className="tr w-100 mt3 pb3 ph2">
<Link to="/explore">
<Button className="bg-white blue-dark mr1 f6 pv2">
<FormattedMessage {...messages.clear} />
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/components/projects/projectNav.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export const ProjectNav = (props) => {
// onSelectedItemChange={(changes) => console.log(changes)}
return (
/* mb1 mb2-ns (removed for map, but now small gap for more-filters) */
<header className="bt bb b--tan w-100 ">
<header id="explore-nav" className="bt bb b--tan w-100 ">
<div className="mt2 mb1 ph3 dib lh-copy w-100 cf">
<div className="w-80-l w-90-m w-100 fl dib">
<div className="dib">
Expand All @@ -125,6 +125,7 @@ export const ProjectNav = (props) => {
<ProjectsActionFilter setQuery={setQuery} fullProjectsQuery={fullProjectsQuery} />
<Link
to={filterRouteToggled}
id="more-filter-id"
className={`dn mr3 dib-l lh-title f6 ${linkCombo} ${moreFiltersCurrentActiveStyle} blue-dark`}
>
<FormattedMessage {...messages.moreFilters} />
Expand Down
121 changes: 114 additions & 7 deletions frontend/src/views/project.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Suspense, lazy, useEffect } from 'react';
import React, { Suspense, lazy, useEffect, useState, useLayoutEffect, useRef } from 'react';
import { useSelector } from 'react-redux';
import ReactPlaceholder from 'react-placeholder';
import { Outlet, useLocation, useNavigate, useParams } from 'react-router-dom';
Expand All @@ -16,6 +16,17 @@ import { useSetTitleTag } from '../hooks/UseMetaTags';
import { NotFound } from './notFound';
import { ProjectDetailPlaceholder } from '../components/projectDetail/projectDetailPlaceholder';
import { useProjectsQuery, useProjectQuery } from '../api/projects';
import { useWindowSize } from '../hooks/UseWindowSize';
import { useOnClickOutside } from '../hooks/UseOnClickOutside';

const smallScreenSize = 960;

// returns true if the element or one of its parents has the classname
function hasSomeParentClass(element, classname) {
if (typeof element.className === 'string' && element.className.split(' ').indexOf(classname) >= 0)
return true;
return element.parentNode && hasSomeParentClass(element.parentNode, classname);
}

const ProjectCreate = lazy(() => import('../components/projectCreate/index'));

Expand All @@ -29,15 +40,24 @@ export const CreateProject = () => {

export const ProjectsPage = () => {
useSetTitleTag('Explore projects');
const { pathname } = useLocation();
const action = useSelector((state) => state.preferences['action']);
const [fullProjectsQuery, setProjectQuery] = useExploreProjectsQueryParams();
const isMapShown = useSelector((state) => state.preferences['mapShown']);
const searchResultWidth = isMapShown ? 'two-column' : 'one-column';

const { data: projects, status, refetch } = useProjectsQuery(fullProjectsQuery, action);
const {
data: projects,
status,
refetch,
} = useProjectsQuery(fullProjectsQuery, action, {
// prevent api call until the filters are applied
enabled: !pathname.includes('/explore/filters/'),
cacheTime: 0,
});

return (
<div className="pull-center">
<div className="pull-center" id="projects-container">
<ProjectNav>
<Outlet />
</ProjectNav>
Expand Down Expand Up @@ -139,22 +159,109 @@ export const ProjectsPageIndex = (props) => {
};

export const MoreFilters = () => {
const [position, setPosition] = useState({ top: 0, left: 0, height: 0, width: 0 });
const [projectContainerHeight, setProjectContainerHeight] = useState({ height: 0 });
const [scrollHeight, setScrollHeight] = useState(0);
const navigate = useNavigate();
const [fullProjectsQuery] = useExploreProjectsQueryParams();
const [componentHeight, setComponentHeight] = useState(`${window.innerHeight}px`);
const filterElement = document?.getElementById('more-filter-id');
const projectContainerElement = document?.getElementById('projects-container');
const [width] = useWindowSize();

// calculate position of more filter button for layout
useLayoutEffect(() => {
if (!filterElement || !projectContainerElement) return;

const { top, left, height, width } = filterElement.getBoundingClientRect();
const { height: containerHeight } = projectContainerElement.getBoundingClientRect();
const navbarHeight = document.getElementById('explore-nav').offsetHeight;
// calculate difference between explore project page and navbar to set popover overlay height
setProjectContainerHeight(containerHeight - navbarHeight);
setPosition({ top, left, height, width });
setScrollHeight(window.scrollY);
}, [filterElement, width, projectContainerElement]);

useEffect(() => {
const contentHeight =
document.getElementById('explore-nav').offsetHeight +
document.getElementById('top-header').offsetHeight;

const handleResize = () => {
setComponentHeight(window.innerHeight - contentHeight);
};

handleResize();

window.addEventListener('resize', handleResize);

return () => {
window.removeEventListener('resize', handleResize);
};
}, []);

const currentUrl = `/explore${
stringify(fullProjectsQuery) ? ['?', stringify(fullProjectsQuery)].join('') : ''
}`;
const moreFilterRef = useRef(null);

useOnClickOutside(moreFilterRef, (e) => {
const clickedElement = e.target;
const isClearSelectButton = hasSomeParentClass(clickedElement, 'react-select__clear-indicator');
if (
e.target.id === 'more-filter-id' ||
isClearSelectButton //prevent popup close on clicking clear button of select component
)
return;
navigate(currentUrl);
});

const isSmallScreen = width < smallScreenSize;

return (
<>
<div className="absolute left-0 z-4 mt1 w-40-l w-100 h-100 bg-white h4 ph1 ph5-l">
<MoreFiltersForm currentUrl={currentUrl} />
<div
ref={moreFilterRef}
className={`absolute z-4 bg-white ${
// compare screen size for two different design in small screen and large screen of filter section
isSmallScreen ? ' left-0 mt1 w-40-l w-100 h4 ph1 ph5-l' : 'pa2 ba b--light-gray'
}`}
style={
isSmallScreen
? { height: `${componentHeight}px` }
: {
// 250 is half the width of filter component to place filter exactly center of more-filter button
left: position.left - 250 + position.width / 2,
top: position.top + position.height + 10 + scrollHeight,
width: '31.25em',
boxShadow: '2px 1px 23px -1px rgba(143,130,130,0.75)',
}
}
>
<div
className={`${
isSmallScreen ? 'scrollable-container h-100 overflow-x-hidden overflow-y-auto' : ''
}`}
>
<MoreFiltersForm currentUrl={currentUrl} />
</div>
</div>
{!isSmallScreen && (
<div
style={{
left: `${position.left + position.width / 2}px`,
top: position.top + position.height + 2 + scrollHeight,
}}
className={`absolute w1 h1 bg-white bl bt b--grey-light rotate-45 z-5`}
/>
)}

<div
onClick={() => navigate(currentUrl)}
role="button"
className="absolute right-0 z-4 br w-60-l w-0 h-100 bg-blue-dark o-70 h6"
className="absolute right-0 z-2 br w-100-l w-0 bg-blue-dark o-70"
style={{
height: `${projectContainerHeight}px`,
}}
/>
</>
);
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/views/tests/project.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,16 @@ describe('UserProjectsPage Component', () => {
});

test('More Filters should close the more filters container when clicked outside the container', async () => {
jest.spyOn(document, 'getElementById').mockReturnValue({
offsetHeight: 100,
getBoundingClientRect: () => ({
top: 0,
left: 0,
height: 100,
width: 100,
}),
});

const { user, router } = createComponentWithMemoryRouter(
<QueryParamProvider adapter={ReactRouter6Adapter}>
<ReduxIntlProviders>
Expand Down