Skip to content

Commit

Permalink
feat: Implement the new top bar component
Browse files Browse the repository at this point in the history
  • Loading branch information
mkarajohn committed Oct 19, 2023
1 parent 0f0da6d commit 43b501c
Show file tree
Hide file tree
Showing 9 changed files with 380 additions and 211 deletions.
134 changes: 0 additions & 134 deletions src/authentication/components/TopBar/TopBar.test.tsx
Original file line number Diff line number Diff line change
@@ -1,134 +0,0 @@
import { ThemeProvider } from '@orfium/ictinus';
import { fireEvent, render, waitFor } from '@testing-library/react';
import React from 'react';

// @ts-ignore
import { createAuth0Client as mockedCreateAuth0 } from '../../../../__mocks__/@auth0/auth0-spa-js';
import { Organization } from '../../../store/useOrganization';
import { Authentication } from '../../index';
const mockOrganizations: Organization[] = [
{
org_id: 'org_cEVFmcCb0XYz1ZPf',
display_name: 'NBCU',
name: 'nbcu',
can_administrate: false,
metadata: {
type: 'other',
product_codes: 'media-engagement-tracker',
},
branding: {
logo_url: '',
},
},
{
org_id: 'org_cEVFmcCb0XYF4Hpp',
display_name: 'WMG',
name: 'wmg',
can_administrate: false,
metadata: {
type: 'other',
product_codes: 'media-engagement-tracker',
},
branding: {
logo_url: '',
},
},
];

const mockedUser = {
name: 'Joe Doe',
picture: '',
given_name: 'Joe',
family_name: 'Doe',
};
const mockedUserFn = jest
.fn<{ name: string; picture: string; given_name: string; family_name: string } | undefined, []>()
.mockReturnValue(mockedUser);
const mockSetSelectedOrganization = jest.fn();
const mockLogout = jest.fn();

jest.mock('../../../store/useOrganization', () =>
jest.fn(() => ({
organizations: mockOrganizations,
setSelectedOrganization: mockSetSelectedOrganization,
selectedOrganization: mockOrganizations[0],
}))
);

jest.mock('../../context', () => ({
useAuthentication: () => ({
user: mockedUserFn(),
logout: mockLogout,
}),
getAuth0Client: mockedCreateAuth0,
}));

describe('TopBar', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('Renders TopBar with organization selected', () => {
const { getByText } = render(
<ThemeProvider>
<Authentication.TopBar logoIcon={<img />} onMenuIconClick={() => {}} />
</ThemeProvider>
);

expect(getByText(mockOrganizations[0].display_name)).toBeTruthy();
});

it('Change οrganization will trigger on select', async () => {
const { getByText, getByTestId } = render(
<ThemeProvider>
<Authentication.TopBar logoIcon={<img />} onMenuIconClick={() => {}} />
</ThemeProvider>
);

fireEvent.click(getByText(mockOrganizations[0].display_name));
fireEvent.click(getByTestId('ictinus_list_item_0'));

await waitFor(() => expect(mockSetSelectedOrganization).toBeCalledTimes(1));
});

describe('user data', function () {
it('Renders TopBar user from the data given', () => {
const { getByText } = render(
<ThemeProvider>
<Authentication.TopBar logoIcon={<img />} onMenuIconClick={() => {}} />
</ThemeProvider>
);

expect(getByText(mockedUser.name)).toBeTruthy();
expect(
getByText(`${mockedUser?.given_name?.charAt(0)}${mockedUser?.family_name?.charAt(0)}`)
).toBeTruthy();
});

it('Renders TopBar user from the undefined data', () => {
mockedUserFn.mockReturnValue(undefined);

const { getByText } = render(
<ThemeProvider>
<Authentication.TopBar logoIcon={<img />} onMenuIconClick={() => {}} />
</ThemeProvider>
);

expect(getByText('undefined')).toBeTruthy();
});
});

it('Logout the user when press logout', async () => {
const { getByText, getByTestId } = render(
<ThemeProvider>
<Authentication.TopBar logoIcon={<img />} onMenuIconClick={() => {}} />
</ThemeProvider>
);

const userMenu = getByTestId('userMenu')?.firstChild;
userMenu && fireEvent.click(userMenu);
fireEvent.click(getByText('Logout'));

await waitFor(() => expect(mockLogout).toBeCalledTimes(1));
});
});
76 changes: 0 additions & 76 deletions src/authentication/components/TopBar/TopBar.tsx
Original file line number Diff line number Diff line change
@@ -1,76 +0,0 @@
import { Menu, TopNavBar } from '@orfium/ictinus';
import { TopAppBarProps } from '@orfium/ictinus/dist/components/TopAppBar/TopAppBar.types';
import React, { memo } from 'react';

import useOrganization from '../../../store/useOrganization';
import { getAuth0Client, useAuthentication } from '../../context';

export type TopBarProps = {
logoIcon: JSX.Element;
userMenu?: never;
} & Omit<TopAppBarProps, 'logoIcon' | 'userMenu'>;

/*
* The component to represent all the information that is coming from the SSO
* Based on Ictinus component
*/
export const TopBar: React.FC<TopBarProps> = memo(
({ logoIcon, onMenuIconClick, additionalTools }) => {
const { user, logout } = useAuthentication();
const { organizations, setSelectedOrganization, selectedOrganization } = useOrganization();

const userConfig = {
items: ['Logout'],
userName: `${user?.name}`,
userAvatar: {
src: `${user?.picture}`,
letter: `${user?.given_name?.charAt(0)}${user?.family_name?.charAt(0)}`,
},
onSelect: () => {
logout();
},
};

return (
<TopNavBar
logoIcon={logoIcon}
onMenuIconClick={onMenuIconClick}
userMenu={userConfig}
additionalTools={
<div
style={{
justifyContent: 'flex-end',
display: 'flex',
flexGrow: 1,
flex: 1,
}}
>
{additionalTools}
<Menu
dataTestId={'organization-picker'}
color={'lightGrey-50'}
onSelect={async (option: string) => {
const foundOrg = organizations.find((org) => org.display_name === option);
if (foundOrg) {
const client = await getAuth0Client();
await client.logout({ openUrl: false });
await client.loginWithRedirect({
authorizationParams: {
organization: foundOrg.org_id,
},
});
setSelectedOrganization(foundOrg);
}
}}
buttonText={selectedOrganization?.display_name}
items={organizations
.filter((org) => org.display_name !== selectedOrganization?.display_name)
.map((org) => org.display_name)}
/>
</div>
}
/>
);
}
);
TopBar.displayName = 'TopBar';
34 changes: 34 additions & 0 deletions src/ui/TopBar/TopBar.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { Theme } from '@orfium/ictinus';
import { flexCenter } from '@orfium/ictinus/dist/theme/functions';
import { rem } from 'polished';
import { DEFAULT_NAVBAR_HEIGHT } from '../consts';

export const backGround = (theme: Theme) => css`
background-color: ${theme.utils.getColor('blue', null, 'pale')};
`;
export const TopAppBarWrapper = styled.div`
${flexCenter};
background-color: transparent;
position: relative;
justify-content: space-between;
height: ${rem(DEFAULT_NAVBAR_HEIGHT)};
padding: 0;
`;

const topAppBarSectionStyles = css`
position: relative;
${flexCenter};
flex-wrap: nowrap;
`;

export const UserSection = styled.div`
${topAppBarSectionStyles};
flex-shrink: 0;
`;
export const UserDefinedSection = styled.div`
${topAppBarSectionStyles};
flex-grow: 1;
justify-content: flex-start;
`;
27 changes: 27 additions & 0 deletions src/ui/TopBar/TopBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Global } from '@emotion/react';
import { useBreakpoints, useTheme } from '@orfium/ictinus';
import { ReactNode } from 'react';
import Logo from '../../assets/orfium_logo.svg';
import UserMenu, { UserMenuProps } from './components/UserMenu';
import { backGround, TopAppBarWrapper, UserDefinedSection, UserSection } from './TopBar.styles';

export type TopBarProps = {
utilitySection?: ReactNode;
} & Partial<UserMenuProps>;

export default function TopBar({ utilitySection, menuItems = [] }: TopBarProps) {
const theme = useTheme();
const breakpoints = useBreakpoints();
const isDesktop = breakpoints.des1200;

return (
<TopAppBarWrapper role="banner" aria-label="Top Application Banner">
<Global styles={{ body: backGround(theme) }} />
{isDesktop ? null : <img alt={'Orfium logo'} src={Logo} height={28} width={28} />}
<UserDefinedSection>{utilitySection}</UserDefinedSection>
<UserSection>
<UserMenu menuItems={menuItems} />
</UserSection>
</TopAppBarWrapper>
);
}
Loading

0 comments on commit 43b501c

Please sign in to comment.