@@ -36,6 +51,15 @@ const createCard = (rawGroup, onClick = () => {}) => {
.querySelector('.card__btn')
.addEventListener('click', () => group.isUpdating || onClick());
+ if (isSuperUser) {
+ cardElement
+ .querySelector('.delete-group')
+ .addEventListener('click', (e) => {
+ e.stopPropagation();
+ onDelete(rawGroup.id, rawGroup.roleId);
+ });
+ }
+
return cardElement;
};
@@ -214,6 +238,44 @@ const createGroupCreationModal = (onClose = () => {}, onSubmit = () => {}) => {
return backdropElement;
};
+const createDeleteConfirmationModal = (
+ onClose = () => {},
+ onConfirm = () => {},
+) => {
+ const backdropElement = document.createElement('div');
+ backdropElement.className = 'backdrop';
+
+ const modalElement = document.createElement('div');
+ modalElement.className = 'delete-confirmation-modal';
+ modalElement.innerHTML = `
+
+
+
Are you sure you want to delete this group?
+
+
+
+
+
+
+ `;
+
+ modalElement.querySelector('#close-button').onclick = onClose;
+ modalElement.querySelector('#cancel-delete').onclick = onClose;
+ modalElement.querySelector('#confirm-delete').onclick = onConfirm;
+
+ backdropElement.appendChild(modalElement);
+ backdropElement.onclick = (e) => {
+ if (e.target === backdropElement) onClose();
+ };
+
+ return backdropElement;
+};
+
export {
createCard,
createLoadingCard,
@@ -222,4 +284,5 @@ export {
createNavbarProfileLoading,
createNavbarProfileSignin,
createGroupCreationModal,
+ createDeleteConfirmationModal,
};
diff --git a/groups/index.html b/groups/index.html
index b58fcdcf..a2e188ab 100644
--- a/groups/index.html
+++ b/groups/index.html
@@ -10,6 +10,7 @@
+
diff --git a/groups/render.js b/groups/render.js
index c8513b2a..9647b07d 100644
--- a/groups/render.js
+++ b/groups/render.js
@@ -6,6 +6,7 @@ import {
createNavbarProfile,
createNavbarProfileLoading,
createNavbarProfileSignin,
+ createDeleteConfirmationModal,
} from './createElements.js';
const renderNotAuthenticatedPage = () => {
@@ -86,8 +87,13 @@ const removeLoadingCards = () => {
loadingCards.forEach((card) => mainContainer.removeChild(card));
};
-const renderGroupById = ({ group, cardOnClick = () => {} }) => {
- const card = createCard(group, cardOnClick);
+const renderGroupById = ({
+ group,
+ cardOnClick = () => {},
+ onDelete = () => {},
+ isSuperUser = false,
+}) => {
+ const card = createCard(group, cardOnClick, onDelete, isSuperUser);
const mainContainer = document.querySelector('.group-container');
const groupElement = document.getElementById(`group-${group.id}`);
if (groupElement) {
@@ -105,6 +111,29 @@ const renderNoGroupFound = () => {
mainContainer.append(noGroupContainer);
};
+const renderDeleteConfirmationModal = ({
+ onClose = () => {},
+ onConfirm = () => {},
+}) => {
+ const container = document.querySelector('body');
+ const existingBackdrop = document.querySelector('.backdrop');
+
+ if (existingBackdrop) {
+ container.removeChild(existingBackdrop);
+ }
+
+ const modal = createDeleteConfirmationModal(onClose, onConfirm);
+ container.appendChild(modal);
+};
+
+const removeDeleteConfirmationModal = () => {
+ const container = document.querySelector('body');
+ const backdrop = document.querySelector('.backdrop');
+ if (backdrop) {
+ container.removeChild(backdrop);
+ }
+};
+
export {
renderNotAuthenticatedPage,
renderGroupCreationModal,
@@ -117,4 +146,6 @@ export {
removeLoadingCards,
renderGroupById,
renderNoGroupFound,
+ renderDeleteConfirmationModal,
+ removeDeleteConfirmationModal,
};
diff --git a/groups/script.js b/groups/script.js
index 9ac12657..7b42e819 100644
--- a/groups/script.js
+++ b/groups/script.js
@@ -11,7 +11,10 @@ import {
renderNavbarProfile,
renderNavbarProfileSignin,
renderNotAuthenticatedPage,
+ renderDeleteConfirmationModal,
+ removeDeleteConfirmationModal,
} from './render.js';
+
import {
addGroupRoleToMember,
createDiscordGroupRole,
@@ -22,11 +25,14 @@ import {
getDiscordGroupIdsFromSearch,
getParamValueFromURL,
setParamValueInURL,
+ deleteDiscordGroupRole,
} from './utils.js';
const QUERY_PARAM_KEY = {
+ DEV_FEATURE_FLAG: 'dev',
GROUP_SEARCH: 'name',
};
+const isDev = getParamValueFromURL(QUERY_PARAM_KEY.DEV_FEATURE_FLAG) === 'true';
const handler = {
set: (obj, prop, value) => {
@@ -42,11 +48,16 @@ const handler = {
.filter(
(ng) => JSON.stringify(oldGroups?.[ng.id]) !== JSON.stringify(ng),
)
- .filter((ng) => dataStore.filteredGroupsIds.includes(ng.id));
+ .filter((ng) => dataStore.filteredGroupsIds.includes(ng.id))
+ .filter((ng) => !ng.isDeleted);
diffGroups.forEach((group) =>
renderGroupById({
- group,
+ group: {
+ ...dataStore.groups[group.id],
+ roleId: dataStore.groups[group.id].roleid,
+ },
cardOnClick: () => groupCardOnAction(group.id),
+ isSuperUser: dataStore.isSuperUser,
}),
);
break;
@@ -99,6 +110,9 @@ const handler = {
case 'discordId':
obj[prop] = value;
break;
+ case 'isSuperUser':
+ obj[prop] = value;
+ break;
default:
throw new Error('Invalid property set');
}
@@ -114,6 +128,7 @@ const dataStore = new Proxy(
search: getParamValueFromURL(QUERY_PARAM_KEY.GROUP_SEARCH),
discordId: null,
isCreateGroupModalOpen: false,
+ isSuperUser: false,
},
handler,
);
@@ -155,10 +170,13 @@ const onCreate = () => {
};
const afterAuthentication = async () => {
renderNavbarProfile({ profile: dataStore.userSelf });
+ dataStore.isSuperUser = await checkUserIsSuperUser();
+
await Promise.all([getDiscordGroups(), getUserGroupRoles()]).then(
([groups, roleData]) => {
- dataStore.filteredGroupsIds = groups.map((group) => group.id);
- dataStore.groups = groups.reduce((acc, group) => {
+ const nonDeletedGroups = groups.filter((group) => !group.isDeleted);
+ dataStore.filteredGroupsIds = nonDeletedGroups.map((group) => group.id);
+ dataStore.groups = nonDeletedGroups.reduce((acc, group) => {
let title = group.rolename
.replace('group-', '')
.split('-')
@@ -180,6 +198,9 @@ const afterAuthentication = async () => {
dataStore.search,
);
dataStore.discordId = roleData.userId;
+ renderAllGroups({
+ cardOnClick: groupCardOnAction,
+ });
},
);
};
@@ -254,12 +275,52 @@ function groupCardOnAction(id) {
function renderAllGroups({ cardOnClick }) {
const mainContainer = document.querySelector('.group-container');
mainContainer.innerHTML = '';
- dataStore.filteredGroupsIds.forEach((id) =>
- renderGroupById({
- group: dataStore.groups[id],
- cardOnClick: () => cardOnClick(id),
- }),
+ const nonDeletedGroups = dataStore.filteredGroupsIds.filter(
+ (id) => !dataStore.groups[id].isDeleted,
);
+ if (nonDeletedGroups.length === 0) {
+ renderNoGroupFound();
+ } else {
+ nonDeletedGroups.forEach((id) => {
+ const group = dataStore.groups[id];
+ if (!group.isDeleted) {
+ renderGroupById({
+ group: group,
+ cardOnClick: () => cardOnClick(id),
+ onDelete: isDev ? showDeleteModal : undefined,
+ isSuperUser: dataStore.isSuperUser && isDev,
+ });
+ }
+ });
+ }
+}
+
+function showDeleteModal(groupId, roleId) {
+ if (!isDev) return;
+ renderDeleteConfirmationModal({
+ onClose: () => {
+ removeDeleteConfirmationModal();
+ },
+ onConfirm: async () => {
+ try {
+ await deleteDiscordGroupRole(groupId, roleId);
+ showToaster('Group deleted successfully');
+
+ updateGroup(groupId, { isDeleted: true });
+
+ dataStore.filteredGroupsIds = dataStore.filteredGroupsIds.filter(
+ (id) => id !== groupId,
+ );
+ renderAllGroups({
+ cardOnClick: groupCardOnAction,
+ });
+ } catch (error) {
+ showToaster(error.message || 'Failed to delete group');
+ } finally {
+ removeDeleteConfirmationModal();
+ }
+ },
+ });
}
onCreate();
diff --git a/groups/style.css b/groups/style.css
index 054e47b4..b9b966f8 100644
--- a/groups/style.css
+++ b/groups/style.css
@@ -127,6 +127,12 @@ body {
grid-template-columns: repeat(4, 1fr);
}
+@media screen and (max-width: 400px) {
+ .group-header {
+ gap: 0.8rem;
+ }
+}
+
.spacer {
flex-grow: 1;
}
@@ -345,6 +351,32 @@ body {
width: 100%;
}
+.card__header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.delete-group {
+ background-color: transparent;
+ border: none;
+ padding: 0;
+ cursor: pointer;
+}
+
+.delete-group__icon {
+ height: 1.2em;
+ opacity: 0.5;
+ transition: transform 0.2s ease-in-out, opacity 0.2s ease-in-out,
+ scale 0.2s ease-in-out;
+}
+
+.delete-group:hover .delete-group__icon {
+ transform: rotate(10deg);
+ opacity: 1;
+ scale: 1.2;
+}
+
.card__title {
font-size: 1.1rem;
font-weight: 600;
@@ -592,3 +624,78 @@ body {
gap: 0;
}
}
+
+/*
+For Delete Confirmation Modal
+*/
+
+.delete-confirmation-modal {
+ background-color: var(--color-white);
+ border-radius: 8px;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+ max-width: 400px;
+ width: 90%;
+ padding: 24px;
+ position: relative;
+}
+
+.delete-modal__header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 16px;
+}
+
+.delete-modal__title {
+ font-size: 24px;
+ font-weight: bold;
+ color: #333;
+ margin: 0;
+}
+
+.delete-modal__close {
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 0.8rem 1rem;
+ margin: -1rem -0.8rem 0.8rem 0.4rem;
+}
+
+.delete-modal__content {
+ margin-bottom: 24px;
+}
+
+.delete-modal__msg {
+ font-size: 16px;
+ color: #666;
+ line-height: 1.5;
+}
+
+.delete-modal__buttons {
+ display: flex;
+ justify-content: flex-end;
+ gap: 12px;
+}
+
+.delete-modal-button {
+ padding: 10px 20px;
+ border-radius: 4px;
+ font-size: 16px;
+ font-weight: bold;
+ cursor: pointer;
+ transition: background-color 0.3s ease;
+}
+
+.button--secondary:hover {
+ background-color: var(--color-group-btn-background);
+}
+
+.button--danger {
+ background-color: #dc3545;
+ color: #fff;
+ border: none;
+}
+
+.button--danger:hover {
+ background-color: #c82333;
+}
diff --git a/groups/utils.js b/groups/utils.js
index 4dfeff83..e6437a0e 100644
--- a/groups/utils.js
+++ b/groups/utils.js
@@ -1,5 +1,4 @@
const BASE_URL = window.API_BASE_URL; // REPLACE WITH YOUR LOCALHOST URL FOR TESTING LOCAL BACKEND
-
async function getMembers() {
try {
const res = await fetch(`${BASE_URL}/users/`, {
@@ -117,6 +116,33 @@ async function removeRoleFromMember(roleId, discordId) {
}
}
+async function deleteDiscordGroupRole(groupId, roleId) {
+ try {
+ const res = await fetch(
+ `${BASE_URL}/discord-actions/groups/${groupId}?dev=true`,
+ {
+ method: 'DELETE',
+ credentials: 'include',
+ headers: {
+ 'Content-type': 'application/json',
+ },
+ body: JSON.stringify({ roleid: roleId }),
+ },
+ );
+
+ if (!res.ok) {
+ const errorResponse = await res.json();
+ throw new Error(
+ `Failed to delete group role: ${JSON.stringify(errorResponse.error)}`,
+ );
+ }
+
+ return await res.json();
+ } catch (err) {
+ throw err;
+ }
+}
+
function removeGroupKeywordFromDiscordRoleName(groupName) {
if (/^group.*/.test(groupName)) {
const splitNames = groupName.split('-');
@@ -164,6 +190,7 @@ export {
createDiscordGroupRole,
addGroupRoleToMember,
removeRoleFromMember,
+ deleteDiscordGroupRole,
removeGroupKeywordFromDiscordRoleName,
getDiscordGroupIdsFromSearch,
getParamValueFromURL,
From 66d8f37c324fc82df49f1f1bf1931cd2d84f6c19 Mon Sep 17 00:00:00 2001
From: Rishi <148757583+rishirishhh@users.noreply.github.com>
Date: Tue, 19 Nov 2024 01:13:38 +0530
Subject: [PATCH 6/7] fix: shimmer is applied to only those data that are
loaded later (#916)
---
.../extension-requests.test.js | 73 ---------
extension-requests/script.js | 150 +++++-------------
2 files changed, 36 insertions(+), 187 deletions(-)
diff --git a/__tests__/extension-requests/extension-requests.test.js b/__tests__/extension-requests/extension-requests.test.js
index b8d50c5e..f758b151 100644
--- a/__tests__/extension-requests/extension-requests.test.js
+++ b/__tests__/extension-requests/extension-requests.test.js
@@ -667,79 +667,6 @@ describe('Tests the Extension Requests Screen', () => {
expect(hasSkeletonClassAfter).toBe(false);
});
- it('checks whether the shimmer effect is working for deadlineValue element under feature flag', async () => {
- await page.goto(`${baseUrl}/?dev=true`);
- const deadlineValueSelector = await page.$$(
- '[data-testid="skeleton-span"]',
- );
- expect(deadlineValueSelector).toBeTruthy();
- await page.waitForTimeout(5000);
- const hasSkeletonClassAfter = await page.$eval('.tooltip-container', (el) =>
- el.classList.contains('skeleton-span'),
- );
- expect(hasSkeletonClassAfter).toBe(false);
- });
-
- it('checks whether the shimmer effect is working for requestedValue element under feature flag', async () => {
- await page.goto(`${baseUrl}/?dev=true`);
- const requestedValueSelector = await page.$$(
- '[data-testid="skeleton-text"]',
- );
- expect(requestedValueSelector).toBeTruthy();
- await page.waitForTimeout(5000);
- const hasSkeletonClassAfter = await page.$eval('.requested-day', (el) =>
- el.classList.contains('skeleton-text'),
- );
- expect(hasSkeletonClassAfter).toBe(false);
- });
- it('checks whether the shimmer effect is working for newDeadlineValue element under feature flag', async () => {
- await page.goto(`${baseUrl}/?dev=true`);
- const newDeadlineValueSelector = await page.$$(
- '[data-testid="skeleton-span"]',
- );
- expect(newDeadlineValueSelector).toBeTruthy();
- await page.waitForTimeout(5000);
- const hasSkeletonClassAfter = await page.$eval('.requested-day', (el) =>
- el.classList.contains('skeleton-span'),
- );
- expect(hasSkeletonClassAfter).toBe(false);
- });
-
- it('checks whether the shimmer effect is working for extensionRequestNumberValue element under feature flag', async () => {
- await page.goto(`${baseUrl}/?dev=true`);
- const extensionRequestNumberValueSelector = await page.$$(
- '[data-testid="skeleton-span"]',
- );
- expect(extensionRequestNumberValueSelector).toBeTruthy();
- await page.waitForTimeout(5000);
- const hasSkeletonClassAfter = await page.$eval(
- '.extension-request-number',
- (el) => el.classList.contains('skeleton-span'),
- );
- expect(hasSkeletonClassAfter).toBe(false);
- });
-
- it('checks whether the shimmer effect is visible under dev flag only for the statusSiteLink element', async () => {
- await page.goto(`${baseUrl}/?dev=true`);
- const statusSiteLinkSelector = await page.$$(
- '[data-testid="external-link skeleton-link"]',
- );
- expect(statusSiteLinkSelector).toBeTruthy();
- await page.waitForTimeout(5000);
- const hasSkeletonClassAfter = await page.$eval('.external-link', (el) =>
- el.classList.contains('skeleton-link'),
- );
- expect(hasSkeletonClassAfter).toBe(false);
- });
-
- it('checks whether the shimmer effect is visible under dev flag only for the taskStatusValue element', async () => {
- await page.goto(`${baseUrl}/?dev=true`);
- const taskStatusValueElement = await page.$$(
- '[data-testid="skeleton-span"]',
- );
- expect(taskStatusValueElement).toBeTruthy();
- });
-
it('Checks whether the card is not removed from display when api call is unsuccessful', async () => {
const extensionCards = await page.$$('.extension-card');
diff --git a/extension-requests/script.js b/extension-requests/script.js
index dfe1818b..19d574c8 100644
--- a/extension-requests/script.js
+++ b/extension-requests/script.js
@@ -623,26 +623,16 @@ async function createExtensionCard(data, dev) {
});
deadlineContainer.appendChild(deadlineText);
- let deadlineValue;
- if (dev) {
- deadlineValue = createElement({
- type: 'span',
- attributes: {
- class: 'skeleton-span',
- 'data-testid': 'skeleton-span',
- },
- });
- } else {
- deadlineValue = createElement({
- type: 'span',
- innerText: `${deadlineDays}`,
- attributes: {
- class: `tooltip-container ${
- isDeadLineCrossed && isStatusPending ? 'red-text' : ''
- }`,
- },
- });
- }
+ const deadlineValue = createElement({
+ type: 'span',
+ innerText: `${deadlineDays}`,
+ attributes: {
+ class: `tooltip-container ${
+ isDeadLineCrossed && isStatusPending ? 'red-text' : ''
+ }`,
+ },
+ });
+
deadlineContainer.appendChild(deadlineValue);
const deadlineTooltip = createElement({
type: 'span',
@@ -662,24 +652,14 @@ async function createExtensionCard(data, dev) {
});
requestedContainer.appendChild(requestedText);
- let requestedValue;
- if (dev) {
- requestedValue = createElement({
- type: 'span',
- attributes: {
- class: 'skeleton-text',
- 'data-testid': 'skeleton-text',
- },
- });
- } else {
- requestedValue = createElement({
- type: 'span',
- attributes: {
- class: `requested-day tooltip-container ${requestedDaysTextColor}`,
- },
- innerText: `${requestedDaysAgo}`,
- });
- }
+ const requestedValue = createElement({
+ type: 'span',
+ attributes: {
+ class: `requested-day tooltip-container ${requestedDaysTextColor}`,
+ },
+ innerText: ` ${requestedDaysAgo}`,
+ });
+
const requestedToolTip = createElement({
type: 'span',
attributes: { class: 'tooltip' },
@@ -744,19 +724,12 @@ async function createExtensionCard(data, dev) {
});
newDeadlineContainer.appendChild(newDeadlineText);
- let newDeadlineValue;
- if (dev) {
- newDeadlineValue = createElement({
- type: 'span',
- attributes: { class: 'skeleton-span', 'data-testid': 'skeleton-span' },
- });
- } else {
- newDeadlineValue = createElement({
- type: 'span',
- attributes: { class: 'requested-day tooltip-container' },
- innerText: ` ${newDeadlineDays}`,
- });
- }
+ const newDeadlineValue = createElement({
+ type: 'span',
+ attributes: { class: 'requested-day tooltip-container' },
+ innerText: ` ${newDeadlineDays}`,
+ });
+
const newDeadlineToolTip = createElement({
type: 'span',
attributes: { class: 'tooltip' },
@@ -776,19 +749,12 @@ async function createExtensionCard(data, dev) {
});
extensionForContainer.appendChild(extensionForText);
- let extensionForValue;
- if (dev) {
- extensionForValue = createElement({
- type: 'span',
- attributes: { class: 'skeleton-span' },
- });
- } else {
- extensionForValue = createElement({
- type: 'span',
- attributes: { class: 'tooltip-container' },
- innerText: ` +${extensionDays}`,
- });
- }
+ const extensionForValue = createElement({
+ type: 'span',
+ attributes: { class: 'tooltip-container' },
+ innerText: ` +${extensionDays}`,
+ });
+
const extensionToolTip = createElement({
type: 'span',
attributes: { class: 'tooltip' },
@@ -821,19 +787,12 @@ async function createExtensionCard(data, dev) {
const requestNumber = data.requestNumber || 1;
- let extensionRequestNumberValue;
- if (dev) {
- extensionRequestNumberValue = createElement({
- type: 'span',
- attributes: { class: 'skeleton-span', 'data-testid': 'skeleton-span' },
- });
- } else {
- extensionRequestNumberValue = createElement({
- type: 'span',
- attributes: { class: 'extension-request-number' },
- innerText: `#${requestNumber}`,
- });
- }
+ const extensionRequestNumberValue = createElement({
+ type: 'span',
+ attributes: { class: 'extension-request-number' },
+ innerText: `#${requestNumber}`,
+ });
+
extensionRequestNumberContainer.appendChild(extensionRequestNumberValue);
const cardAssigneeButtonContainer = createElement({
type: 'div',
@@ -1330,44 +1289,7 @@ async function createExtensionCard(data, dev) {
CommitedHoursContent.innerText = 'Missing';
CommitedHoursContent.classList.add('label-content-missing');
}
- if (dev) {
- deadlineValue.classList.remove('skeleton-span');
- deadlineValue.innerText = `${deadlineDays}`;
-
- deadlineValue.classList.add('tooltip-container');
- if (isDeadLineCrossed && isStatusPending) {
- deadlineValue.classList.add('red-text');
- }
- }
-
- if (dev) {
- requestedValue.classList.remove('skeleton-text');
- requestedValue.innerText = `${requestedDaysAgo}`;
-
- requestedValue.classList.add(
- 'requested-day',
- 'tooltip-container',
- requestedDaysTextColor,
- );
- }
-
- if (dev) {
- newDeadlineValue.classList.remove('skeleton-span');
- newDeadlineValue.innerText = ` ${newDeadlineDays}`;
- newDeadlineValue.classList.add('requested-day', 'tooltip-container');
- }
- if (dev) {
- extensionForValue.classList.remove('skeleton-span');
- extensionForValue.innerText = ` +${extensionDays}`;
- extensionForValue.classList.add('tooltip-container');
- }
-
- if (dev) {
- extensionRequestNumberValue.classList.remove('skeleton-span');
- extensionRequestNumberValue.innerText = `#${requestNumber}`;
- extensionRequestNumberValue.classList.add('extension-request-number');
- }
if (!dev) {
removeSpinner();
renderExtensionCreatedLog();
From 2629ec5e3e6cdb0a490d9371ea104623e794e141 Mon Sep 17 00:00:00 2001
From: Depayan M <72678445+DepayanMondal@users.noreply.github.com>
Date: Fri, 22 Nov 2024 00:00:57 +0530
Subject: [PATCH 7/7] Converted Tabs to Dropdown on Discord Users Page (#873)
* added dropdown
* draft: add dropdown on discord user page to combine 'linked account' and 'n discord' buttons
* added dropdown in the discord users page
* Added dropdown option in the Discord Users page
* used data test id for the tests and variable for css
* added a comment on react.js for future convenience
---------
Co-authored-by: Depayan Mondal
Co-authored-by: Achintya Chatterjee <55826451+Achintya-Chatterjee@users.noreply.github.com>
Co-authored-by: Satyam Bajpai
---
__tests__/users/App.test.js | 6 ++----
__tests__/users/applyFilterPagination.test.js | 4 ++--
users/discord/App.js | 6 +++---
users/discord/components/TabsSection.js | 10 ++++++----
users/discord/style.css | 8 ++++++--
users/discord/utils/react.js | 3 ++-
6 files changed, 21 insertions(+), 16 deletions(-)
diff --git a/__tests__/users/App.test.js b/__tests__/users/App.test.js
index 54cbbeeb..f501677c 100644
--- a/__tests__/users/App.test.js
+++ b/__tests__/users/App.test.js
@@ -93,10 +93,8 @@ describe('App Component', () => {
});
it('should update the URL query string and re-render the app', async () => {
- await page.waitForSelector('[data_key="verified"]');
-
- // Click on the "Linked Accounts" tab
- await page.click('[data_key="verified"]');
+ await page.waitForSelector('[data-testid="tabs-section-select"]');
+ await page.select('[data-testid="tabs-section-select"]', 'verified');
// Get the current URL and make sure the query string has been updated
const url = await page.url();
diff --git a/__tests__/users/applyFilterPagination.test.js b/__tests__/users/applyFilterPagination.test.js
index a987fa99..e9561f0e 100644
--- a/__tests__/users/applyFilterPagination.test.js
+++ b/__tests__/users/applyFilterPagination.test.js
@@ -78,8 +78,8 @@ describe('Apply Filter and Pagination Functionality', () => {
});
it('should update the URL query string when applying filters', async () => {
- // click on the "Verified" tab
- await page.click('[data_key="verified"]');
+ await page.waitForSelector('[data-testid="tabs-section-select"]');
+ await page.select('[data-testid="tabs-section-select"]', 'verified');
// get the current URL
const url = await page.url();
diff --git a/users/discord/App.js b/users/discord/App.js
index d1b57e0e..2fe6dc37 100644
--- a/users/discord/App.js
+++ b/users/discord/App.js
@@ -7,8 +7,8 @@ import { NoUserFound } from './components/NoUserFound.js';
const { createElement, rerender } = react;
const tabs = [
- { display_name: 'In Discord', id: 'in_discord' },
- { display_name: 'Linked Accounts', id: 'verified' },
+ { display_name: 'In Discord', id: 'in_discord', value: 'in_discord' },
+ { display_name: 'Linked Accounts', id: 'verified', value: 'verified' },
];
export const usersData = {
in_discord: null,
@@ -22,7 +22,7 @@ let showUser = 0;
usersData[activeTab] = await getUsers(activeTab);
const handleTabNavigation = async (e) => {
- const selectedTabId = e.target.getAttribute('data_key');
+ const selectedTabId = e.target.value;
if (selectedTabId) {
document.location.search = `tab=${selectedTabId}`;
diff --git a/users/discord/components/TabsSection.js b/users/discord/components/TabsSection.js
index 57dd4416..9e3ff12d 100644
--- a/users/discord/components/TabsSection.js
+++ b/users/discord/components/TabsSection.js
@@ -2,18 +2,20 @@ const { createElement } = react;
export const TabsSection = ({ tabs, activeTab, handleTabNavigation }) => {
return createElement(
- 'div',
+ 'select',
{
class: 'tabs_section',
- onclick: handleTabNavigation,
- 'data-testid': 'tabs-section',
+ onchange: handleTabNavigation,
+ 'data-testid': 'tabs-section-select',
},
tabs.map((tabItem) => {
return createElement(
- 'span',
+ 'option',
{
data_key: `${tabItem.id}`,
class: `tab ${activeTab === tabItem.id ? 'active_tab' : ''}`,
+ value: `${tabItem.value}`,
+ ...(activeTab === tabItem.id && { selected: 'true' }),
},
[tabItem.display_name],
);
diff --git a/users/discord/style.css b/users/discord/style.css
index 23058547..c44c8f11 100644
--- a/users/discord/style.css
+++ b/users/discord/style.css
@@ -1,5 +1,6 @@
:root {
--light-gray: #d4d4d478;
+ --border-color-gray: #808080;
}
.header {
@@ -24,13 +25,16 @@ main {
}
.tabs_section {
- padding: 1rem 0;
+ padding: 1rem;
margin: 1rem;
grid-area: tab;
+ width: 18rem;
+ border: 2px solid var(--border-color-gray);
+ font-size: unset;
}
.tab {
- padding: 0.5rem;
+ padding: 1rem;
margin-inline-end: 0.5rem;
cursor: pointer;
}
diff --git a/users/discord/utils/react.js b/users/discord/utils/react.js
index faa43304..ed807527 100644
--- a/users/discord/utils/react.js
+++ b/users/discord/utils/react.js
@@ -8,7 +8,8 @@ const render = function (element, container) {
const component = document.createElement(element.tag);
element.props &&
Object.keys(element.props).forEach((prop) =>
- prop == 'onclick' || prop == 'onsubmit'
+ // based on requirements, any other type of event listner can be added here as 'prop'
+ prop == 'onclick' || prop == 'onsubmit' || prop == 'onchange'
? (component[prop] = element.props[prop])
: component.setAttribute(prop, element.props[prop]),
);