Skip to content

Commit

Permalink
[FIX] Figma User Portal: Organization Left Drawer Violates The Figma …
Browse files Browse the repository at this point in the history
…Style Guide (#3421)

* UI fix UserSidebarOrg

* fix

* Update src/components/ProfileDropdown/ProfileDropdown.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/components/ProfileDropdown/ProfileDropdown.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/components/ProfileCard/ProfileCard.spec.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/components/SignOut/SignOut.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/components/ProfileCard/ProfileCard.spec.tsx

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
hustlernik and coderabbitai[bot] authored Jan 25, 2025
1 parent 45b332c commit 00fe16b
Show file tree
Hide file tree
Showing 12 changed files with 1,058 additions and 382 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[Admin Docs](/)

***

# Function: default()

> **default**(): `Element`
Defined in: [src/components/ProfileCard/ProfileCard.tsx:21](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/ProfileCard/ProfileCard.tsx#L21)

Renders a profile card for the user.

This component displays the user's profile picture or an avatar, their name (truncated if necessary),
and their role (SuperAdmin, Admin, or User). It provides options to view the profile.

- If a user image is available, it displays that; otherwise, it shows an avatar.
- The displayed name is truncated if it exceeds a specified length.

## Returns

`Element`

JSX.Element - The profile card .
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[Admin Docs](/)

***

# Function: default()

> **default**(): `Element`
Defined in: [src/components/SignOut/SignOut.tsx:20](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/SignOut/SignOut.tsx#L20)

Renders a sign out button.

This component helps to logout.
The logout function revokes the refresh token and clears local storage before redirecting to the home page.

## Returns

`Element`

JSX.Element - The profile card .
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

> **default**(`__namedParameters`): `Element`
Defined in: [src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx:39](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx#L39)
Defined in: [src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx:42](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx#L42)

Sidebar component for user navigation within an organization.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,36 @@

# Interface: InterfaceUserSidebarOrgProps

Defined in: [src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx:17](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx#L17)
Defined in: [src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx:19](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx#L19)

## Properties

### hideDrawer

> **hideDrawer**: `boolean`
Defined in: [src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx:20](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx#L20)
Defined in: [src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx:22](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx#L22)

***

### orgId

> **orgId**: `string`
Defined in: [src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx:18](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx#L18)
Defined in: [src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx:20](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx#L20)

***

### setHideDrawer

> **setHideDrawer**: `Dispatch`\<`SetStateAction`\<`boolean`\>\>
Defined in: [src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx:21](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx#L21)
Defined in: [src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx:23](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx#L23)

***

### targets

> **targets**: [`TargetsType`](../../../../../state/reducers/routesReducer/type-aliases/TargetsType.md)[]
Defined in: [src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx:19](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx#L19)
Defined in: [src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx:21](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/components/UserPortal/UserSidebarOrg/UserSidebarOrg.tsx#L21)
191 changes: 191 additions & 0 deletions src/components/ProfileCard/ProfileCard.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import React, { act } from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import ProfileCard from './ProfileCard';
import { MockedProvider } from '@apollo/react-testing';
import { REVOKE_REFRESH_TOKEN } from 'GraphQl/Mutations/mutations';
import useLocalStorage from 'utils/useLocalstorage';
import { I18nextProvider } from 'react-i18next';
import i18nForTest from 'utils/i18nForTest';
import { GET_COMMUNITY_SESSION_TIMEOUT_DATA } from 'GraphQl/Queries/Queries';
import { vi } from 'vitest';

const { setItem } = useLocalStorage();

const mockNavigate = vi.fn();

// Mock useNavigate hook
vi.mock('react-router-dom', async () => {
const actual = await vi.importActual('react-router-dom');
return {
...actual,
useNavigate: () => mockNavigate,
};
});

const MOCKS = [
{
request: {
query: REVOKE_REFRESH_TOKEN,
},
result: {
data: {
revokeRefreshTokenForUser: true,
},
},
},
{
request: {
query: GET_COMMUNITY_SESSION_TIMEOUT_DATA,
},
result: {
data: {
getCommunityData: {
timeout: 30,
},
},
},
delay: 1000,
},
];

vi.mock('react-toastify', () => ({
toast: {
success: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
},
clear: vi.fn(),
}));

beforeEach(() => {
setItem('FirstName', 'John');
setItem('LastName', 'Doe');
setItem(
'UserImage',
'https://api.dicebear.com/5.x/initials/svg?seed=John%20Doe',
);
setItem('SuperAdmin', false);
setItem('AdminFor', []);
setItem('id', '123');
});

afterEach(() => {
vi.clearAllMocks();
localStorage.clear();
});

describe('ProfileDropdown Component', () => {
test('renders with user information', () => {
render(
<MockedProvider mocks={MOCKS} addTypename={false}>
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<ProfileCard />
</I18nextProvider>
</BrowserRouter>
</MockedProvider>,
);

expect(screen.getByTestId('display-name')).toBeInTheDocument();
expect(screen.getByText('John Doe')).toBeInTheDocument();
expect(screen.getByText('User')).toBeInTheDocument();
expect(screen.getByTestId('display-type')).toBeInTheDocument();
expect(screen.getByAltText('profile picture')).toBeInTheDocument();
});

test('renders Super admin', () => {
setItem('SuperAdmin', true);
render(
<MockedProvider mocks={MOCKS} addTypename={false}>
<BrowserRouter>
<ProfileCard />
</BrowserRouter>
</MockedProvider>,
);
expect(screen.getByText('SuperAdmin')).toBeInTheDocument();
});
test('renders Admin', () => {
setItem('AdminFor', ['123']);
render(
<MockedProvider mocks={MOCKS} addTypename={false}>
<BrowserRouter>
<ProfileCard />
</BrowserRouter>
</MockedProvider>,
);
expect(screen.getByText('Admin')).toBeInTheDocument();
});
});

describe('Member screen routing testing', () => {
test('navigates to /user/settings for a user', async () => {
setItem('SuperAdmin', false);
setItem('AdminFor', []);

render(
<MockedProvider mocks={MOCKS} addTypename={false}>
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<ProfileCard />
</I18nextProvider>
</BrowserRouter>
</MockedProvider>,
);

await act(async () => {
userEvent.click(screen.getByTestId('profileBtn'));
});

expect(mockNavigate).toHaveBeenCalledWith('/user/settings');
});

test('navigates to /member/:orgId for non-user roles when orgId is not present', async () => {
window.history.pushState({}, 'Test page', '/orglist');
setItem('SuperAdmin', true); // Set as admin
setItem('id', '123');

render(
<MockedProvider mocks={MOCKS} addTypename={false}>
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<Routes>
<Route path="/orglist" element={<ProfileCard />} />
</Routes>
</I18nextProvider>
</BrowserRouter>
</MockedProvider>,
);

await act(async () => {
userEvent.click(screen.getByTestId('profileBtn'));
});

expect(mockNavigate).toHaveBeenCalledWith('/member/');
});

test('navigates to /member/:userID for non-user roles', async () => {
window.history.pushState({}, 'Test page', '/321');
setItem('SuperAdmin', true); // Set as admin
setItem('id', '123');

render(
<MockedProvider mocks={MOCKS} addTypename={false}>
<BrowserRouter>
<I18nextProvider i18n={i18nForTest}>
<Routes>
<Route path="/:orgId" element={<ProfileCard />} />
</Routes>
</I18nextProvider>
</BrowserRouter>
</MockedProvider>,
);

await act(async () => {
userEvent.click(screen.getByTestId('profileBtn'));
});

expect(mockNavigate).toHaveBeenCalledWith('/member/321');
});
});
95 changes: 95 additions & 0 deletions src/components/ProfileCard/ProfileCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import Avatar from 'components/Avatar/Avatar';
import React from 'react';
import { ButtonGroup, Dropdown } from 'react-bootstrap';
import { useNavigate, useParams } from 'react-router-dom';
import useLocalStorage from 'utils/useLocalstorage';
import styles from './../../style/app.module.css';
import ChevronRightIcon from '@mui/icons-material/ChevronRight';

/**
* Renders a profile card for the user.
*
* This component displays the user's profile picture or an avatar, their name (truncated if necessary),
* and their role (SuperAdmin, Admin, or User). It provides options to view the profile.
*
* - If a user image is available, it displays that; otherwise, it shows an avatar.
* - The displayed name is truncated if it exceeds a specified length.
*
* @returns JSX.Element - The profile card .
*/
const profileCard = (): JSX.Element => {
const { getItem } = useLocalStorage();
const superAdmin = getItem('SuperAdmin');
const adminFor = getItem('AdminFor');
const userRole = superAdmin
? 'SuperAdmin'
: adminFor?.length > 0
? 'Admin'
: 'User';
const firstName = getItem('FirstName');
const lastName = getItem('LastName');
const userImage = getItem('UserImage');
const navigate = useNavigate();
const { orgId } = useParams();

const MAX_NAME_LENGTH = 20;
const fullName = `${firstName} ${lastName}`;
const displayedName =
fullName.length > MAX_NAME_LENGTH
? fullName.substring(0, MAX_NAME_LENGTH - 3) + '...'
: fullName;

return (
<Dropdown as={ButtonGroup} variant="none">
<div className={styles.profileContainer}>
<div className={styles.imageContainer}>
{userImage && userImage !== 'null' ? (
<img
src={userImage}
alt={`profile picture`}
data-testid="display-img"
/>
) : (
<Avatar
data-testid="display-img"
size={45}
avatarStyle={styles.avatarStyle}
name={`${firstName} ${lastName}`}
alt={`dummy picture`}
/>
)}
</div>
<div className={styles.profileTextUserSidebarOrg}>
<span className={styles.primaryText} data-testid="display-name">
{displayedName}
</span>
<span className={styles.secondaryText} data-testid="display-type">
{`${userRole}`}
</span>
</div>
<button
className="border-0 bg-white"
data-testid="profileBtn"
onClick={() =>
userRole === 'User'
? navigate(`/user/settings`)
: navigate(`/member/${orgId || ''}`)
}
>
<ChevronRightIcon
sx={{
color: 'gray',
background: 'white',
fontSize: 40,
strokeWidth: 0.5,
marginLeft: '50px',
}}
/>
</button>
</div>
</Dropdown>
);
};

export default profileCard;
6 changes: 1 addition & 5 deletions src/components/ProfileDropdown/ProfileDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,7 @@ const profileDropdown = (): JSX.Element => {
: fullName;

return (
<Dropdown
className={`${styles.profilebutton}`}
as={ButtonGroup}
variant="none"
>
<Dropdown as={ButtonGroup} variant="none">
<div className={styles.profileContainer}>
<div className={styles.imageContainer}>
{userImage && userImage !== 'null' ? (
Expand Down
Loading

0 comments on commit 00fe16b

Please sign in to comment.