Skip to content

Commit 6a2a761

Browse files
authored
Merge pull request #703 from Real-Dev-Squad/develop
Add query params to task requests page url (#702)
2 parents 0b9ff39 + b878fb7 commit 6a2a761

File tree

4 files changed

+215
-23
lines changed

4 files changed

+215
-23
lines changed

__tests__/task-requests/task-request.test.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,3 +285,75 @@ describe('createCustomElement', () => {
285285
});
286286
});
287287
});
288+
289+
describe('urlParams', () => {
290+
beforeEach(async () => {
291+
await page.goto(`${SITE_URL}/task-requests`);
292+
await page.waitForNetworkIdle();
293+
});
294+
295+
it('Should update page url for default filter and sort by', async () => {
296+
const url = page.url();
297+
expect(url).toBe(
298+
`${SITE_URL}/task-requests?sort=created-asc&status=pending`,
299+
);
300+
});
301+
302+
it('Should update page url when filter is changed', async () => {
303+
await page.click('#filter-button');
304+
await page.click('input[value="APPROVED"]');
305+
await page.click('input[value="DENIED"]');
306+
await page.click('input[value="assignment"]');
307+
await page.click('input[value="creation"]');
308+
await page.click('#apply-filter-button');
309+
await page.waitForNetworkIdle();
310+
const url = page.url();
311+
312+
expect(url).toBe(
313+
`${SITE_URL}/task-requests?sort=created-asc&status=approved&status=pending&status=denied&request-type=assignment&request-type=creation`,
314+
);
315+
});
316+
317+
it('Should update page url when sort by is changed', async () => {
318+
await page.click('.sort-button');
319+
await page.click('#REQUESTORS_COUNT_ASC');
320+
await page.waitForNetworkIdle();
321+
const url = page.url();
322+
323+
expect(url).toBe(
324+
`${SITE_URL}/task-requests?sort=requestors-asc&status=pending`,
325+
);
326+
});
327+
328+
it('Should have UI elements in sync with url', async () => {
329+
await page.goto(
330+
`${SITE_URL}/task-requests?sort=created-desc&status=approved&status=pending&status=denied&request-type=assignment&request-type=creation`,
331+
);
332+
await page.click('#filter-button');
333+
await page.waitForSelector('.filter-modal');
334+
335+
const approvedFilter = await page.$('input[value="APPROVED"]');
336+
const pendingFilter = await page.$('input[value="PENDING"]');
337+
const deniedFilter = await page.$('input[value="DENIED"]');
338+
339+
const isApprovedChecked = await (
340+
await approvedFilter.getProperty('checked')
341+
).jsonValue();
342+
const isPendingChecked = await (
343+
await pendingFilter.getProperty('checked')
344+
).jsonValue();
345+
const isDeniedChecked = await (
346+
await deniedFilter.getProperty('checked')
347+
).jsonValue();
348+
349+
expect(isApprovedChecked).toBe(true);
350+
expect(isPendingChecked).toBe(true);
351+
expect(isDeniedChecked).toBe(true);
352+
353+
const newestFirst = await page.$('#CREATED_TIME_DESC');
354+
const newestFirstClass = await (
355+
await newestFirst.getProperty('className')
356+
).jsonValue();
357+
expect(newestFirstClass).toContain('selected');
358+
});
359+
});

task-requests/constants.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,10 @@ const ErrorMessages = {
4848
NOT_FOUND: 'Task Requests not found',
4949
SERVER_ERROR: 'Unexpected error occurred',
5050
};
51+
52+
const Sort = {
53+
REQUESTORS_COUNT_ASC: 'requestors-asc',
54+
REQUESTORS_COUNT_DESC: 'requestors-desc',
55+
CREATED_TIME_DESC: 'created-desc',
56+
CREATED_TIME_ASC: 'created-asc',
57+
};

task-requests/script.js

Lines changed: 85 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ const filterStates = {
2828

2929
const updateFilterStates = (key, value) => {
3030
filterStates[key] = value;
31+
const constructedQueryString = formURLQueryString(filterStates, isDev);
32+
manipulateURLQueryParams(constructedQueryString);
3133
};
3234

3335
async function getTaskRequests(query = {}, nextLink) {
@@ -119,6 +121,45 @@ backDrop.addEventListener('click', () => {
119121
backDrop.style.display = 'none';
120122
});
121123

124+
function updateUIBasedOnQueryParams(parsedQueryObj) {
125+
const statusFilters = document.querySelectorAll(
126+
`input[name="status-filter"]`,
127+
);
128+
const requestTypeFilters = document.querySelectorAll(
129+
`input[name="request-type-filter"]`,
130+
);
131+
const sortOptions = document.querySelectorAll('.sort-container');
132+
133+
if (parsedQueryObj.sort) {
134+
sortOptions.forEach((option) => {
135+
if (parsedQueryObj.sort.includes(Sort[option.id])) {
136+
selectButton(option);
137+
updateFilterStates('order', option.id);
138+
sortModal.classList.toggle('hidden');
139+
}
140+
});
141+
}
142+
143+
if (parsedQueryObj.status) {
144+
statusFilters.forEach((filter) => {
145+
if (parsedQueryObj.status.includes(filter.value.toLowerCase())) {
146+
filter.checked = true;
147+
}
148+
});
149+
}
150+
151+
if (parsedQueryObj['request-type']) {
152+
requestTypeFilters.forEach((filter) => {
153+
if (parsedQueryObj['request-type'].includes(filter.value)) {
154+
filter.checked = true;
155+
}
156+
});
157+
}
158+
159+
applyFilterButton.click();
160+
filterModal.classList.toggle('hidden');
161+
}
162+
122163
function toggleStatusCheckbox(statusValue) {
123164
const element = document.querySelector(
124165
`#status-filter input[value=${statusValue}]`,
@@ -143,6 +184,18 @@ filterButton.addEventListener('click', (event) => {
143184
backDrop.style.display = 'flex';
144185
});
145186

187+
function manipulateURLQueryParams(constructedQueryString) {
188+
const currentURLInstance = new URL(window.location.href);
189+
currentURLInstance.search = '';
190+
const currentURL = currentURLInstance.href;
191+
const newURLWithQueryParams = `${currentURL}${constructedQueryString}`;
192+
window.history.pushState(
193+
{ path: newURLWithQueryParams },
194+
'',
195+
newURLWithQueryParams,
196+
);
197+
}
198+
146199
applyFilterButton.addEventListener('click', async () => {
147200
filterModal.classList.toggle('hidden');
148201
const checkedValuesStatus = getCheckedValues('status-filter');
@@ -158,9 +211,11 @@ applyFilterButton.addEventListener('click', async () => {
158211
});
159212
clearButton.addEventListener('click', async function () {
160213
clearCheckboxes('status-filter');
214+
clearCheckboxes('request-type-filter');
161215
filterModal.classList.toggle('hidden');
162216
changeFilter();
163217
updateFilterStates('status', '');
218+
updateFilterStates('requestType', '');
164219
await renderTaskRequestCards(filterStates);
165220
});
166221

@@ -183,6 +238,26 @@ function addSortByIcon(name, id, groupName, order) {
183238
group.appendChild(containerAsc);
184239
}
185240

241+
function toggleSortModal() {
242+
sortModal.classList.toggle('hidden');
243+
backDrop.style.display = 'none';
244+
}
245+
246+
function selectButton(button) {
247+
if (selectedSortButton === button) {
248+
selectedSortButton.classList.remove('selected');
249+
selectedSortButton = null;
250+
toggleSortModal();
251+
} else {
252+
if (selectedSortButton) {
253+
selectedSortButton.classList.remove('selected');
254+
}
255+
selectedSortButton = button;
256+
selectedSortButton.classList.add('selected');
257+
toggleSortModal();
258+
}
259+
}
260+
186261
function sortModalButtons() {
187262
const assigneeAsc = document.getElementById(ASSIGNEE_COUNT);
188263
const assigneeDesc = document.getElementById(ASSIGNEE_DESC);
@@ -196,26 +271,6 @@ function sortModalButtons() {
196271
createTimeDesc,
197272
];
198273

199-
function toggleSortModal() {
200-
sortModal.classList.toggle('hidden');
201-
backDrop.style.display = 'none';
202-
}
203-
204-
function selectButton(button) {
205-
if (selectedSortButton === button) {
206-
selectedSortButton.classList.remove('selected');
207-
selectedSortButton = null;
208-
toggleSortModal();
209-
} else {
210-
if (selectedSortButton) {
211-
selectedSortButton.classList.remove('selected');
212-
}
213-
selectedSortButton = button;
214-
selectedSortButton.classList.add('selected');
215-
toggleSortModal();
216-
}
217-
}
218-
219274
sortModalButtons.forEach((button) => {
220275
if (button) {
221276
button.addEventListener('click', async () => {
@@ -226,6 +281,7 @@ function sortModalButtons() {
226281
});
227282
}
228283
});
284+
229285
selectButton(createTimeAsc);
230286
toggleSortModal();
231287
}
@@ -410,9 +466,15 @@ async function renderTaskRequestCards(queries = {}, newLink = '') {
410466
}
411467

412468
async function render() {
413-
toggleStatusCheckbox(Status.PENDING.toUpperCase());
414-
415-
await renderTaskRequestCards(filterStates);
469+
if (window.location.search === '') {
470+
toggleStatusCheckbox(Status.PENDING.toUpperCase());
471+
const constructedQueryString = formURLQueryString(filterStates);
472+
manipulateURLQueryParams(constructedQueryString);
473+
await renderTaskRequestCards(filterStates);
474+
} else {
475+
const parsedQuery = parseQueryParams(params);
476+
updateUIBasedOnQueryParams(parsedQuery);
477+
}
416478
addIntersectionObserver();
417479
}
418480

task-requests/util.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,54 @@ const addSpinner = (container) => {
6767

6868
return removeSpinner;
6969
};
70+
71+
/**
72+
* Parses the query parameters from the URLSearchParams object and organizes them into an object.
73+
*
74+
* @param {URLSearchParams} searchParams - The URLSearchParams object that needs to be parsed.
75+
* @returns {Object.<string, string[]>} An object containing query parameter keys as properties
76+
* and arrays of corresponding values.
77+
* */
78+
function parseQueryParams(searchParams) {
79+
const queryObject = {};
80+
81+
searchParams.forEach((value, key) => {
82+
if (!queryObject[key]) {
83+
queryObject[key] = [];
84+
}
85+
queryObject[key].push(value);
86+
});
87+
return queryObject;
88+
}
89+
90+
function formURLQueryString(queryStates, isDev) {
91+
const urlParams = new URLSearchParams();
92+
93+
if (queryStates.order) {
94+
let sortQueryString = Order[queryStates.order];
95+
const key = Object.keys(sortQueryString)[0];
96+
const value = sortQueryString[key];
97+
sortQueryString = key + '-' + value;
98+
urlParams.append('sort', sortQueryString);
99+
}
100+
if (queryStates.status) {
101+
if (Array.isArray(queryStates.status)) {
102+
queryStates.status.forEach((_, index) => {
103+
urlParams.append('status', queryStates.status[index]);
104+
});
105+
} else {
106+
urlParams.append('status', queryStates.status);
107+
}
108+
}
109+
if (queryStates.requestType) {
110+
queryStates.requestType.forEach((_, index) =>
111+
urlParams.append('request-type', queryStates.requestType[index]),
112+
);
113+
}
114+
115+
if (isDev) {
116+
urlParams.append('dev', 'true');
117+
}
118+
119+
return '?' + urlParams.toString().trim();
120+
}

0 commit comments

Comments
 (0)