Skip to content

Commit 39cae15

Browse files
authored
Show applied filter on applications page with delete option (#845)
* Display applied filters on applications page with clear option * Added test case and feature falg for filter
1 parent d0419b8 commit 39cae15

File tree

4 files changed

+237
-19
lines changed

4 files changed

+237
-19
lines changed

__tests__/applications/applications.test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,24 @@ describe('Applications page', () => {
135135
).toBe(true, 'status query param is not removed from url');
136136
});
137137

138+
it('should load and render accepted application and check the applied filter label,render all applications when the applied filter is removed', async function () {
139+
await page.goto(`${SITE_URL}/applications/?dev=true`);
140+
await page.waitForNetworkIdle();
141+
await page.click('#filter-button-new');
142+
await page.click('.filter-dropdown div[data-filter="accepted"]');
143+
applicationCards = await page.$$('.application-card');
144+
expect(applicationCards.length).toBe(4);
145+
const filterLabelElement = page.$('.filter-label .filter-text');
146+
expect(filterLabelElement).toBeTruthy();
147+
await page.click('.filter-remove');
148+
await page.waitForNetworkIdle();
149+
applicationCards = await page.$$('.application-card');
150+
const urlAfterClearingStatusFilter = new URL(page.url());
151+
expect(
152+
urlAfterClearingStatusFilter.searchParams.get('status') === null,
153+
).toBe(true, 'status query param is not removed from url');
154+
expect(applicationCards.length).toBe(6);
155+
});
138156
it('should load more applications on going to the bottom of the page', async function () {
139157
let applicationCards = await page.$$('.application-card');
140158
expect(applicationCards.length).toBe(6);

applications/index.html

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<script src="/helpers/loadENV.js"></script>
99
<title>Applications</title>
1010
</head>
11+
1112
<body>
1213
<header class="header">
1314
<h1>RDS Join Applications</h1>
@@ -24,6 +25,30 @@ <h1>RDS Join Applications</h1>
2425
alt="funnel icon"
2526
/>
2627
</button>
28+
<div class="filter-container hidden">
29+
<div class="filter-menu">
30+
<button id="filter-button-new" class="filter-button hidden">
31+
<img
32+
class="funnel-icon"
33+
src="/task-requests/assets/funnel.svg"
34+
alt="funnel icon"
35+
/>
36+
Filters
37+
</button>
38+
<div class="filter-dropdown">
39+
<div data-filter="" class="filter-dropdown-header">
40+
Status <button class="close-dropdown-btn">&#x2715;</button>
41+
</div>
42+
<div data-filter="rejected">Rejected</div>
43+
<div data-filter="accepted">Accepted</div>
44+
<div data-filter="pending">Pending</div>
45+
</div>
46+
</div>
47+
<div class="filter-label hidden">
48+
<span class="filter-text"></span>
49+
<span class="filter-remove">&#x2715;</span>
50+
</div>
51+
</div>
2752
<p class="no_applications_found hidden">No applications Found!</p>
2853
<div
2954
class="filter-modal hidden"

applications/script.js

Lines changed: 98 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {
99
let nextLink;
1010
let isDataLoading = false;
1111
const loader = document.querySelector('.loader');
12-
const filterButton = document.getElementById('filter-button');
1312
const filterModal = document.querySelector('.filter-modal');
1413
const backDrop = document.querySelector('.backdrop');
1514
const backDropBlur = document.querySelector('.backdrop-blur');
@@ -36,8 +35,25 @@ const lastElementContainer = document.getElementById('page_bottom_element');
3635
const applicationDetailsActionsContainer = document.querySelector(
3736
'.application-details-actions',
3837
);
39-
4038
const urlParams = new URLSearchParams(window.location.search);
39+
const isDev = urlParams.get('dev') === 'true';
40+
const filterButton = isDev
41+
? document.getElementById('filter-button-new')
42+
: document.getElementById('filter-button');
43+
if (isDev)
44+
document
45+
.getElementsByClassName('filter-container')[0]
46+
.classList.remove('hidden');
47+
48+
const filterDropdown = document.querySelector('.filter-dropdown');
49+
const filterOptions = document.querySelectorAll(
50+
'.filter-dropdown div:not(.close-dropdown-btn)',
51+
);
52+
const filterLabel = document.querySelector('.filter-label');
53+
const filterText = document.querySelector('.filter-label .filter-text');
54+
const filterRemove = document.querySelector('.filter-remove');
55+
const closeDropdownBtn = document.querySelector('.close-dropdown-btn');
56+
4157
let applicationId = urlParams.get('id');
4258

4359
let currentApplicationId;
@@ -76,8 +92,12 @@ function updateUserApplication({ isAccepted }) {
7692

7793
function changeFilter() {
7894
nextLink = '';
79-
filterModal.classList.add('hidden');
80-
backDrop.style.display = 'none';
95+
if (!isDev) {
96+
filterModal.classList.add('hidden');
97+
backDrop.style.display = 'none';
98+
} else {
99+
status = 'all';
100+
}
81101
applicationContainer.innerHTML = '';
82102
}
83103

@@ -343,6 +363,9 @@ async function renderApplicationCards(next, status, isInitialRender) {
343363
changeLoaderVisibility({ hide: true });
344364
const applications = data.applications;
345365
nextLink = data.next;
366+
if (isDev && status != 'all') {
367+
showAppliedFilter(status);
368+
}
346369
if (isInitialRender) filterButton.classList.remove('hidden');
347370
if (!applications.length)
348371
return noApplicationFoundText.classList.remove('hidden');
@@ -392,7 +415,7 @@ async function renderApplicationById(id) {
392415
const urlParams = new URLSearchParams(window.location.search);
393416
status = urlParams.get('status') || 'all';
394417

395-
if (status !== 'all') {
418+
if (!isDev && status !== 'all') {
396419
document.querySelector(`input[name="status"]#${status}`).checked = true;
397420
}
398421

@@ -418,9 +441,69 @@ const addIntersectionObserver = () => {
418441
intersectionObserver.observe(lastElementContainer);
419442
};
420443

421-
filterButton.addEventListener('click', () => {
422-
filterModal.classList.toggle('hidden');
423-
backDrop.style.display = 'flex';
444+
if (isDev) {
445+
filterButton.addEventListener('click', () => {
446+
filterDropdown.style.display =
447+
filterDropdown.style.display === 'block' ? 'none' : 'block';
448+
});
449+
450+
filterOptions.forEach((option) => {
451+
option.addEventListener('click', () => {
452+
const filter = option.getAttribute('data-filter');
453+
applyFilter(filter);
454+
});
455+
});
456+
} else {
457+
filterButton.addEventListener('click', () => {
458+
filterModal.classList.toggle('hidden');
459+
backDrop.style.display = 'flex';
460+
});
461+
462+
backDrop.addEventListener('click', () => {
463+
filterModal.classList.add('hidden');
464+
backDrop.style.display = 'none';
465+
});
466+
467+
applyFilterButton.addEventListener('click', () => {
468+
const selectedFilterOption = document.querySelector(
469+
'input[name="status"]:checked',
470+
);
471+
472+
const selectedStatus = selectedFilterOption.value;
473+
addQueryParamInUrl('status', selectedStatus);
474+
changeFilter();
475+
status = selectedStatus;
476+
renderApplicationCards(nextLink, status);
477+
});
478+
479+
clearButton.addEventListener('click', clearFilter);
480+
}
481+
482+
function showAppliedFilter(filterApplied) {
483+
filterLabel.classList.remove('hidden');
484+
filterText.textContent =
485+
'Status :' + filterApplied[0].toUpperCase() + filterApplied.substring(1);
486+
}
487+
488+
function applyFilter(filter) {
489+
if (filter.length > 0) {
490+
if (!filterLabel.classList.contains('hidden')) {
491+
filterLabel.classList.add('hidden');
492+
}
493+
addQueryParamInUrl('status', filter);
494+
changeFilter();
495+
status = filter;
496+
renderApplicationCards(nextLink, status);
497+
filterDropdown.style.display = 'none';
498+
}
499+
}
500+
501+
filterRemove.addEventListener('click', () => {
502+
filterLabel.classList.add('hidden');
503+
filterText.textContent = '';
504+
removeQueryParamInUrl('status');
505+
changeFilter();
506+
renderApplicationCards(nextLink, status);
424507
});
425508

426509
backDrop.addEventListener('click', () => {
@@ -431,19 +514,15 @@ backDrop.addEventListener('click', () => {
431514
backDropBlur.addEventListener('click', closeApplicationDetails);
432515
applicationCloseButton.addEventListener('click', closeApplicationDetails);
433516

434-
applyFilterButton.addEventListener('click', () => {
435-
const selectedFilterOption = document.querySelector(
436-
'input[name="status"]:checked',
437-
);
438-
439-
const selectedStatus = selectedFilterOption.value;
440-
addQueryParamInUrl('status', selectedStatus);
441-
changeFilter();
442-
status = selectedStatus;
443-
renderApplicationCards(nextLink, status);
517+
document.addEventListener('click', (e) => {
518+
if (!filterButton.contains(e.target) && !filterDropdown.contains(e.target)) {
519+
filterDropdown.style.display = 'none';
520+
}
444521
});
445522

446-
clearButton.addEventListener('click', clearFilter);
523+
closeDropdownBtn.addEventListener('click', () => {
524+
filterDropdown.style.display = 'none';
525+
});
447526

448527
applicationAcceptButton.addEventListener('click', () =>
449528
updateUserApplication({ isAccepted: true }),

applications/style.css

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,96 @@ body {
5353
overflow-y: auto;
5454
}
5555

56+
.filter-container {
57+
display: flex;
58+
align-items: end;
59+
gap: 10px;
60+
margin: 20px;
61+
}
62+
5663
.funnel-icon {
5764
width: 1.2rem;
5865
height: 1.5rem;
5966
margin-left: 0.5rem;
6067
}
6168

69+
.filter-menu .filter-button:hover {
70+
background-color: var(--color-primary-hover);
71+
}
72+
73+
.filter-menu .filter-button {
74+
margin-left: auto;
75+
background-color: var(--color-primary);
76+
color: var(--white);
77+
border: none;
78+
border-radius: 0.4rem;
79+
cursor: pointer;
80+
display: flex;
81+
align-items: center;
82+
justify-content: center;
83+
width: 9rem;
84+
height: 2.5rem;
85+
padding: 0.7rem;
86+
}
87+
88+
.filter-dropdown {
89+
width: 300px;
90+
padding: 0;
91+
margin: 0;
92+
display: none;
93+
position: absolute;
94+
border: 1px solid var(--light-gray-color);
95+
border-radius: 0.2rem;
96+
background-color: var(--white);
97+
}
98+
99+
.filter-dropdown div {
100+
padding: 0.5rem 1rem 0.5rem 2rem;
101+
color: var(--color-gray);
102+
border: 1px solid var(--light-gray-color);
103+
}
104+
105+
.filter-dropdown div:first-child {
106+
font-weight: bold;
107+
}
108+
109+
.filter-dropdown div:not(:first-child) {
110+
padding-left: 4rem;
111+
font-weight: normal;
112+
}
113+
.filter-dropdown div:not(:first-child):hover {
114+
background-color: var(--light-gray-color);
115+
cursor: pointer;
116+
}
117+
118+
.filter-dropdown-header {
119+
display: flex;
120+
justify-content: space-between;
121+
align-items: center;
122+
}
123+
124+
/* Filter label */
125+
.filter-label {
126+
display: flex;
127+
font-size: 14px;
128+
font-weight: 600;
129+
align-items: center;
130+
color: var(--color-primary);
131+
background-color: var(--white);
132+
border: 1px solid var(--color-primary);
133+
padding: 5px 10px;
134+
border-radius: 50px;
135+
}
136+
137+
.filter-text {
138+
margin-right: 5px;
139+
}
140+
141+
.filter-remove {
142+
cursor: pointer;
143+
font-size: 14px;
144+
}
145+
62146
.filter-button:hover {
63147
background-color: var(--color-primary-hover);
64148
}
@@ -134,6 +218,18 @@ body {
134218
justify-content: center;
135219
}
136220

221+
.close-dropdown-btn {
222+
font-weight: bold;
223+
color: var(--color-gray);
224+
text-align: center;
225+
font-size: 12px;
226+
background-color: var(--white);
227+
border: none;
228+
outline: none;
229+
box-shadow: none;
230+
cursor: pointer;
231+
}
232+
137233
.modal-form {
138234
text-align: initial;
139235
padding: 0.5rem;

0 commit comments

Comments
 (0)