Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 29 additions & 6 deletions web-ui/src/helpers/query-parameters.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,21 @@ import { useEffect } from 'react';
/**
* @param {(QPBoolean | QPString)[]} qps - query parameters
*/
export const useQueryParameters = qps => {
export const useQueryParameters = (
qps,
requirements = [],
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some pages require data to be loaded before query parameters can be processed. This page is one of them. We must have an array of all possible PDL objects in order to call setSelectedPdls with an array of PDL objects that have ids that match those in the pdls query parameter. Support this was extremely difficult and did not increase my love of React. ;-)

requirements is an array of data values that must be set in order to process the query parameters. It defaults to an empty array because most pages do not need this.

processedQPs is an object created by a call to useRef in the calling page. It holds a Boolean that indicates whether we have finished processing the query parameters. This can only happen after all the requirements values are present. I'm using a ref because the value needs to be maintained across multiple renders of the page which React loves to do. I'm open to suggestions for other ways to maintain this flag.

processedQPs = null
) => {
// This examines all the query parameters and
// sets the state value associated with each of them.
useEffect(() => {
if (processedQPs?.current) return;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the page supplied a value for processedQPs AND the value it holds is true then we don't need to run the code that follows again.


const haveRequirements = requirements.every(req =>
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If requirements is an empty array, this will be set to true which is what we want.

Array.isArray(req) ? req.length > 0 : req !== null && req !== undefined
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a required value is an array, the requirement is met when the array is not empty.
Otherwise the requirement is met when the value is not null or undefined.

);
if (!haveRequirements) return;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the requirements are not met then we cannot processed the query parameters yet.


const url = new URL(location.href);
const params = url.searchParams;
for (const qp of qps) {
Expand All @@ -34,13 +47,21 @@ export const useQueryParameters = qps => {
qp.setter(v || qp.default);
}
}
}, []);
if (processedQPs) processedQPs.current = true;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the caller supplied this ref, we need to set its value to to true to include that we have processed the query parameters. This prevents use from doing it again later.

}, requirements);

const dependencies = qps.map(qp => qp.value);

// This updates the query parameters in the URL
// when their associated state values change.
useEffect(() => {
const haveRequirements = requirements.every(req =>
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the requirements have not been met then we cannot update the query parameters.

Array.isArray(req) ? req.length > 0 : req !== null && req !== undefined
);
if (!haveRequirements) return;

const url = new URL(location.href);
let newUrl = url.origin + url.pathname;
const baseUrl = url.origin + url.pathname;
const params = {};

// Add query parameters listed in qps that do not have their default value.
Expand All @@ -55,10 +76,12 @@ export const useQueryParameters = qps => {
if (!qps.some(qp => qp.name === k)) params[k] = v;
}

if (Object.keys(params).length) {
newUrl += '?' + new URLSearchParams(params).toString();
const search = Object.keys(params).length
? '?' + new URLSearchParams(params).toString()
: '';
if (search !== url.search) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only update the URL if the query parameters have changed.

history.replaceState(params, '', baseUrl + search);
}
history.replaceState(params, '', newUrl);
}, dependencies);
};

Expand Down
35 changes: 31 additions & 4 deletions web-ui/src/pages/CheckinsReportPage.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import React, { useContext, useEffect, useState } from 'react';
import React, { useContext, useEffect, useRef, useState } from 'react';

import { TextField } from '@mui/material';
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor reordering of imports.

import Autocomplete from '@mui/material/Autocomplete';

import { AppContext } from '../context/AppContext';
import CheckinReport from '../components/reports-section/CheckinReport';
import {
selectCheckinPDLS,
selectTeamMembersWithCheckinPDL
} from '../context/selectors';

import { TextField } from '@mui/material';
import Autocomplete from '@mui/material/Autocomplete';
import { isArrayPresent } from '../helpers/checks';
import { useQueryParameters } from '../helpers/query-parameters';

import './CheckinsReportPage.css';

Expand All @@ -26,6 +28,31 @@ const CheckinsReportPage = () => {
});
const [filteredPdls, setFilteredPdls] = useState(pdls);

const processedQPs = useRef(false);
useQueryParameters(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds support for the pdl query parameter which keeps track of the PDLs the user has selected.

[
{
name: 'pdls',
default: [],
value: selectedPdls,
setter(ids) {
const newPdls = ids.map(id => pdls.find(pdl => pdl.id === id));
setSelectedPdls(newPdls);
},
toQP(newPdls) {
if (isArrayPresent(newPdls)) {
const ids = newPdls.map(pdl => pdl.id);
return ids.join(',');
} else {
return [];
}
}
}
],
[pdls],
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The requirements for processing query parameters for this page are to have entries in the pdl array.

processedQPs
);

useEffect(() => {
if (!pdls) return;
pdls.map(
Expand Down