forked from sombochea/verdaccio-ui
fix: Header Component - Replaced class by func. comp (#142)
* refactor: replaced class by func.comp * refactor: replacing jest test by react-testing-library * refactor: added test todos * feat: added more unit tests * fix: fixed tooltip import * fix: fixed test * fix: fixed typo * fix: fixed imports
This commit is contained in:
parent
ae73772a37
commit
d1ce82854a
@ -1,125 +1,138 @@
|
||||
import React from 'react';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { shallow } from 'enzyme';
|
||||
import { render, fireEvent, waitForElementToBeRemoved, waitForElement } from '@testing-library/react';
|
||||
|
||||
import Header from './Header';
|
||||
|
||||
describe('<Header /> component with logged in state', () => {
|
||||
let wrapper;
|
||||
let routerWrapper;
|
||||
let instance;
|
||||
let props;
|
||||
const headerProps = {
|
||||
username: 'verddacio-user',
|
||||
scope: 'test scope',
|
||||
withoutSearch: true,
|
||||
handleToggleLoginModal: jest.fn(),
|
||||
handleLogout: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
props = {
|
||||
username: 'test user',
|
||||
handleLogout: jest.fn(),
|
||||
logo: '',
|
||||
onToggleLoginModal: jest.fn(),
|
||||
scope: 'test scope',
|
||||
withoutSearch: true,
|
||||
};
|
||||
routerWrapper = shallow(
|
||||
/* eslint-disable react/jsx-no-bind*/
|
||||
describe('<Header /> component with logged in state', () => {
|
||||
test('should load the component in logged out state', () => {
|
||||
const { container, queryByTestId, getByText } = render(
|
||||
<Router>
|
||||
<Header
|
||||
logo={props.logo}
|
||||
onLogout={props.handleLogout}
|
||||
onToggleLoginModal={props.onToggleLoginModal}
|
||||
scope={props.scope}
|
||||
username={props.username}
|
||||
withoutSearch={props.withoutSearch}
|
||||
/>
|
||||
<Header onLogout={headerProps.handleLogout} onToggleLoginModal={headerProps.handleToggleLoginModal} scope={headerProps.scope} />
|
||||
</Router>
|
||||
);
|
||||
wrapper = routerWrapper.find(Header).dive();
|
||||
instance = wrapper.instance();
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
expect(queryByTestId('header--menu-acountcircle')).toBeNull();
|
||||
expect(getByText('Login')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should load the component in logged in state', () => {
|
||||
const state = {
|
||||
openInfoDialog: false,
|
||||
packages: undefined,
|
||||
registryUrl: 'http://localhost',
|
||||
showMobileNavBar: false,
|
||||
};
|
||||
|
||||
expect(wrapper.state()).toEqual(state);
|
||||
expect(routerWrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('handleLoggedInMenu: set anchorEl to html element value in state', () => {
|
||||
// creates a sample menu
|
||||
const div = document.createElement('div');
|
||||
const text = document.createTextNode('sample menu');
|
||||
div.appendChild(text);
|
||||
|
||||
const event = {
|
||||
currentTarget: div,
|
||||
};
|
||||
|
||||
instance.handleLoggedInMenu(event);
|
||||
expect(wrapper.state('anchorEl')).toEqual(div);
|
||||
});
|
||||
});
|
||||
|
||||
describe('<Header /> component with logged out state', () => {
|
||||
let wrapper;
|
||||
let routerWrapper;
|
||||
let instance;
|
||||
let props;
|
||||
|
||||
beforeEach(() => {
|
||||
props = {
|
||||
handleLogout: jest.fn(),
|
||||
onToggleLoginModal: jest.fn(),
|
||||
scope: 'test scope',
|
||||
logo: '',
|
||||
withoutSearch: true,
|
||||
};
|
||||
routerWrapper = shallow(
|
||||
const { container, getByTestId, queryByText } = render(
|
||||
<Router>
|
||||
<Header
|
||||
logo={props.logo}
|
||||
onLogout={props.handleLogout}
|
||||
onToggleLoginModal={props.onToggleLoginModal}
|
||||
scope={props.scope}
|
||||
withoutSearch={props.withoutSearch}
|
||||
onLogout={headerProps.handleLogout}
|
||||
onToggleLoginModal={headerProps.handleToggleLoginModal}
|
||||
scope={headerProps.scope}
|
||||
username={headerProps.username}
|
||||
/>
|
||||
</Router>
|
||||
);
|
||||
wrapper = routerWrapper.find(Header).dive();
|
||||
instance = wrapper.instance();
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
expect(getByTestId('header--menu-acountcircle')).toBeTruthy();
|
||||
expect(queryByText('Login')).toBeNull();
|
||||
});
|
||||
|
||||
test('should load the component in logged out state', () => {
|
||||
const state = {
|
||||
openInfoDialog: false,
|
||||
packages: undefined,
|
||||
registryUrl: 'http://localhost',
|
||||
showMobileNavBar: false,
|
||||
};
|
||||
expect(wrapper.state()).toEqual(state);
|
||||
expect(routerWrapper.html()).toMatchSnapshot();
|
||||
test('should open login dialog', async () => {
|
||||
const { getByText } = render(
|
||||
<Router>
|
||||
<Header onLogout={headerProps.handleLogout} onToggleLoginModal={headerProps.handleToggleLoginModal} scope={headerProps.scope} />
|
||||
</Router>
|
||||
);
|
||||
|
||||
const loginBtn = getByText('Login');
|
||||
fireEvent.click(loginBtn);
|
||||
expect(headerProps.handleToggleLoginModal).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('handleLoggedInMenuClose: set anchorEl value to null in state', () => {
|
||||
instance.handleLoggedInMenuClose();
|
||||
expect(wrapper.state('anchorEl')).toBeNull();
|
||||
test('should logout the user', async () => {
|
||||
const { getByText, getByTestId } = render(
|
||||
<Router>
|
||||
<Header
|
||||
onLogout={headerProps.handleLogout}
|
||||
onToggleLoginModal={headerProps.handleToggleLoginModal}
|
||||
scope={headerProps.scope}
|
||||
username={headerProps.username}
|
||||
/>
|
||||
</Router>
|
||||
);
|
||||
|
||||
const headerMenuAccountCircle = getByTestId('header--menu-acountcircle');
|
||||
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();
|
||||
});
|
||||
|
||||
test('handleOpenRegistryInfoDialog: set openInfoDialog to be truthy in state', () => {
|
||||
instance.handleOpenRegistryInfoDialog();
|
||||
expect(wrapper.state('openInfoDialog')).toBeTruthy();
|
||||
test("The question icon should open a new tab of verdaccio's website - installation doc", async () => {
|
||||
const { getByTestId } = render(
|
||||
<Router>
|
||||
<Header
|
||||
onLogout={headerProps.handleLogout}
|
||||
onToggleLoginModal={headerProps.handleToggleLoginModal}
|
||||
scope={headerProps.scope}
|
||||
username={headerProps.username}
|
||||
/>
|
||||
</Router>
|
||||
);
|
||||
|
||||
const documentationBtn = getByTestId('header--tooltip-documentation');
|
||||
expect(documentationBtn.getAttribute('href')).toBe('https://verdaccio.org/docs/en/installation');
|
||||
});
|
||||
|
||||
test('handleCloseRegistryInfoDialog: set openInfoDialog to be falsy in state', () => {
|
||||
instance.handleCloseRegistryInfoDialog();
|
||||
expect(wrapper.state('openInfoDialog')).toBeFalsy();
|
||||
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}
|
||||
/>
|
||||
</Router>
|
||||
);
|
||||
|
||||
const infoBtn = getByTestId('header--tooltip-info');
|
||||
fireEvent.click(infoBtn);
|
||||
|
||||
// wait for registrationInfo modal appearance and return the element
|
||||
const registrationInfoModal = await waitForElement(() => getByTestId('registryInfo--dialog'));
|
||||
expect(registrationInfoModal).toBeTruthy();
|
||||
});
|
||||
|
||||
test('handleToggleLogin: close/open popover menu', () => {
|
||||
instance.handleToggleLogin();
|
||||
expect(wrapper.state('anchorEl')).toBeNull();
|
||||
expect(props.onToggleLoginModal).toHaveBeenCalled();
|
||||
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}
|
||||
/>
|
||||
</Router>
|
||||
);
|
||||
|
||||
const infoBtn = getByTestId('header--tooltip-info');
|
||||
fireEvent.click(infoBtn);
|
||||
|
||||
// wait for Close's button of registrationInfo modal appearance and return the element
|
||||
const closeBtn = await waitForElement(() => getByText('CLOSE'));
|
||||
fireEvent.click(closeBtn);
|
||||
|
||||
const hasRegistrationInfoModalBeenRemoved = await waitForElementToBeRemoved(() => queryByTestId('registryInfo--dialog'));
|
||||
expect(hasRegistrationInfoModalBeenRemoved).toBeTruthy();
|
||||
});
|
||||
test.todo('autocompletion should display suggestions according to the type value');
|
||||
});
|
||||
|
@ -1,35 +1,13 @@
|
||||
import React, { SyntheticEvent, Component, Fragment, ReactElement } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { css } from 'emotion';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import Menu from '@material-ui/core/Menu';
|
||||
import Info from '@material-ui/icons/Info';
|
||||
import Help from '@material-ui/icons/Help';
|
||||
import AccountCircle from '@material-ui/icons/AccountCircle';
|
||||
import { default as IconSearch } from '@material-ui/icons/Search';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import Search from '../Search';
|
||||
import { getRegistryURL } from '../../utils/url';
|
||||
import Logo from '../Logo';
|
||||
import RegistryInfoDialog from '../RegistryInfoDialog/RegistryInfoDialog';
|
||||
import Label from '../Label/Label';
|
||||
import Search from '../Search/Search';
|
||||
import RegistryInfoContent from '../RegistryInfoContent/RegistryInfoContent';
|
||||
import IconButton from '../../muiComponents/IconButton';
|
||||
import Tooltip from '../../muiComponents/Tooltip';
|
||||
import Button from '../../muiComponents/Button';
|
||||
|
||||
import {
|
||||
Greetings,
|
||||
NavBar,
|
||||
InnerNavBar,
|
||||
MobileNavBar,
|
||||
InnerMobileNavBar,
|
||||
LeftSide,
|
||||
RightSide,
|
||||
IconSearchButton,
|
||||
SearchWrapper,
|
||||
StyledExternalLink,
|
||||
} from './styles';
|
||||
import { NavBar, InnerNavBar, MobileNavBar, InnerMobileNavBar } from './styles';
|
||||
import HeaderLeft from './HeaderLeft';
|
||||
import HeaderRight from './HeaderRight';
|
||||
import HeaderInfoDialog from './HeaderInfoDialog';
|
||||
|
||||
interface Props {
|
||||
logo?: string;
|
||||
@ -40,248 +18,38 @@ interface Props {
|
||||
withoutSearch?: boolean;
|
||||
}
|
||||
|
||||
interface State {
|
||||
anchorEl?: null | Element | ((element: Element) => Element);
|
||||
openInfoDialog: boolean;
|
||||
registryUrl: string;
|
||||
showMobileNavBar: boolean;
|
||||
}
|
||||
/* eslint-disable react/jsx-max-depth */
|
||||
/* eslint-disable react/jsx-no-bind*/
|
||||
const Header: React.FC<Props> = ({ logo, withoutSearch, username, onLogout, onToggleLoginModal, scope }) => {
|
||||
const [isInfoDialogOpen, setOpenInfoDialog] = useState();
|
||||
const [showMobileNavBar, setShowMobileNavBar] = useState();
|
||||
|
||||
type ToolTipType = 'search' | 'help' | 'info';
|
||||
|
||||
class Header extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
openInfoDialog: false,
|
||||
registryUrl: getRegistryURL(),
|
||||
showMobileNavBar: false,
|
||||
};
|
||||
}
|
||||
|
||||
public render(): ReactElement<HTMLElement> {
|
||||
const { showMobileNavBar } = this.state;
|
||||
const { withoutSearch = false } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<NavBar position="static">
|
||||
<InnerNavBar>
|
||||
{this.renderLeftSide()}
|
||||
{this.renderRightSide()}
|
||||
</InnerNavBar>
|
||||
{this.renderInfoDialog()}
|
||||
</NavBar>
|
||||
{showMobileNavBar && !withoutSearch && (
|
||||
<MobileNavBar>
|
||||
<InnerMobileNavBar>
|
||||
<Search />
|
||||
</InnerMobileNavBar>
|
||||
<Button color="inherit" onClick={this.handleDismissMNav}>
|
||||
{'Cancel'}
|
||||
</Button>
|
||||
</MobileNavBar>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* opens popover menu for logged in user.
|
||||
*/
|
||||
public handleLoggedInMenu = (event: SyntheticEvent<HTMLElement>) => {
|
||||
this.setState({
|
||||
anchorEl: event.currentTarget,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* closes popover menu for logged in user
|
||||
*/
|
||||
public handleLoggedInMenuClose = () => {
|
||||
this.setState({
|
||||
anchorEl: null,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* opens registry information dialog.
|
||||
*/
|
||||
public handleOpenRegistryInfoDialog = () => {
|
||||
this.setState({
|
||||
openInfoDialog: true,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* closes registry information dialog.
|
||||
*/
|
||||
public handleCloseRegistryInfoDialog = () => {
|
||||
this.setState({
|
||||
openInfoDialog: false,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* close/open popover menu for logged in users.
|
||||
*/
|
||||
public handleToggleLogin = () => {
|
||||
const { onToggleLoginModal } = this.props;
|
||||
this.setState(
|
||||
{
|
||||
anchorEl: null,
|
||||
},
|
||||
onToggleLoginModal
|
||||
);
|
||||
};
|
||||
|
||||
public handleToggleMNav = () => {
|
||||
const { showMobileNavBar } = this.state;
|
||||
this.setState({
|
||||
showMobileNavBar: !showMobileNavBar,
|
||||
});
|
||||
};
|
||||
|
||||
public handleDismissMNav = () => {
|
||||
this.setState({
|
||||
showMobileNavBar: false,
|
||||
});
|
||||
};
|
||||
|
||||
public renderLeftSide = () => {
|
||||
const { withoutSearch = false } = this.props;
|
||||
return (
|
||||
<LeftSide>
|
||||
<Link
|
||||
className={css`
|
||||
margin-right: 1em;
|
||||
`}
|
||||
to={'/'}>
|
||||
{this.renderLogo()}
|
||||
</Link>
|
||||
{!withoutSearch && (
|
||||
<SearchWrapper>
|
||||
return (
|
||||
<>
|
||||
<NavBar position="static">
|
||||
<InnerNavBar>
|
||||
<HeaderLeft logo={logo} />
|
||||
<HeaderRight
|
||||
onLogout={onLogout}
|
||||
onOpenRegistryInfoDialog={() => setOpenInfoDialog(true)}
|
||||
onToggleLogin={onToggleLoginModal}
|
||||
onToggleMobileNav={() => setShowMobileNavBar(!showMobileNavBar)}
|
||||
username={username}
|
||||
withoutSearch={withoutSearch}
|
||||
/>
|
||||
</InnerNavBar>
|
||||
<HeaderInfoDialog isOpen={isInfoDialogOpen} onCloseDialog={() => setOpenInfoDialog(false)} registryUrl={getRegistryURL()} scope={scope} />
|
||||
</NavBar>
|
||||
{showMobileNavBar && !withoutSearch && (
|
||||
<MobileNavBar>
|
||||
<InnerMobileNavBar>
|
||||
<Search />
|
||||
</SearchWrapper>
|
||||
)}
|
||||
</LeftSide>
|
||||
);
|
||||
};
|
||||
|
||||
public renderLogo = () => {
|
||||
const { logo } = this.props;
|
||||
|
||||
if (logo) {
|
||||
return <img alt="logo" height="40px" src={logo} />;
|
||||
} else {
|
||||
return <Logo />;
|
||||
}
|
||||
};
|
||||
|
||||
public renderToolTipIcon = (title: string, type: ToolTipType) => {
|
||||
let content;
|
||||
switch (type) {
|
||||
case 'help':
|
||||
content = (
|
||||
<StyledExternalLink blank={true} to={'https://verdaccio.org/docs/en/installation'}>
|
||||
<IconButton color={'inherit'}>
|
||||
<Help />
|
||||
</IconButton>
|
||||
</StyledExternalLink>
|
||||
);
|
||||
break;
|
||||
case 'info':
|
||||
content = (
|
||||
<IconButton color="inherit" id="header--button-registryInfo" onClick={this.handleOpenRegistryInfoDialog}>
|
||||
<Info />
|
||||
</IconButton>
|
||||
);
|
||||
break;
|
||||
case 'search':
|
||||
content = (
|
||||
<IconSearchButton color="inherit" onClick={this.handleToggleMNav}>
|
||||
<IconSearch />
|
||||
</IconSearchButton>
|
||||
);
|
||||
break;
|
||||
}
|
||||
return (
|
||||
<Tooltip disableFocusListener={true} title={title}>
|
||||
{content}
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
public renderRightSide = () => {
|
||||
const { username = '', withoutSearch = false } = this.props;
|
||||
return (
|
||||
<RightSide>
|
||||
{!withoutSearch && this.renderToolTipIcon('Search packages', 'search')}
|
||||
{this.renderToolTipIcon('Documentation', 'help')}
|
||||
{this.renderToolTipIcon('Registry Information', 'info')}
|
||||
{username ? (
|
||||
this.renderMenu()
|
||||
) : (
|
||||
<Button color="inherit" id="header--button-login" onClick={this.handleToggleLogin}>
|
||||
{'Login'}
|
||||
</Button>
|
||||
)}
|
||||
</RightSide>
|
||||
);
|
||||
};
|
||||
|
||||
private renderGreetings = () => {
|
||||
const { username = '' } = this.props;
|
||||
return (
|
||||
<Fragment>
|
||||
<Greetings>{'Hi,'}</Greetings>
|
||||
<Label capitalize={true} text={username} weight="bold" />
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* render popover menu
|
||||
*/
|
||||
private renderMenu = () => {
|
||||
const { onLogout } = this.props;
|
||||
const { anchorEl } = this.state;
|
||||
const open = Boolean(anchorEl);
|
||||
return (
|
||||
<>
|
||||
<IconButton color="inherit" id="header--button-account" onClick={this.handleLoggedInMenu}>
|
||||
<AccountCircle />
|
||||
</IconButton>
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
id="sidebar-menu"
|
||||
onClose={this.handleLoggedInMenuClose}
|
||||
open={open}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}>
|
||||
<MenuItem disabled={true}>{this.renderGreetings()}</MenuItem>
|
||||
<MenuItem id="header--button-logout" onClick={onLogout}>
|
||||
{'Logout'}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
private renderInfoDialog = () => {
|
||||
const { scope } = this.props;
|
||||
const { openInfoDialog, registryUrl } = this.state;
|
||||
return (
|
||||
<RegistryInfoDialog onClose={this.handleCloseRegistryInfoDialog} open={openInfoDialog}>
|
||||
<RegistryInfoContent registryUrl={registryUrl} scope={scope} />
|
||||
</RegistryInfoDialog>
|
||||
);
|
||||
};
|
||||
}
|
||||
</InnerMobileNavBar>
|
||||
<Button color="inherit">{'Cancel'}</Button>
|
||||
</MobileNavBar>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
|
18
src/components/Header/HeaderGreetings.tsx
Normal file
18
src/components/Header/HeaderGreetings.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
|
||||
import Label from '../Label';
|
||||
|
||||
import { Greetings } from './styles';
|
||||
|
||||
interface Props {
|
||||
username: string;
|
||||
}
|
||||
|
||||
const HeaderGreetings: React.FC<Props> = ({ username }) => (
|
||||
<>
|
||||
<Greetings>{'Hi,'}</Greetings>
|
||||
<Label capitalize={true} text={username} weight="bold" />
|
||||
</>
|
||||
);
|
||||
|
||||
export default HeaderGreetings;
|
19
src/components/Header/HeaderInfoDialog.tsx
Normal file
19
src/components/Header/HeaderInfoDialog.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
|
||||
import RegistryInfoDialog from '../RegistryInfoDialog';
|
||||
import RegistryInfoContent from '../RegistryInfoContent';
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean;
|
||||
onCloseDialog: () => void;
|
||||
registryUrl: string;
|
||||
scope: string;
|
||||
}
|
||||
|
||||
const HeaderInfoDialog: React.FC<Props> = ({ onCloseDialog, isOpen, registryUrl, scope }) => (
|
||||
<RegistryInfoDialog onClose={onCloseDialog} open={isOpen}>
|
||||
<RegistryInfoContent registryUrl={registryUrl} scope={scope} />
|
||||
</RegistryInfoDialog>
|
||||
);
|
||||
|
||||
export default HeaderInfoDialog;
|
32
src/components/Header/HeaderLeft.tsx
Normal file
32
src/components/Header/HeaderLeft.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import { css } from 'emotion';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import Search from '../Search/';
|
||||
|
||||
import HeaderLogo from './HeaderLogo';
|
||||
import { LeftSide, SearchWrapper } from './styles';
|
||||
|
||||
interface Props {
|
||||
withoutSearch?: boolean;
|
||||
logo?: string;
|
||||
}
|
||||
|
||||
const HeaderLeft: React.FC<Props> = ({ withoutSearch = false, logo }) => (
|
||||
<LeftSide>
|
||||
<Link
|
||||
className={css`
|
||||
margin-right: 1em;
|
||||
`}
|
||||
to={'/'}>
|
||||
<HeaderLogo logo={logo} />
|
||||
</Link>
|
||||
{!withoutSearch && (
|
||||
<SearchWrapper>
|
||||
<Search />
|
||||
</SearchWrapper>
|
||||
)}
|
||||
</LeftSide>
|
||||
);
|
||||
|
||||
export default HeaderLeft;
|
17
src/components/Header/HeaderLogo.tsx
Normal file
17
src/components/Header/HeaderLogo.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
|
||||
import Logo from '../Logo';
|
||||
|
||||
interface Props {
|
||||
logo?: string;
|
||||
}
|
||||
|
||||
const HeaderLogo: React.FC<Props> = ({ logo }) => {
|
||||
if (logo) {
|
||||
return <img alt="logo" height="40px" src={logo} />;
|
||||
}
|
||||
|
||||
return <Logo />;
|
||||
};
|
||||
|
||||
export default HeaderLogo;
|
47
src/components/Header/HeaderMenu.tsx
Normal file
47
src/components/Header/HeaderMenu.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
import React, { MouseEvent } from 'react';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import Menu from '@material-ui/core/Menu';
|
||||
import AccountCircle from '@material-ui/icons/AccountCircle';
|
||||
|
||||
import HeaderGreetings from './HeaderGreetings';
|
||||
|
||||
interface Props {
|
||||
username: string;
|
||||
isMenuOpen: boolean;
|
||||
anchorEl?: Element | ((element: Element) => Element) | null | undefined;
|
||||
onLogout: () => void;
|
||||
onLoggedInMenu: (event: MouseEvent<HTMLButtonElement>) => void;
|
||||
onLoggedInMenuClose: () => void;
|
||||
}
|
||||
|
||||
/* eslint-disable react/jsx-max-depth */
|
||||
const HeaderMenu: React.FC<Props> = ({ onLogout, username, isMenuOpen = false, anchorEl, onLoggedInMenu, onLoggedInMenuClose }) => (
|
||||
<>
|
||||
<IconButton color="inherit" data-testid="header--menu-acountcircle" id="header--button-account" onClick={onLoggedInMenu}>
|
||||
<AccountCircle />
|
||||
</IconButton>
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
id="header--button-account"
|
||||
onClose={onLoggedInMenuClose}
|
||||
open={isMenuOpen}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}>
|
||||
<MenuItem disabled={true}>
|
||||
<HeaderGreetings username={username} />
|
||||
</MenuItem>
|
||||
<MenuItem id="header--button-logout" onClick={onLogout}>
|
||||
{'Logout'}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
|
||||
export default HeaderMenu;
|
70
src/components/Header/HeaderRight.tsx
Normal file
70
src/components/Header/HeaderRight.tsx
Normal file
@ -0,0 +1,70 @@
|
||||
import React, { useState, useEffect, MouseEvent } from 'react';
|
||||
import Button from '@material-ui/core/Button';
|
||||
|
||||
import { RightSide } from './styles';
|
||||
import HeaderToolTip from './HeaderToolTip';
|
||||
import HeaderMenu from './HeaderMenu';
|
||||
|
||||
interface Props {
|
||||
withoutSearch?: boolean;
|
||||
username?: string;
|
||||
onToggleLogin: () => void;
|
||||
onOpenRegistryInfoDialog: () => void;
|
||||
onToggleMobileNav: () => void;
|
||||
onLogout: () => void;
|
||||
}
|
||||
|
||||
const HeaderRight: React.FC<Props> = ({ withoutSearch = false, username, onToggleLogin, onLogout, onToggleMobileNav, onOpenRegistryInfoDialog }) => {
|
||||
const [anchorEl, setAnchorEl] = useState();
|
||||
const [isMenuOpen, setIsMenuOpen] = useState();
|
||||
|
||||
useEffect(() => {
|
||||
setIsMenuOpen(Boolean(anchorEl));
|
||||
}, [anchorEl]);
|
||||
|
||||
/**
|
||||
* opens popover menu for logged in user.
|
||||
*/
|
||||
const handleLoggedInMenu = (event: MouseEvent<HTMLButtonElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
/**
|
||||
* closes popover menu for logged in user
|
||||
*/
|
||||
const handleLoggedInMenuClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
/**
|
||||
* close/open popover menu for logged in users.
|
||||
*/
|
||||
const handleToggleLogin = () => {
|
||||
setAnchorEl(null);
|
||||
onToggleLogin();
|
||||
};
|
||||
|
||||
return (
|
||||
<RightSide>
|
||||
{!withoutSearch && <HeaderToolTip onClick={onToggleMobileNav} title={'Search packages'} tooltipIconType={'search'} />}
|
||||
<HeaderToolTip title={'Documentation'} tooltipIconType={'help'} />
|
||||
<HeaderToolTip onClick={onOpenRegistryInfoDialog} title={'Registry Information'} tooltipIconType={'info'} />
|
||||
{username ? (
|
||||
<HeaderMenu
|
||||
anchorEl={anchorEl}
|
||||
isMenuOpen={isMenuOpen}
|
||||
onLoggedInMenu={handleLoggedInMenu}
|
||||
onLoggedInMenuClose={handleLoggedInMenuClose}
|
||||
onLogout={onLogout}
|
||||
username={username}
|
||||
/>
|
||||
) : (
|
||||
<Button color="inherit" data-testid="header--button-login" onClick={handleToggleLogin}>
|
||||
{'Login'}
|
||||
</Button>
|
||||
)}
|
||||
</RightSide>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeaderRight;
|
18
src/components/Header/HeaderToolTip.tsx
Normal file
18
src/components/Header/HeaderToolTip.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import Tooltip from '@material-ui/core/Tooltip';
|
||||
|
||||
import HeaderToolTipIcon, { TooltipIconType } from './HeaderToolTipIcon';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
tooltipIconType: TooltipIconType;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
const HeaderToolTip: React.FC<Props> = ({ tooltipIconType, title, onClick }) => (
|
||||
<Tooltip disableFocusListener={true} title={title}>
|
||||
<HeaderToolTipIcon onClick={onClick} tooltipIconType={tooltipIconType} />
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
export default HeaderToolTip;
|
43
src/components/Header/HeaderToolTipIcon.tsx
Normal file
43
src/components/Header/HeaderToolTipIcon.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import Info from '@material-ui/icons/Info';
|
||||
import Help from '@material-ui/icons/Help';
|
||||
import Search from '@material-ui/icons/Search';
|
||||
|
||||
import { IconSearchButton, StyledExternalLink } from './styles';
|
||||
|
||||
export type TooltipIconType = 'search' | 'help' | 'info';
|
||||
|
||||
interface Props {
|
||||
tooltipIconType: TooltipIconType;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
const HeaderToolTipIcon: React.FC<Props> = ({ tooltipIconType, onClick }) => {
|
||||
switch (tooltipIconType) {
|
||||
case 'help':
|
||||
return (
|
||||
<StyledExternalLink blank={true} data-testid={'header--tooltip-documentation'} to={'https://verdaccio.org/docs/en/installation'}>
|
||||
<IconButton color={'inherit'}>
|
||||
<Help />
|
||||
</IconButton>
|
||||
</StyledExternalLink>
|
||||
);
|
||||
case 'info':
|
||||
return (
|
||||
<IconButton color="inherit" data-testid={'header--tooltip-info'} id="header--button-registryInfo" onClick={onClick}>
|
||||
<Info />
|
||||
</IconButton>
|
||||
);
|
||||
case 'search':
|
||||
return (
|
||||
<IconSearchButton color="inherit" onClick={onClick}>
|
||||
<Search />
|
||||
</IconSearchButton>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export default HeaderToolTipIcon;
|
@ -1,5 +1,366 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<Header /> component with logged in state should load the component in logged in state 1`] = `"<div><header class=\\"MuiPaper-root MuiPaper-elevation4 MuiAppBar-root MuiAppBar-positionStatic css-rfunvc e1jf5lit8 MuiAppBar-colorPrimary\\"><div class=\\"MuiToolbar-root MuiToolbar-regular css-1pwdmmq e1jf5lit0 MuiToolbar-gutters\\"><div class=\\"MuiToolbar-root MuiToolbar-regular css-1vacr9s e1jf5lit3 MuiToolbar-gutters\\"><a class=\\"css-1dk30lc\\" href=\\"/\\"><div class=\\"css-1sifsqk em793ed0\\"></div></a></div><div class=\\"MuiToolbar-root MuiToolbar-regular css-m61s5i e1jf5lit2 MuiToolbar-gutters\\"><a href=\\"https://verdaccio.org/docs/en/installation\\" target=\\"_blank\\" title=\\"Documentation\\" class=\\"css-1aacqdd e1jf5lit9\\"><button class=\\"MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit\\" tabindex=\\"0\\" type=\\"button\\"><span class=\\"MuiIconButton-label\\"><svg class=\\"MuiSvgIcon-root\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z\\"></path></svg></span></button></a><button class=\\"MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit\\" tabindex=\\"0\\" type=\\"button\\" id=\\"header--button-registryInfo\\" title=\\"Registry Information\\"><span class=\\"MuiIconButton-label\\"><svg class=\\"MuiSvgIcon-root\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\\"></path></svg></span></button><button class=\\"MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit\\" tabindex=\\"0\\" type=\\"button\\" id=\\"header--button-account\\"><span class=\\"MuiIconButton-label\\"><svg class=\\"MuiSvgIcon-root\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z\\"></path></svg></span></button></div></div></header></div>"`;
|
||||
exports[`<Header /> component with logged in state should load the component in logged in state 1`] = `
|
||||
<header
|
||||
class="MuiPaper-root MuiPaper-elevation4 MuiAppBar-root MuiAppBar-positionStatic css-rfunvc emotion-9 MuiAppBar-colorPrimary"
|
||||
>
|
||||
<div
|
||||
class="MuiToolbar-root MuiToolbar-regular css-1pwdmmq emotion-8 MuiToolbar-gutters"
|
||||
>
|
||||
<div
|
||||
class="MuiToolbar-root MuiToolbar-regular css-1vacr9s emotion-4 MuiToolbar-gutters"
|
||||
>
|
||||
<a
|
||||
class="css-1dk30lc"
|
||||
href="/"
|
||||
>
|
||||
<div
|
||||
class="css-1sifsqk emotion-0"
|
||||
/>
|
||||
</a>
|
||||
<div
|
||||
class="css-13zpdre emotion-3"
|
||||
>
|
||||
<div
|
||||
class="css-1crzyyo emotion-2"
|
||||
>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="react-autowhatever-1"
|
||||
class="react-autosuggest__container"
|
||||
role="combobox"
|
||||
>
|
||||
<div
|
||||
aria-autocomplete="list"
|
||||
aria-controls="react-autowhatever-1"
|
||||
class="MuiFormControl-root MuiTextField-root react-autosuggest__input MuiFormControl-fullWidth"
|
||||
>
|
||||
<div
|
||||
class="MuiInputBase-root MuiInput-root css-n9ojyg MuiInput-underline MuiInputBase-fullWidth MuiInput-fullWidth MuiInputBase-formControl MuiInput-formControl MuiInputBase-adornedStart"
|
||||
>
|
||||
<div
|
||||
class="MuiInputAdornment-root css-16qv2i2 MuiInputAdornment-positionStart"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
aria-invalid="false"
|
||||
autocomplete="off"
|
||||
class="MuiInputBase-input MuiInput-input css-hodoyq MuiInputBase-inputAdornedStart"
|
||||
placeholder="Search Packages"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="MuiPaper-root MuiPaper-elevation1 react-autosuggest__suggestions-container css-cfo6a emotion-1"
|
||||
id="react-autowhatever-1"
|
||||
role="listbox"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="MuiToolbar-root MuiToolbar-regular css-m61s5i emotion-7 MuiToolbar-gutters"
|
||||
>
|
||||
<button
|
||||
class="MuiButtonBase-root MuiIconButton-root css-1y1xi9f emotion-5 MuiIconButton-colorInherit"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="MuiIconButton-label"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="MuiTouchRipple-root"
|
||||
/>
|
||||
</button>
|
||||
<a
|
||||
class="css-1aacqdd emotion-6"
|
||||
data-testid="header--tooltip-documentation"
|
||||
href="https://verdaccio.org/docs/en/installation"
|
||||
target="_blank"
|
||||
>
|
||||
<button
|
||||
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="MuiIconButton-label"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="MuiTouchRipple-root"
|
||||
/>
|
||||
</button>
|
||||
</a>
|
||||
<button
|
||||
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit"
|
||||
data-testid="header--tooltip-info"
|
||||
id="header--button-registryInfo"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="MuiIconButton-label"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="MuiTouchRipple-root"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit"
|
||||
data-testid="header--menu-acountcircle"
|
||||
id="header--button-account"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="MuiIconButton-label"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="MuiTouchRipple-root"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
`;
|
||||
|
||||
exports[`<Header /> component with logged out state should load the component in logged out state 1`] = `"<div><header class=\\"MuiPaper-root MuiPaper-elevation4 MuiAppBar-root MuiAppBar-positionStatic css-rfunvc e1jf5lit8 MuiAppBar-colorPrimary\\"><div class=\\"MuiToolbar-root MuiToolbar-regular css-1pwdmmq e1jf5lit0 MuiToolbar-gutters\\"><div class=\\"MuiToolbar-root MuiToolbar-regular css-1vacr9s e1jf5lit3 MuiToolbar-gutters\\"><a class=\\"css-1dk30lc\\" href=\\"/\\"><div class=\\"css-1sifsqk em793ed0\\"></div></a></div><div class=\\"MuiToolbar-root MuiToolbar-regular css-m61s5i e1jf5lit2 MuiToolbar-gutters\\"><a href=\\"https://verdaccio.org/docs/en/installation\\" target=\\"_blank\\" title=\\"Documentation\\" class=\\"css-1aacqdd e1jf5lit9\\"><button class=\\"MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit\\" tabindex=\\"0\\" type=\\"button\\"><span class=\\"MuiIconButton-label\\"><svg class=\\"MuiSvgIcon-root\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z\\"></path></svg></span></button></a><button class=\\"MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit\\" tabindex=\\"0\\" type=\\"button\\" id=\\"header--button-registryInfo\\" title=\\"Registry Information\\"><span class=\\"MuiIconButton-label\\"><svg class=\\"MuiSvgIcon-root\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\\"></path></svg></span></button><button class=\\"MuiButtonBase-root MuiButton-root MuiButton-text MuiButton-colorInherit\\" tabindex=\\"0\\" type=\\"button\\" id=\\"header--button-login\\"><span class=\\"MuiButton-label\\">Login</span></button></div></div></header></div>"`;
|
||||
exports[`<Header /> component with logged in state should load the component in logged out state 1`] = `
|
||||
<header
|
||||
class="MuiPaper-root MuiPaper-elevation4 MuiAppBar-root MuiAppBar-positionStatic css-rfunvc emotion-9 MuiAppBar-colorPrimary"
|
||||
>
|
||||
<div
|
||||
class="MuiToolbar-root MuiToolbar-regular css-1pwdmmq emotion-8 MuiToolbar-gutters"
|
||||
>
|
||||
<div
|
||||
class="MuiToolbar-root MuiToolbar-regular css-1vacr9s emotion-4 MuiToolbar-gutters"
|
||||
>
|
||||
<a
|
||||
class="css-1dk30lc"
|
||||
href="/"
|
||||
>
|
||||
<div
|
||||
class="css-1sifsqk emotion-0"
|
||||
/>
|
||||
</a>
|
||||
<div
|
||||
class="css-13zpdre emotion-3"
|
||||
>
|
||||
<div
|
||||
class="css-1crzyyo emotion-2"
|
||||
>
|
||||
<div
|
||||
aria-expanded="false"
|
||||
aria-haspopup="listbox"
|
||||
aria-owns="react-autowhatever-1"
|
||||
class="react-autosuggest__container"
|
||||
role="combobox"
|
||||
>
|
||||
<div
|
||||
aria-autocomplete="list"
|
||||
aria-controls="react-autowhatever-1"
|
||||
class="MuiFormControl-root MuiTextField-root react-autosuggest__input MuiFormControl-fullWidth"
|
||||
>
|
||||
<div
|
||||
class="MuiInputBase-root MuiInput-root css-n9ojyg MuiInput-underline MuiInputBase-fullWidth MuiInput-fullWidth MuiInputBase-formControl MuiInput-formControl MuiInputBase-adornedStart"
|
||||
>
|
||||
<div
|
||||
class="MuiInputAdornment-root css-16qv2i2 MuiInputAdornment-positionStart"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<input
|
||||
aria-invalid="false"
|
||||
autocomplete="off"
|
||||
class="MuiInputBase-input MuiInput-input css-hodoyq MuiInputBase-inputAdornedStart"
|
||||
placeholder="Search Packages"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="MuiPaper-root MuiPaper-elevation1 react-autosuggest__suggestions-container css-cfo6a emotion-1"
|
||||
id="react-autowhatever-1"
|
||||
role="listbox"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="MuiToolbar-root MuiToolbar-regular css-m61s5i emotion-7 MuiToolbar-gutters"
|
||||
>
|
||||
<button
|
||||
class="MuiButtonBase-root MuiIconButton-root css-1y1xi9f emotion-5 MuiIconButton-colorInherit"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="MuiIconButton-label"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="MuiTouchRipple-root"
|
||||
/>
|
||||
</button>
|
||||
<a
|
||||
class="css-1aacqdd emotion-6"
|
||||
data-testid="header--tooltip-documentation"
|
||||
href="https://verdaccio.org/docs/en/installation"
|
||||
target="_blank"
|
||||
>
|
||||
<button
|
||||
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="MuiIconButton-label"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="MuiTouchRipple-root"
|
||||
/>
|
||||
</button>
|
||||
</a>
|
||||
<button
|
||||
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit"
|
||||
data-testid="header--tooltip-info"
|
||||
id="header--button-registryInfo"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="MuiIconButton-label"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root"
|
||||
focusable="false"
|
||||
role="presentation"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="MuiTouchRipple-root"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
class="MuiButtonBase-root MuiButton-root MuiButton-text MuiButton-colorInherit"
|
||||
data-testid="header--button-login"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="MuiButton-label"
|
||||
>
|
||||
Login
|
||||
</span>
|
||||
<span
|
||||
class="MuiTouchRipple-root"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
`;
|
||||
|
@ -10,7 +10,7 @@ import { Props } from './types';
|
||||
const LABEL = 'CLOSE';
|
||||
|
||||
const RegistryInfoDialog: React.FC<Props> = ({ open = false, children, onClose }) => (
|
||||
<Dialog id="registryInfo--dialog-container" onClose={onClose} open={open}>
|
||||
<Dialog data-testid={'registryInfo--dialog'} id="registryInfo--dialog-container" onClose={onClose} open={open}>
|
||||
<Title disableTypography={true}>{'Register Info'}</Title>
|
||||
<Content>{children}</Content>
|
||||
<DialogActions>
|
||||
|
Loading…
Reference in New Issue
Block a user