diff --git a/src/App/App.test.tsx b/src/App/App.test.tsx
index 3e273bc..6b0a71d 100644
--- a/src/App/App.test.tsx
+++ b/src/App/App.test.tsx
@@ -1,13 +1,11 @@
import React from 'react';
-import { ReactWrapper } from 'enzyme';
-import { mount } from '../utils/test-enzyme';
+import { render, waitForElement, fireEvent } from '../utils/test-react-testing-library';
import storage from '../utils/storage';
// eslint-disable-next-line jest/no-mocks-import
import { generateTokenWithTimeRange } from '../../jest/unit/components/__mocks__/token';
import App from './App';
-import { AppProps } from './AppContext';
jest.mock('../utils/storage', () => {
class LocalStorageMock {
@@ -36,63 +34,71 @@ jest.mock('../utils/api', () => ({
request: require('../../jest/unit/components/__mocks__/api').default.request,
}));
-describe('App', () => {
- let wrapper: ReactWrapper<{}, AppProps, App>;
+/* eslint-disable react/jsx-no-bind*/
+describe('', () => {
+ test('should display the Loading component at the beginning ', () => {
+ const { container, queryByTestId } = render();
- beforeEach(() => {
- wrapper = mount();
+ expect(container.firstChild).toMatchSnapshot();
+ expect(queryByTestId('loading')).toBeTruthy();
});
- test('toggleLoginModal: should toggle the value in state', () => {
- const { handleToggleLoginModal } = wrapper.instance();
- expect(wrapper.state().showLoginModal).toBeFalsy();
- handleToggleLoginModal();
- expect(wrapper.state('showLoginModal')).toBeTruthy();
- expect(wrapper.state('error')).toEqual(undefined);
+ test('should display the Header component ', async () => {
+ const { container, queryByTestId } = render();
+
+ expect(container.firstChild).toMatchSnapshot();
+ expect(queryByTestId('loading')).toBeTruthy();
+
+ // wait for the Header component appearance and return the element
+ const headerElement = await waitForElement(() => queryByTestId('header'));
+ expect(headerElement).toBeTruthy();
+ });
+
+ test('handleLogout - logouts the user and clear localstorage', async () => {
+ storage.setItem('username', 'verdaccio');
+ storage.setItem('token', generateTokenWithTimeRange(24));
+
+ const { queryByTestId } = render();
+
+ // wait for the Account's circle element component appearance and return the element
+ const accountCircleElement = await waitForElement(() => queryByTestId('header--menu-accountcircle'));
+ expect(accountCircleElement).toBeTruthy();
+
+ if (accountCircleElement) {
+ fireEvent.click(accountCircleElement);
+
+ // wait for the Button's logout element component appearance and return the element
+ const buttonLogoutElement = await waitForElement(() => queryByTestId('header--button-logout'));
+ expect(buttonLogoutElement).toBeTruthy();
+
+ if (buttonLogoutElement) {
+ fireEvent.click(buttonLogoutElement);
+
+ expect(queryByTestId('greetings-label')).toBeFalsy();
+ }
+ }
});
test('isUserAlreadyLoggedIn: token already available in storage', async () => {
storage.setItem('username', 'verdaccio');
storage.setItem('token', generateTokenWithTimeRange(24));
- const { isUserAlreadyLoggedIn } = wrapper.instance();
- isUserAlreadyLoggedIn();
+ const { queryByTestId, queryAllByText } = render();
- expect(wrapper.state('user').username).toEqual('verdaccio');
- });
+ // wait for the Account's circle element component appearance and return the element
+ const accountCircleElement = await waitForElement(() => queryByTestId('header--menu-accountcircle'));
+ expect(accountCircleElement).toBeTruthy();
- test('handleLogout - logouts the user and clear localstorage', async () => {
- const { handleLogout } = wrapper.instance();
- storage.setItem('username', 'verdaccio');
- storage.setItem('token', 'xxxx.TOKEN.xxxx');
+ if (accountCircleElement) {
+ fireEvent.click(accountCircleElement);
- await handleLogout();
- expect(wrapper.state('user')).toEqual({});
- expect(wrapper.state('isUserLoggedIn')).toBeFalsy();
- });
+ // wait for the Greeting's label element component appearance and return the element
+ const greetingsLabelElement = await waitForElement(() => queryByTestId('greetings-label'));
+ expect(greetingsLabelElement).toBeTruthy();
- test('handleDoLogin - login the user successfully', async () => {
- const { handleDoLogin } = wrapper.instance();
- await handleDoLogin('sam', '1234');
- const result = {
- username: 'sam',
- };
- expect(wrapper.state('isUserLoggedIn')).toBeTruthy();
- expect(wrapper.state('showLoginModal')).toBeFalsy();
- expect(storage.getItem('username')).toEqual('sam');
- expect(storage.getItem('token')).toEqual('TEST_TOKEN');
- expect(wrapper.state('user')).toEqual(result);
- });
-
- test('handleDoLogin - authentication failure', async () => {
- const { handleDoLogin } = wrapper.instance();
- await handleDoLogin('sam', '12345');
- const result = {
- description: 'bad username/password, access denied',
- title: 'Unable to login',
- type: 'error',
- };
- expect(wrapper.state('user')).toEqual({});
- expect(wrapper.state('error')).toEqual(result);
+ if (greetingsLabelElement) {
+ expect(queryAllByText('verdaccio')).toBeTruthy();
+ }
+ }
});
});
diff --git a/src/App/App.tsx b/src/App/App.tsx
index 702d5e2..e32b812 100644
--- a/src/App/App.tsx
+++ b/src/App/App.tsx
@@ -1,184 +1,106 @@
-import React, { Component, ReactElement } from 'react';
+import React, { useState, useEffect } from 'react';
+import styled from '@emotion/styled';
import isNil from 'lodash/isNil';
+import { Router } from 'react-router-dom';
import storage from '../utils/storage';
-import { makeLogin, isTokenExpire } from '../utils/login';
-import Loading from '../components/Loading';
-import LoginModal from '../components/Login';
-import Header from '../components/Header';
-import { Container, Content } from '../components/Layout';
+import { isTokenExpire } from '../utils/login';
import API from '../utils/api';
+import Header from '../components/Header';
import Footer from '../components/Footer';
+import Box from '../muiComponents/Box';
+import Loading from '../components/Loading';
+import StyleBaseline from '../design-tokens/StyleBaseline';
+import { breakpoints } from '../utils/styles/media';
-import AppRoute from './AppRoute';
-import { AppProps, AppContextProvider } from './AppContext';
+import AppContextProvider from './AppContextProvider';
+import AppRoute, { history } from './AppRoute';
-export default class App extends Component<{}, AppProps> {
- public state: AppProps = {
- logoUrl: window.VERDACCIO_LOGO,
- user: {},
- scope: window.VERDACCIO_SCOPE || '',
- showLoginModal: false,
- isUserLoggedIn: false,
- packages: [],
- isLoading: true,
+const StyledBoxContent = styled(Box)({
+ padding: 15,
+ [`@media screen and (min-width: ${breakpoints.container}px)`]: {
+ maxWidth: breakpoints.container,
+ width: '100%',
+ marginLeft: 'auto',
+ marginRight: 'auto',
+ },
+});
+
+/* eslint-disable react/jsx-max-depth */
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+/* eslint-disable react/jsx-no-bind */
+/* eslint-disable react-hooks/exhaustive-deps */
+const App: React.FC = () => {
+ const [user, setUser] = useState();
+ const [packages, setPackages] = useState([]);
+ const [isLoading, setIsLoading] = useState(true);
+
+ /**
+ * Logouts user
+ * Required by:
+ */
+ const logout = () => {
+ storage.removeItem('username');
+ storage.removeItem('token');
+ setUser(undefined);
};
- public componentDidMount(): void {
- this.isUserAlreadyLoggedIn();
- this.loadOnHandler();
- }
-
- // eslint-disable-next-line no-unused-vars
- public componentDidUpdate(_: AppProps, prevState: AppProps): void {
- const { isUserLoggedIn } = this.state;
- if (prevState.isUserLoggedIn !== isUserLoggedIn) {
- this.loadOnHandler();
- }
- }
-
- public render(): React.ReactElement {
- const { isLoading, isUserLoggedIn, packages, logoUrl, user, scope } = this.state;
-
- const context = { isUserLoggedIn, packages, logoUrl, user, scope };
-
- return (
-
- {isLoading ? : {this.renderContent()}}
- {this.renderLoginModal()}
-
- );
- }
-
- public isUserAlreadyLoggedIn = () => {
+ const checkUserAlreadyLoggedIn = () => {
// checks for token validity
const token = storage.getItem('token');
- const username: string = storage.getItem('username') as string;
+ const username = storage.getItem('username');
+
if (isTokenExpire(token) || isNil(username)) {
- this.handleLogout();
- } else {
- this.setState({
- user: { username },
- isUserLoggedIn: true,
- });
+ logout();
+ return;
}
+
+ setUser({ username });
};
- public loadOnHandler = async () => {
+ const loadOnHandler = async () => {
try {
- const packages = await API.request('packages', 'GET');
- // @ts-ignore: FIX THIS TYPE: Type 'any[]' is not assignable to type '[]'
- this.setState({
- packages,
- isLoading: false,
- });
+ const packages = await API.request('packages', 'GET');
+ // FIXME add correct type for package
+ setPackages(packages as never[]);
} catch (error) {
// FIXME: add dialog
console.error({
title: 'Warning',
message: `Unable to load package list: ${error.message}`,
});
- this.setLoading(false);
- }
- };
-
- public setLoading = (isLoading: boolean) =>
- this.setState({
- isLoading,
- });
-
- /**
- * Toggles the login modal
- * Required by:
- */
- public handleToggleLoginModal = () => {
- this.setState(prevState => ({
- showLoginModal: !prevState.showLoginModal,
- }));
- };
-
- /**
- * handles login
- * Required by:
- */
- public 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);
- this.setLoggedUser(username);
}
- if (error) {
- this.setState({
- user: {},
- error,
- });
- }
+ setIsLoading(false);
};
- public setLoggedUser = (username: string) => {
- this.setState({
- user: {
- username,
- },
- isUserLoggedIn: true, // close login modal after successful login
- showLoginModal: false, // set isUserLoggedIn to true
- });
- };
+ useEffect(() => {
+ checkUserAlreadyLoggedIn();
+ loadOnHandler();
+ }, []);
- /**
- * Logouts user
- * Required by:
- */
- public handleLogout = () => {
- storage.removeItem('username');
- storage.removeItem('token');
- this.setState({
- user: {},
- isUserLoggedIn: false,
- });
- };
+ return (
+ <>
+
+
+ {isLoading ? (
+
+ ) : (
+ <>
+
+
+
+
+
+
+
+
+
+ >
+ )}
+
+ >
+ );
+};
- public renderLoginModal = (): ReactElement => {
- const { error, showLoginModal } = this.state;
- return (
-
- );
- };
-
- public renderContent = (): ReactElement => {
- return (
- <>
-
- {this.renderHeader()}
-
-
- >
- );
- };
-
- public renderHeader = (): ReactElement => {
- const {
- logoUrl,
- user: { username },
- scope,
- } = this.state;
-
- return (
-
- );
- };
-}
+export default App;
diff --git a/src/App/AppContext.ts b/src/App/AppContext.ts
new file mode 100644
index 0000000..e41a73c
--- /dev/null
+++ b/src/App/AppContext.ts
@@ -0,0 +1,23 @@
+import { createContext } from 'react';
+
+import { FormError } from '../components/Login/Login';
+
+export interface AppProps {
+ error?: FormError;
+ user?: User;
+ scope: string;
+ packages: any[];
+}
+
+export interface User {
+ username: string;
+}
+
+export interface AppContextProps extends AppProps {
+ setUser: (user?: User) => void;
+ setError: (error?: FormError) => void;
+}
+
+const AppContext = createContext(undefined);
+
+export default AppContext;
diff --git a/src/App/AppContext.tsx b/src/App/AppContext.tsx
deleted file mode 100644
index 9d06e08..0000000
--- a/src/App/AppContext.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import { createContext } from 'react';
-
-import { FormError } from '../components/Login/Login';
-
-export interface AppProps {
- error?: FormError;
- logoUrl: string;
- user: {
- username?: string;
- };
- scope: string;
- showLoginModal: boolean;
- isUserLoggedIn: boolean;
- packages: [];
- isLoading: boolean;
-}
-
-export const AppContext = createContext>({});
-export const AppContextProvider = AppContext.Provider;
-export const AppContextConsumer = AppContext.Consumer;
diff --git a/src/App/AppContextProvider.tsx b/src/App/AppContextProvider.tsx
new file mode 100644
index 0000000..260609f
--- /dev/null
+++ b/src/App/AppContextProvider.tsx
@@ -0,0 +1,61 @@
+import React, { useState, useEffect } from 'react';
+
+import { FormError } from '../components/Login/Login';
+
+import AppContext, { AppProps, User } from './AppContext';
+
+interface Props {
+ packages: any[];
+ user?: User;
+}
+
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+/* eslint-disable react-hooks/exhaustive-deps */
+const AppContextProvider: React.FC = ({ children, packages, user }) => {
+ const [state, setState] = useState({
+ scope: window.VERDACCIO_SCOPE || '',
+ packages,
+ user,
+ });
+
+ useEffect(() => {
+ setState({
+ ...state,
+ user,
+ });
+ }, [user]);
+
+ useEffect(() => {
+ setState({
+ ...state,
+ packages,
+ });
+ }, [packages]);
+
+ const setUser = (user?: User) => {
+ setState({
+ ...state,
+ user,
+ });
+ };
+
+ const setError = (error?: FormError) => {
+ setState({
+ ...state,
+ error,
+ });
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+export default AppContextProvider;
diff --git a/src/App/AppRoute.tsx b/src/App/AppRoute.tsx
index 46c745e..2b1d737 100644
--- a/src/App/AppRoute.tsx
+++ b/src/App/AppRoute.tsx
@@ -4,7 +4,7 @@ import { createBrowserHistory } from 'history';
import Loading from '../components/Loading';
-import { AppContext } from './AppContext';
+import AppContext from './AppContext';
const NotFound = lazy(() => import('../components/NotFound'));
const VersionContextProvider = lazy(() => import('../pages/Version/VersionContextProvider'));
@@ -19,19 +19,25 @@ enum Route {
PACKAGE_VERSION = '/-/web/detail/:package/v/:version',
}
-const history = createBrowserHistory({
+export const history = createBrowserHistory({
basename: window.__VERDACCIO_BASENAME_UI_OPTIONS && window.__VERDACCIO_BASENAME_UI_OPTIONS.url_prefix,
});
/* eslint react/jsx-max-depth: 0 */
-const AppRoute: React.FC = ({ children }) => {
+const AppRoute: React.FC = () => {
const appContext = useContext(AppContext);
- const { isUserLoggedIn, packages } = appContext;
+
+ if (!appContext) {
+ throw Error('The app Context was not correct used');
+ }
+
+ const { user, packages } = appContext;
+
+ const isUserLoggedIn = user && user.username;
return (
}>
- {children}
diff --git a/src/App/__snapshots__/App.test.tsx.snap b/src/App/__snapshots__/App.test.tsx.snap
new file mode 100644
index 0000000..1ad9492
--- /dev/null
+++ b/src/App/__snapshots__/App.test.tsx.snap
@@ -0,0 +1,179 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` should display the Header component 1`] = `
+.emotion-8 {
+ -webkit-transform: translate(-50%,-50%);
+ -ms-transform: translate(-50%,-50%);
+ transform: translate(-50%,-50%);
+ top: 50%;
+ left: 50%;
+ position: absolute;
+}
+
+.emotion-2 {
+ margin: 0 0 30px 0;
+ border-radius: 25px;
+ box-shadow: 0 10px 20px 0 rgba(69,58,100,0.2);
+ background: #f7f8f6;
+}
+
+.emotion-0 {
+ display: inline-block;
+ vertical-align: middle;
+ box-sizing: border-box;
+ background-position: center;
+ background-size: contain;
+ background-image: url([object Object]);
+ background-repeat: no-repeat;
+ width: 90px;
+ height: 90px;
+}
+
+.emotion-6 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -webkit-justify-content: center;
+ -ms-flex-pack: center;
+ justify-content: center;
+}
+
+.emotion-4 {
+ color: #4b5e40;
+}
+
+
+`;
+
+exports[` should display the Loading component at the beginning 1`] = `
+.emotion-8 {
+ -webkit-transform: translate(-50%,-50%);
+ -ms-transform: translate(-50%,-50%);
+ transform: translate(-50%,-50%);
+ top: 50%;
+ left: 50%;
+ position: absolute;
+}
+
+.emotion-2 {
+ margin: 0 0 30px 0;
+ border-radius: 25px;
+ box-shadow: 0 10px 20px 0 rgba(69,58,100,0.2);
+ background: #f7f8f6;
+}
+
+.emotion-0 {
+ display: inline-block;
+ vertical-align: middle;
+ box-sizing: border-box;
+ background-position: center;
+ background-size: contain;
+ background-image: url([object Object]);
+ background-repeat: no-repeat;
+ width: 90px;
+ height: 90px;
+}
+
+.emotion-6 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -webkit-justify-content: center;
+ -ms-flex-pack: center;
+ justify-content: center;
+}
+
+.emotion-4 {
+ color: #4b5e40;
+}
+
+
+`;
diff --git a/src/App/index.ts b/src/App/index.ts
index 9122fa1..97e147f 100644
--- a/src/App/index.ts
+++ b/src/App/index.ts
@@ -1 +1,2 @@
export { default } from './App';
+export { default as AppContextProvider } from './AppContextProvider';
diff --git a/src/components/Header/Header.test.tsx b/src/components/Header/Header.test.tsx
index 4654386..dee9036 100644
--- a/src/components/Header/Header.test.tsx
+++ b/src/components/Header/Header.test.tsx
@@ -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(' component with logged in state', () => {
test('should load the component in logged out state', () => {
const { container, queryByTestId, getByText } = render(
-
+
+
+
);
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(
-
+
+
+
);
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(
-
+
+
+
);
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(
-
+
+
+
);
- 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(
-
+
+
+
);
@@ -104,12 +93,9 @@ describe(' component with logged in state', () => {
test('should open the registrationInfo modal when clicking on the info icon', async () => {
const { getByTestId } = render(
-
+
+
+
);
@@ -124,12 +110,9 @@ describe(' component with logged in state', () => {
test('should close the registrationInfo modal when clicking on the button close', async () => {
const { getByTestId, getByText, queryByTestId } = render(
-
+
+
+
);
@@ -144,6 +127,6 @@ describe(' 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');
});
diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx
index 21f0cff..7c93a64 100644
--- a/src/components/Header/Header.tsx
+++ b/src/components/Header/Header.tsx
@@ -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 = ({ logo, withoutSearch, username, onLogout, onToggleLoginModal, scope }) => {
+/* eslint-disable @typescript-eslint/explicit-function-return-type */
+const Header: React.FC = ({ 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:
+ */
+ 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:
+ */
+ const handleLogout = () => {
+ storage.removeItem('username');
+ storage.removeItem('token');
+ setUser(undefined);
+ };
return (
<>
-
+
setOpenInfoDialog(true)}
- onToggleLogin={onToggleLoginModal}
+ onToggleLogin={() => setShowLoginModal(!showLoginModal)}
onToggleMobileNav={() => setShowMobileNavBar(!showMobileNavBar)}
- username={username}
+ username={user && user.username}
withoutSearch={withoutSearch}
/>
@@ -55,6 +94,12 @@ const Header: React.FC = ({ logo, withoutSearch, username, onLogout, onTo
)}
+ setShowLoginModal(false)}
+ onSubmit={handleDoLogin}
+ visibility={showLoginModal}
+ />
>
);
};
diff --git a/src/components/Header/HeaderGreetings.tsx b/src/components/Header/HeaderGreetings.tsx
index 4a76ae8..74c865f 100644
--- a/src/components/Header/HeaderGreetings.tsx
+++ b/src/components/Header/HeaderGreetings.tsx
@@ -11,7 +11,7 @@ interface Props {
const HeaderGreetings: React.FC = ({ username }) => (
<>
{'Hi,'}
-
+
>
);
diff --git a/src/components/Header/HeaderMenu.tsx b/src/components/Header/HeaderMenu.tsx
index ea83cda..295beb7 100644
--- a/src/components/Header/HeaderMenu.tsx
+++ b/src/components/Header/HeaderMenu.tsx
@@ -28,7 +28,7 @@ const HeaderMenu: React.FC = ({
<>