mirror of
https://github.com/SomboChea/ui
synced 2026-01-17 16:45:49 +07:00
Refactor(#209): Converted App component from class to func
This commit is contained in:
committed by
GitHub
parent
0a48906fc8
commit
0d9232a92c
@@ -1,16 +1,16 @@
|
||||
import React from 'react';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
|
||||
import { render, fireEvent, waitForElementToBeRemoved, waitForElement } from '../../utils/test-react-testing-library';
|
||||
import { render, fireEvent, waitForElement, waitForElementToBeRemoved } from '../../utils/test-react-testing-library';
|
||||
import { AppContextProvider } from '../../App';
|
||||
|
||||
import Header from './Header';
|
||||
|
||||
const headerProps = {
|
||||
username: 'verddacio-user',
|
||||
scope: 'test scope',
|
||||
withoutSearch: true,
|
||||
handleToggleLoginModal: jest.fn(),
|
||||
handleLogout: jest.fn(),
|
||||
const props = {
|
||||
user: {
|
||||
username: 'verddacio-user',
|
||||
},
|
||||
packages: [],
|
||||
};
|
||||
|
||||
/* eslint-disable react/jsx-no-bind*/
|
||||
@@ -18,82 +18,71 @@ describe('<Header /> component with logged in state', () => {
|
||||
test('should load the component in logged out state', () => {
|
||||
const { container, queryByTestId, getByText } = render(
|
||||
<Router>
|
||||
<Header
|
||||
onLogout={headerProps.handleLogout}
|
||||
onToggleLoginModal={headerProps.handleToggleLoginModal}
|
||||
scope={headerProps.scope}
|
||||
/>
|
||||
<AppContextProvider packages={props.packages}>
|
||||
<Header />
|
||||
</AppContextProvider>
|
||||
</Router>
|
||||
);
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
expect(queryByTestId('header--menu-acountcircle')).toBeNull();
|
||||
expect(queryByTestId('header--menu-accountcircle')).toBeNull();
|
||||
expect(getByText('Login')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should load the component in logged in state', () => {
|
||||
const { container, getByTestId, queryByText } = render(
|
||||
<Router>
|
||||
<Header
|
||||
onLogout={headerProps.handleLogout}
|
||||
onToggleLoginModal={headerProps.handleToggleLoginModal}
|
||||
scope={headerProps.scope}
|
||||
username={headerProps.username}
|
||||
/>
|
||||
<AppContextProvider packages={props.packages} user={props.user}>
|
||||
<Header />
|
||||
</AppContextProvider>
|
||||
</Router>
|
||||
);
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
expect(getByTestId('header--menu-acountcircle')).toBeTruthy();
|
||||
expect(getByTestId('header--menu-accountcircle')).toBeTruthy();
|
||||
expect(queryByText('Login')).toBeNull();
|
||||
});
|
||||
|
||||
test('should open login dialog', async () => {
|
||||
const { getByText } = render(
|
||||
const { getByText, getByTestId } = render(
|
||||
<Router>
|
||||
<Header
|
||||
onLogout={headerProps.handleLogout}
|
||||
onToggleLoginModal={headerProps.handleToggleLoginModal}
|
||||
scope={headerProps.scope}
|
||||
/>
|
||||
<AppContextProvider packages={props.packages}>
|
||||
<Header />
|
||||
</AppContextProvider>
|
||||
</Router>
|
||||
);
|
||||
|
||||
const loginBtn = getByText('Login');
|
||||
fireEvent.click(loginBtn);
|
||||
expect(headerProps.handleToggleLoginModal).toHaveBeenCalled();
|
||||
// wait for login modal appearance and return the element
|
||||
const registrationInfoModal = await waitForElement(() => getByTestId('login--form-container'));
|
||||
expect(registrationInfoModal).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should logout the user', async () => {
|
||||
const { getByText, getByTestId } = render(
|
||||
<Router>
|
||||
<Header
|
||||
onLogout={headerProps.handleLogout}
|
||||
onToggleLoginModal={headerProps.handleToggleLoginModal}
|
||||
scope={headerProps.scope}
|
||||
username={headerProps.username}
|
||||
/>
|
||||
<AppContextProvider packages={props.packages} user={props.user}>
|
||||
<Header />
|
||||
</AppContextProvider>
|
||||
</Router>
|
||||
);
|
||||
|
||||
const headerMenuAccountCircle = getByTestId('header--menu-acountcircle');
|
||||
const headerMenuAccountCircle = getByTestId('header--menu-accountcircle');
|
||||
fireEvent.click(headerMenuAccountCircle);
|
||||
|
||||
// wait for button Logout's appearance and return the element
|
||||
const logoutBtn = await waitForElement(() => getByText('Logout'));
|
||||
fireEvent.click(logoutBtn);
|
||||
expect(headerProps.handleLogout).toHaveBeenCalled();
|
||||
expect(getByText('Login')).toBeTruthy();
|
||||
});
|
||||
|
||||
test("The question icon should open a new tab of verdaccio's website - installation doc", async () => {
|
||||
test("The question icon should open a new tab of verdaccio's website - installation doc", () => {
|
||||
const { getByTestId } = render(
|
||||
<Router>
|
||||
<Header
|
||||
onLogout={headerProps.handleLogout}
|
||||
onToggleLoginModal={headerProps.handleToggleLoginModal}
|
||||
scope={headerProps.scope}
|
||||
username={headerProps.username}
|
||||
/>
|
||||
<AppContextProvider packages={props.packages} user={props.user}>
|
||||
<Header />
|
||||
</AppContextProvider>
|
||||
</Router>
|
||||
);
|
||||
|
||||
@@ -104,12 +93,9 @@ describe('<Header /> component with logged in state', () => {
|
||||
test('should open the registrationInfo modal when clicking on the info icon', async () => {
|
||||
const { getByTestId } = render(
|
||||
<Router>
|
||||
<Header
|
||||
onLogout={headerProps.handleLogout}
|
||||
onToggleLoginModal={headerProps.handleToggleLoginModal}
|
||||
scope={headerProps.scope}
|
||||
username={headerProps.username}
|
||||
/>
|
||||
<AppContextProvider packages={props.packages} user={props.user}>
|
||||
<Header />
|
||||
</AppContextProvider>
|
||||
</Router>
|
||||
);
|
||||
|
||||
@@ -124,12 +110,9 @@ describe('<Header /> component with logged in state', () => {
|
||||
test('should close the registrationInfo modal when clicking on the button close', async () => {
|
||||
const { getByTestId, getByText, queryByTestId } = render(
|
||||
<Router>
|
||||
<Header
|
||||
onLogout={headerProps.handleLogout}
|
||||
onToggleLoginModal={headerProps.handleToggleLoginModal}
|
||||
scope={headerProps.scope}
|
||||
username={headerProps.username}
|
||||
/>
|
||||
<AppContextProvider packages={props.packages} user={props.user}>
|
||||
<Header />
|
||||
</AppContextProvider>
|
||||
</Router>
|
||||
);
|
||||
|
||||
@@ -144,6 +127,6 @@ describe('<Header /> component with logged in state', () => {
|
||||
queryByTestId('registryInfo--dialog')
|
||||
);
|
||||
expect(hasRegistrationInfoModalBeenRemoved).toBeTruthy();
|
||||
test.todo('autocompletion should display suggestions according to the type value');
|
||||
});
|
||||
test.todo('autocompletion should display suggestions according to the type value');
|
||||
});
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useContext } from 'react';
|
||||
|
||||
import Search from '../Search';
|
||||
import storage from '../../utils/storage';
|
||||
import { getRegistryURL } from '../../utils/url';
|
||||
import { makeLogin } from '../../utils/login';
|
||||
import Button from '../../muiComponents/Button';
|
||||
import AppContext from '../../App/AppContext';
|
||||
import LoginModal from '../Login';
|
||||
import Search from '../Search';
|
||||
|
||||
import { NavBar, InnerNavBar, MobileNavBar, InnerMobileNavBar } from './styles';
|
||||
import HeaderLeft from './HeaderLeft';
|
||||
@@ -10,31 +14,66 @@ import HeaderRight from './HeaderRight';
|
||||
import HeaderInfoDialog from './HeaderInfoDialog';
|
||||
|
||||
interface Props {
|
||||
logo?: string;
|
||||
username?: string;
|
||||
onLogout: () => void;
|
||||
onToggleLoginModal: () => void;
|
||||
scope: string;
|
||||
withoutSearch?: boolean;
|
||||
}
|
||||
|
||||
/* eslint-disable react/jsx-max-depth */
|
||||
/* eslint-disable react/jsx-no-bind*/
|
||||
const Header: React.FC<Props> = ({ logo, withoutSearch, username, onLogout, onToggleLoginModal, scope }) => {
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
const Header: React.FC<Props> = ({ withoutSearch }) => {
|
||||
const appContext = useContext(AppContext);
|
||||
const [isInfoDialogOpen, setOpenInfoDialog] = useState();
|
||||
const [showMobileNavBar, setShowMobileNavBar] = useState();
|
||||
const [showLoginModal, setShowLoginModal] = useState(false);
|
||||
|
||||
if (!appContext) {
|
||||
throw Error('The app Context was not correct used');
|
||||
}
|
||||
|
||||
const { user, scope, error, setUser, setError } = appContext;
|
||||
const logo = window.VERDACCIO_LOGO;
|
||||
|
||||
/**
|
||||
* handles login
|
||||
* Required by: <Header />
|
||||
*/
|
||||
const handleDoLogin = async (usernameValue: string, passwordValue: string) => {
|
||||
const { username, token, error } = await makeLogin(usernameValue, passwordValue);
|
||||
|
||||
if (username && token) {
|
||||
storage.setItem('username', username);
|
||||
storage.setItem('token', token);
|
||||
setUser({ username });
|
||||
setShowLoginModal(false);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
setUser(undefined);
|
||||
setError(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Logouts user
|
||||
* Required by: <Header />
|
||||
*/
|
||||
const handleLogout = () => {
|
||||
storage.removeItem('username');
|
||||
storage.removeItem('token');
|
||||
setUser(undefined);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<NavBar position="static">
|
||||
<NavBar data-testid="header" position="static">
|
||||
<InnerNavBar>
|
||||
<HeaderLeft logo={logo} />
|
||||
<HeaderRight
|
||||
onLogout={onLogout}
|
||||
onLogout={handleLogout}
|
||||
onOpenRegistryInfoDialog={() => setOpenInfoDialog(true)}
|
||||
onToggleLogin={onToggleLoginModal}
|
||||
onToggleLogin={() => setShowLoginModal(!showLoginModal)}
|
||||
onToggleMobileNav={() => setShowMobileNavBar(!showMobileNavBar)}
|
||||
username={username}
|
||||
username={user && user.username}
|
||||
withoutSearch={withoutSearch}
|
||||
/>
|
||||
</InnerNavBar>
|
||||
@@ -55,6 +94,12 @@ const Header: React.FC<Props> = ({ logo, withoutSearch, username, onLogout, onTo
|
||||
</Button>
|
||||
</MobileNavBar>
|
||||
)}
|
||||
<LoginModal
|
||||
error={error}
|
||||
onCancel={() => setShowLoginModal(false)}
|
||||
onSubmit={handleDoLogin}
|
||||
visibility={showLoginModal}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@ interface Props {
|
||||
const HeaderGreetings: React.FC<Props> = ({ username }) => (
|
||||
<>
|
||||
<Greetings>{'Hi,'}</Greetings>
|
||||
<Label capitalize={true} text={username} weight="bold" />
|
||||
<Label capitalize={true} data-testid="greetings-label" text={username} weight="bold" />
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ const HeaderMenu: React.FC<Props> = ({
|
||||
<>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
data-testid="header--menu-acountcircle"
|
||||
data-testid="header--menu-accountcircle"
|
||||
id="header--button-account"
|
||||
onClick={onLoggedInMenu}>
|
||||
<AccountCircle />
|
||||
@@ -48,7 +48,7 @@ const HeaderMenu: React.FC<Props> = ({
|
||||
<MenuItem disabled={true}>
|
||||
<HeaderGreetings username={username} />
|
||||
</MenuItem>
|
||||
<MenuItem button={true} id="header--button-logout" onClick={onLogout}>
|
||||
<MenuItem button={true} data-testid="header--button-logout" id="header--button-logout" onClick={onLogout}>
|
||||
{'Logout'}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
@@ -53,7 +53,7 @@ const HeaderRight: React.FC<Props> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<RightSide>
|
||||
<RightSide data-testid="header-right">
|
||||
{!withoutSearch && (
|
||||
<HeaderToolTip onClick={onToggleMobileNav} title={'Search packages'} tooltipIconType={'search'} />
|
||||
)}
|
||||
|
||||
@@ -141,6 +141,7 @@ exports[`<Header /> component with logged in state should load the component in
|
||||
|
||||
<header
|
||||
class="MuiPaper-root MuiPaper-elevation4 MuiAppBar-root MuiAppBar-positionStatic emotion-24 emotion-25 MuiAppBar-colorPrimary"
|
||||
data-testid="header"
|
||||
>
|
||||
<div
|
||||
class="MuiToolbar-root MuiToolbar-regular emotion-22 emotion-23 MuiToolbar-gutters"
|
||||
@@ -213,6 +214,7 @@ exports[`<Header /> component with logged in state should load the component in
|
||||
</div>
|
||||
<div
|
||||
class="MuiToolbar-root MuiToolbar-regular emotion-20 emotion-21 MuiToolbar-gutters"
|
||||
data-testid="header-right"
|
||||
>
|
||||
<button
|
||||
class="MuiButtonBase-root MuiIconButton-root emotion-16 emotion-17 MuiIconButton-colorInherit"
|
||||
@@ -302,7 +304,7 @@ exports[`<Header /> component with logged in state should load the component in
|
||||
</button>
|
||||
<button
|
||||
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit"
|
||||
data-testid="header--menu-acountcircle"
|
||||
data-testid="header--menu-accountcircle"
|
||||
id="header--button-account"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
@@ -472,6 +474,7 @@ exports[`<Header /> component with logged in state should load the component in
|
||||
|
||||
<header
|
||||
class="MuiPaper-root MuiPaper-elevation4 MuiAppBar-root MuiAppBar-positionStatic emotion-24 emotion-25 MuiAppBar-colorPrimary"
|
||||
data-testid="header"
|
||||
>
|
||||
<div
|
||||
class="MuiToolbar-root MuiToolbar-regular emotion-22 emotion-23 MuiToolbar-gutters"
|
||||
@@ -544,6 +547,7 @@ exports[`<Header /> component with logged in state should load the component in
|
||||
</div>
|
||||
<div
|
||||
class="MuiToolbar-root MuiToolbar-regular emotion-20 emotion-21 MuiToolbar-gutters"
|
||||
data-testid="header-right"
|
||||
>
|
||||
<button
|
||||
class="MuiButtonBase-root MuiIconButton-root emotion-16 emotion-17 MuiIconButton-colorInherit"
|
||||
|
||||
Reference in New Issue
Block a user