mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-28 12:43:11 +10:00
commit
2b99ab4407
22 changed files with 568 additions and 48 deletions
|
|
@ -84,7 +84,7 @@
|
|||
border-width: 0px;
|
||||
|
||||
/* Font configuration */
|
||||
text-decoration: underline;
|
||||
text-decoration: none;
|
||||
|
||||
/* Hovers */
|
||||
&:enabled {
|
||||
|
|
@ -93,6 +93,7 @@
|
|||
&:enabled:hover,
|
||||
&:enabled:active {
|
||||
background-color: transparent;
|
||||
text-decoration: underline;
|
||||
}
|
||||
&:disabled {
|
||||
background-color: transparent;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,5 @@
|
|||
.container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 50px;
|
||||
border-top: solid 1px #f7f7f7;
|
||||
}
|
||||
.container:hover {
|
||||
background-color: #f7f7f7;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.openIndicator {
|
||||
margin-left: auto;
|
||||
margin-right: 1em;
|
||||
.root {
|
||||
width: auto;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
|
|
|||
49
src/components/Menu/Menu.example.js
Normal file
49
src/components/Menu/Menu.example.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import React from 'react';
|
||||
import { InlineButton, MenuContent, MenuLabel, MenuItem } from '../../components';
|
||||
import Menu from './Menu';
|
||||
|
||||
const noop = () => null;
|
||||
const style = { padding: '24px' };
|
||||
const btnStyle = { whiteSpace: 'nowrap' };
|
||||
|
||||
const MenuWrapper = () => {
|
||||
return (
|
||||
<Menu>
|
||||
<MenuLabel>
|
||||
<span>Menu</span>
|
||||
</MenuLabel>
|
||||
<MenuContent style={style}>
|
||||
<MenuItem key="first item">
|
||||
<InlineButton onClick={noop} style={btnStyle}>Click this</InlineButton>
|
||||
</MenuItem>
|
||||
<MenuItem key="second item">
|
||||
<InlineButton onClick={noop} style={btnStyle}>Click this</InlineButton>
|
||||
</MenuItem>
|
||||
</MenuContent>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
||||
const MenuOnLeft = () => {
|
||||
return <div style={{ width: '50px' }}><MenuWrapper /></div>;
|
||||
};
|
||||
|
||||
export const MenuBasic = {
|
||||
component: MenuOnLeft,
|
||||
props: {},
|
||||
group: 'navigation',
|
||||
};
|
||||
|
||||
const MenuOnRight = () => {
|
||||
return (
|
||||
<div style={{ width: '50px', marginLeft: 'auto', marginRight: '36px' }}>
|
||||
<MenuWrapper />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const MenuBasicOnRight = {
|
||||
component: MenuOnRight,
|
||||
props: {},
|
||||
group: 'navigation',
|
||||
};
|
||||
|
|
@ -1,10 +1,152 @@
|
|||
import React from 'react';
|
||||
/**
|
||||
* Menu is component that shows extra content when it is clicked.
|
||||
* Clicking it toggles visibility of MenuContent.
|
||||
*
|
||||
* Example:
|
||||
* <Menu>
|
||||
* <MenuLabel>
|
||||
* <span>Open menu</span>
|
||||
* </MenuLabel>
|
||||
* <MenuContent>
|
||||
* <MenuItem key="first item">
|
||||
* <button onClick={onClick}>Click this</button>
|
||||
* </MenuItem>
|
||||
* </MenuContent>
|
||||
* </Menu>
|
||||
*
|
||||
*/
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { MenuContent, MenuLabel } from '../../components';
|
||||
import css from './Menu.css';
|
||||
|
||||
const Menu = () => (
|
||||
<div className={css.container}>
|
||||
New York, Jan 2nd – Jan 4th <span className={css.openIndicator}>▼</span>
|
||||
</div>
|
||||
);
|
||||
const KEY_CODE_ESCAPE = 27;
|
||||
const CONTENT_PLACEMENT_OFFSET = 22;
|
||||
|
||||
// This should work, but it doesn't <div className="foo" onClick={() => {}} role="button" />
|
||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||
class Menu extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = { isOpen: false };
|
||||
|
||||
this.onBlur = this.onBlur.bind(this);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
this.toggleOpen = this.toggleOpen.bind(this);
|
||||
this.prepareChildren = this.prepareChildren.bind(this);
|
||||
this.positionStyleForMenuContent = this.positionStyleForMenuContent.bind(this);
|
||||
this.positionStyleForArrow = this.positionStyleForArrow.bind(this);
|
||||
|
||||
this.menu = null;
|
||||
this.menuContent = null;
|
||||
}
|
||||
|
||||
onBlur(event) {
|
||||
// FocusEvent is fired faster than the link elements native click handler
|
||||
// gets its own event. Therefore, we need to check the origin of this FocusEvent.
|
||||
if (!this.menu.contains(event.relatedTarget)) {
|
||||
this.setState({ isOpen: false });
|
||||
}
|
||||
}
|
||||
|
||||
onKeyDown(e) {
|
||||
// Gather all escape presses to close menu
|
||||
if (e.keyCode === KEY_CODE_ESCAPE) {
|
||||
this.toggleOpen(false);
|
||||
}
|
||||
}
|
||||
|
||||
toggleOpen(enforcedState) {
|
||||
this.setState(prevState => {
|
||||
const isOpen = enforcedState != null ? enforcedState : !prevState.isOpen;
|
||||
return { isOpen };
|
||||
});
|
||||
}
|
||||
|
||||
positionStyleForMenuContent() {
|
||||
if (this.menu && this.menuContent) {
|
||||
// Calculate wether we should show the menu to the left of the component or right
|
||||
const distanceToRight = window.innerWidth - this.menu.getBoundingClientRect().right;
|
||||
const menuWidth = this.menu.offsetWidth;
|
||||
const contentWidthBiggerThanLabel = this.menuContent.offsetWidth - menuWidth;
|
||||
return distanceToRight < contentWidthBiggerThanLabel
|
||||
? { right: -1 * CONTENT_PLACEMENT_OFFSET, minWidth: menuWidth }
|
||||
: { left: 0, minWidth: menuWidth };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
positionStyleForArrow(isPositionedRight) {
|
||||
if (this.menu) {
|
||||
const menuWidth = this.menu.offsetWidth;
|
||||
return isPositionedRight
|
||||
? Math.floor(menuWidth / 2) + CONTENT_PLACEMENT_OFFSET
|
||||
: Math.floor(menuWidth / 2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
prepareChildren() {
|
||||
if (React.Children.count(this.props.children) !== 2) {
|
||||
throw new Error('Menu needs to have two children: MenuLabel and MenuContent.');
|
||||
}
|
||||
|
||||
return React.Children.map(this.props.children, child => {
|
||||
if (child.type === MenuLabel) {
|
||||
// MenuLabel needs toggleOpen function
|
||||
// We pass that directly so that component user doesn't need to worry about that
|
||||
return React.cloneElement(child, { onToggleActive: this.toggleOpen });
|
||||
} else if (child.type === MenuContent) {
|
||||
// MenuContent needs some styling data (width, arrowPosition, and isOpen info)
|
||||
// We pass those directly so that component user doesn't need to worry about those.
|
||||
const positionStyles = this.positionStyleForMenuContent();
|
||||
return React.cloneElement(child, {
|
||||
arrowPosition: this.positionStyleForArrow(positionStyles.right != null),
|
||||
contentRef: node => {
|
||||
this.menuContent = node;
|
||||
},
|
||||
isOpen: this.state.isOpen,
|
||||
style: { ...child.props.style, ...positionStyles },
|
||||
});
|
||||
} else {
|
||||
throw new Error('Menu has an unknown child. Only MenuLabel and MenuContent are allowed.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, rootClassName } = this.props;
|
||||
const rootClass = rootClassName || css.root;
|
||||
const classes = classNames(rootClass, className);
|
||||
const menuChildren = this.prepareChildren();
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes}
|
||||
onBlur={this.onBlur}
|
||||
onKeyDown={this.onKeyDown}
|
||||
ref={c => {
|
||||
this.menu = c;
|
||||
}}
|
||||
>
|
||||
{menuChildren}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
/* eslint-enable jsx-a11y/no-static-element-interactions */
|
||||
|
||||
Menu.defaultProps = { className: null, rootClassName: '' };
|
||||
|
||||
const { node, string } = PropTypes;
|
||||
|
||||
Menu.propTypes = {
|
||||
children: node.isRequired,
|
||||
className: string,
|
||||
rootClassName: string,
|
||||
};
|
||||
|
||||
export default Menu;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,18 @@
|
|||
import React from 'react';
|
||||
import { renderDeep } from '../../util/test-helpers';
|
||||
import { MenuItem, MenuLabel, MenuContent } from '../../components';
|
||||
import Menu from './Menu';
|
||||
|
||||
describe('Menu', () => {
|
||||
it('matches snapshot', () => {
|
||||
const tree = renderDeep(<Menu />);
|
||||
const tree = renderDeep(
|
||||
<Menu>
|
||||
<MenuLabel>Label</MenuLabel>
|
||||
<MenuContent>
|
||||
<MenuItem key="1">Menu item 1</MenuItem><MenuItem key="2">Menu item 2</MenuItem>
|
||||
</MenuContent>
|
||||
</Menu>
|
||||
);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,10 +1,29 @@
|
|||
exports[`Menu matches snapshot 1`] = `
|
||||
<div
|
||||
className={undefined}>
|
||||
New York, Jan 2nd – Jan 4th
|
||||
<span
|
||||
className={undefined}>
|
||||
▼
|
||||
</span>
|
||||
className=""
|
||||
onBlur={[Function]}
|
||||
onKeyDown={[Function]}>
|
||||
<button
|
||||
className=""
|
||||
onClick={[Function]}>
|
||||
Label
|
||||
</button>
|
||||
<div
|
||||
className=""
|
||||
style={Object {}}>
|
||||
<ul
|
||||
className="">
|
||||
<li
|
||||
className=""
|
||||
role="menuitem">
|
||||
Menu item 1
|
||||
</li>
|
||||
<li
|
||||
className=""
|
||||
role="menuitem">
|
||||
Menu item 2
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
48
src/components/MenuContent/MenuContent.css
Normal file
48
src/components/MenuContent/MenuContent.css
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
@import '../../marketplace.css';
|
||||
|
||||
.root {
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
|
||||
position: absolute;
|
||||
z-index: var(--zIndexPopup);
|
||||
|
||||
background-color: var(--matterColorLight);
|
||||
border: 1px solid var(--matterColorAnti);
|
||||
box-shadow: var(--boxShadowPopup);
|
||||
}
|
||||
|
||||
.isOpen {
|
||||
visibility: visible;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Styles for arrow (if arrowPosition is defined) */
|
||||
.arrowTop,
|
||||
.arrowBelow {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
height: 0;
|
||||
width: 0;
|
||||
border: solid transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.arrowTop {
|
||||
border-bottom-color: var(--matterColorLight);
|
||||
border-width: 7px;
|
||||
margin-left: -7px;
|
||||
}
|
||||
|
||||
.arrowBelow {
|
||||
border-bottom-color: var(--matterColorAnti);
|
||||
border-width: 9px;
|
||||
margin-left: -9px;
|
||||
}
|
||||
80
src/components/MenuContent/MenuContent.js
Normal file
80
src/components/MenuContent/MenuContent.js
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* MenuContent is a immediate child of Menu component sibling to MenuLabel.
|
||||
* Clicking MenuLabel toggles visibility of MenuContent.
|
||||
*/
|
||||
import React, { PropTypes } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { MenuItem } from '../../components';
|
||||
|
||||
import css from './MenuContent.css';
|
||||
|
||||
const MenuContent = props => {
|
||||
const {
|
||||
arrowPosition,
|
||||
children,
|
||||
className,
|
||||
contentClassName,
|
||||
contentRef,
|
||||
isOpen,
|
||||
rootClassName,
|
||||
style,
|
||||
} = props;
|
||||
|
||||
const rootClass = rootClassName || css.root;
|
||||
const classes = classNames(rootClass, className, { [css.isOpen]: isOpen });
|
||||
const contentClasses = classNames(contentClassName, css.content);
|
||||
|
||||
const arrowPositionStyle = arrowPosition && style.right != null
|
||||
? { position: 'absolute', right: arrowPosition, top: 0 }
|
||||
: { position: 'absolute', left: arrowPosition, top: 0 };
|
||||
|
||||
const arrow = arrowPosition
|
||||
? <div style={arrowPositionStyle}>
|
||||
<div className={css.arrowBelow} />
|
||||
<div className={css.arrowTop} />
|
||||
</div>
|
||||
: null;
|
||||
|
||||
React.Children.forEach(children, child => {
|
||||
if (child.type !== MenuItem) {
|
||||
throw new Error('All children of MenuContent must be MenuItems.');
|
||||
}
|
||||
if (child.key == null) {
|
||||
throw new Error('All children of MenuContent must have a "key" prop.');
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={classes} ref={contentRef} style={style}>
|
||||
{arrow}
|
||||
<ul className={contentClasses}>
|
||||
{children}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
MenuContent.defaultProps = {
|
||||
arrowPosition: null,
|
||||
className: null,
|
||||
contentClassName: null,
|
||||
contentRef: null,
|
||||
isOpen: false,
|
||||
rootClassName: '',
|
||||
style: null,
|
||||
};
|
||||
|
||||
const { bool, func, node, number, object, string } = PropTypes;
|
||||
|
||||
MenuContent.propTypes = {
|
||||
arrowPosition: number,
|
||||
children: node.isRequired,
|
||||
className: string,
|
||||
contentClassName: string,
|
||||
contentRef: func,
|
||||
isOpen: bool,
|
||||
rootClassName: string,
|
||||
style: object,
|
||||
};
|
||||
|
||||
export default MenuContent;
|
||||
4
src/components/MenuItem/MenuItem.css
Normal file
4
src/components/MenuItem/MenuItem.css
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
.root {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
35
src/components/MenuItem/MenuItem.js
Normal file
35
src/components/MenuItem/MenuItem.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* MenuItem is part of Menu and specifically a child of MenuContent.
|
||||
* MenuItems should have a 'key' prop specified.
|
||||
* https://facebook.github.io/react/docs/lists-and-keys.html#keys
|
||||
*
|
||||
* Example:
|
||||
* <MenuItem key="item 1"><a href="example.com">Click me</a><MenuItem>
|
||||
*/
|
||||
import React, { PropTypes } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import css from './MenuItem.css';
|
||||
|
||||
const MenuItem = props => {
|
||||
const { children, className, rootClassName } = props;
|
||||
const rootClass = rootClassName || css.root;
|
||||
const classes = classNames(rootClass, className);
|
||||
|
||||
return <li className={classes} role="menuitem">{children}</li>;
|
||||
};
|
||||
|
||||
MenuItem.defaultProps = {
|
||||
className: null,
|
||||
rootClassName: '',
|
||||
};
|
||||
|
||||
const { node, string } = PropTypes;
|
||||
|
||||
MenuItem.propTypes = {
|
||||
children: node.isRequired,
|
||||
className: string,
|
||||
rootClassName: string,
|
||||
};
|
||||
|
||||
export default MenuItem;
|
||||
6
src/components/MenuLabel/MenuLabel.css
Normal file
6
src/components/MenuLabel/MenuLabel.css
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.root {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
46
src/components/MenuLabel/MenuLabel.js
Normal file
46
src/components/MenuLabel/MenuLabel.js
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* MenuLabel is the only always visible part of Menu.
|
||||
* Clicking it toggles visibility of MenuContent.
|
||||
*/
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import css from './MenuLabel.css';
|
||||
|
||||
class MenuLabel extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onClick = this.onClick.bind(this);
|
||||
}
|
||||
|
||||
onClick() {
|
||||
this.props.onToggleActive();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { children, className, rootClassName } = this.props;
|
||||
|
||||
const rootClass = rootClassName || css.root;
|
||||
const classes = classNames(rootClass, className);
|
||||
|
||||
return (
|
||||
<button className={classes} onClick={this.onClick}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
/* eslint-enable jsx-a11y/no-static-element-interactions */
|
||||
|
||||
MenuLabel.defaultProps = { className: null, onToggleActive: null, rootClassName: '' };
|
||||
|
||||
const { func, node, string } = PropTypes;
|
||||
|
||||
MenuLabel.propTypes = {
|
||||
children: node.isRequired,
|
||||
className: string,
|
||||
onToggleActive: func,
|
||||
rootClassName: string,
|
||||
};
|
||||
|
||||
export default MenuLabel;
|
||||
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
/* logo */
|
||||
.logoLink {
|
||||
align-self: flex-start;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -92,14 +93,12 @@
|
|||
}
|
||||
|
||||
/* Profile menu */
|
||||
/* TODO This is placeholder, it needs Menu component */
|
||||
.avatarLink {
|
||||
.profileMenuLabel {
|
||||
align-self: flex-start;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
|
|
@ -107,6 +106,10 @@
|
|||
height: 41px;
|
||||
}
|
||||
|
||||
.profileMenuContent {
|
||||
min-width: 276px;
|
||||
}
|
||||
|
||||
/* Authentication */
|
||||
.signupLink {
|
||||
align-self: flex-start;
|
||||
|
|
@ -125,3 +128,16 @@
|
|||
composes: bodyFont from '../../marketplace.css';
|
||||
color: var(--matterColor);
|
||||
}
|
||||
|
||||
.logoutButton {
|
||||
width: 100%;
|
||||
padding: 20px 24px;
|
||||
composes: bodyFont from '../../marketplace.css';
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { fakeIntl } from '../../util/test-data';
|
||||
import TopbarDesktop from './TopbarDesktop';
|
||||
|
||||
const noop = () => null;
|
||||
|
||||
export const AuthenticatedDesktopTopbar = {
|
||||
component: TopbarDesktop,
|
||||
props: {
|
||||
|
|
@ -8,6 +10,7 @@ export const AuthenticatedDesktopTopbar = {
|
|||
currentUserHasListings: true,
|
||||
name: 'John Doe',
|
||||
intl: fakeIntl,
|
||||
onLogout: noop,
|
||||
},
|
||||
group: 'navigation',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,13 +1,29 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { FormattedMessage, intlShape, injectIntl } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { Avatar, NamedLink } from '../../components';
|
||||
import {
|
||||
Avatar,
|
||||
InlineButton,
|
||||
Menu,
|
||||
MenuLabel,
|
||||
MenuContent,
|
||||
MenuItem,
|
||||
NamedLink,
|
||||
} from '../../components';
|
||||
|
||||
import logo from './images/saunatime-logo.png';
|
||||
import css from './TopbarDesktop.css';
|
||||
|
||||
const TopbarDesktop = props => {
|
||||
const { className, rootClassName, currentUserHasListings, intl, isAuthenticated, name } = props;
|
||||
const {
|
||||
className,
|
||||
rootClassName,
|
||||
currentUserHasListings,
|
||||
intl,
|
||||
isAuthenticated,
|
||||
name,
|
||||
onLogout,
|
||||
} = props;
|
||||
|
||||
const rootClass = rootClassName || css.root;
|
||||
const classes = classNames(rootClass, className);
|
||||
|
|
@ -29,9 +45,19 @@ const TopbarDesktop = props => {
|
|||
</NamedLink>
|
||||
: null;
|
||||
|
||||
// TODO This is just a placeholder
|
||||
const profileMenu = isAuthenticated
|
||||
? <div className={css.avatarLink}> <Avatar className={css.avatar} name={name} /></div>
|
||||
? <Menu>
|
||||
<MenuLabel className={css.profileMenuLabel}>
|
||||
<Avatar className={css.avatar} name={name} />
|
||||
</MenuLabel>
|
||||
<MenuContent className={css.profileMenuContent}>
|
||||
<MenuItem key="logout">
|
||||
<InlineButton className={css.logoutButton} onClick={onLogout}>
|
||||
<FormattedMessage id="TopbarDesktop.logout" />
|
||||
</InlineButton>
|
||||
</MenuItem>
|
||||
</MenuContent>
|
||||
</Menu>
|
||||
: null;
|
||||
|
||||
const signupLink = isAuthenticated
|
||||
|
|
@ -47,7 +73,7 @@ const TopbarDesktop = props => {
|
|||
</NamedLink>;
|
||||
|
||||
return (
|
||||
<div className={classes}>
|
||||
<nav className={classes}>
|
||||
<NamedLink className={css.logoLink} name="LandingPage">
|
||||
<img
|
||||
className={css.logo}
|
||||
|
|
@ -66,18 +92,19 @@ const TopbarDesktop = props => {
|
|||
{profileMenu}
|
||||
{signupLink}
|
||||
{loginLink}
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
TopbarDesktop.defaultProps = { name: '', className: null, rootClassName: '' };
|
||||
|
||||
const { bool, string } = PropTypes;
|
||||
const { bool, func, string } = PropTypes;
|
||||
|
||||
TopbarDesktop.propTypes = {
|
||||
className: string,
|
||||
currentUserHasListings: bool.isRequired,
|
||||
isAuthenticated: bool.isRequired,
|
||||
onLogout: func.isRequired,
|
||||
name: string,
|
||||
rootClassName: string,
|
||||
|
||||
|
|
|
|||
|
|
@ -6,12 +6,20 @@ import routesConfiguration from '../../routesConfiguration';
|
|||
import { flattenRoutes } from '../../util/routes';
|
||||
import TopbarDesktop from './TopbarDesktop';
|
||||
|
||||
const noop = () => null;
|
||||
|
||||
describe('TopbarDesktop', () => {
|
||||
it('data matches snapshot', () => {
|
||||
const flattenedRoutes = flattenRoutes(routesConfiguration);
|
||||
const tree = renderDeep(
|
||||
<RoutesProvider flattenedRoutes={flattenedRoutes}>
|
||||
<TopbarDesktop isAuthenticated currentUserHasListings name="John Doe" intl={fakeIntl} />
|
||||
<TopbarDesktop
|
||||
isAuthenticated
|
||||
currentUserHasListings
|
||||
name="John Doe"
|
||||
intl={fakeIntl}
|
||||
onLogout={noop}
|
||||
/>
|
||||
</RoutesProvider>
|
||||
);
|
||||
expect(tree).toMatchSnapshot();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
exports[`TopbarDesktop data matches snapshot 1`] = `
|
||||
<div
|
||||
<nav
|
||||
className="">
|
||||
<a
|
||||
className="NamedLink_active"
|
||||
|
|
@ -45,12 +45,35 @@ exports[`TopbarDesktop data matches snapshot 1`] = `
|
|||
</span>
|
||||
</a>
|
||||
<div
|
||||
className={undefined}>
|
||||
|
||||
<img
|
||||
alt="John Doe"
|
||||
className=""
|
||||
onBlur={[Function]}
|
||||
onKeyDown={[Function]}>
|
||||
<button
|
||||
className=""
|
||||
src="wireframeAvatar.svg" />
|
||||
onClick={[Function]}>
|
||||
<img
|
||||
alt="John Doe"
|
||||
className=""
|
||||
src="wireframeAvatar.svg" />
|
||||
</button>
|
||||
<div
|
||||
className=""
|
||||
style={Object {}}>
|
||||
<ul
|
||||
className="">
|
||||
<li
|
||||
className=""
|
||||
role="menuitem">
|
||||
<button
|
||||
className=""
|
||||
onClick={[Function]}>
|
||||
<span>
|
||||
Log out
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ import LocationAutocompleteInput from './LocationAutocompleteInput/LocationAutoc
|
|||
import Map from './Map/Map';
|
||||
import MapPanel from './MapPanel/MapPanel';
|
||||
import Menu from './Menu/Menu';
|
||||
import MenuContent from './MenuContent/MenuContent';
|
||||
import MenuItem from './MenuItem/MenuItem';
|
||||
import MenuLabel from './MenuLabel/MenuLabel';
|
||||
import Modal from './Modal/Modal';
|
||||
import ModalInMobile from './ModalInMobile/ModalInMobile';
|
||||
import NamedLink from './NamedLink/NamedLink';
|
||||
|
|
@ -71,6 +74,9 @@ export {
|
|||
Map,
|
||||
MapPanel,
|
||||
Menu,
|
||||
MenuContent,
|
||||
MenuItem,
|
||||
MenuLabel,
|
||||
Modal,
|
||||
ModalInMobile,
|
||||
NamedLink,
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ class TopbarComponent extends Component {
|
|||
intl={intl}
|
||||
isAuthenticated={isAuthenticated}
|
||||
name={name}
|
||||
onLogout={this.handleLogout}
|
||||
/>
|
||||
</div>
|
||||
<Modal
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import * as DateInput from './components/DateInput/DateInput.example';
|
|||
import * as EditListingWizard from './components/EditListingWizard/EditListingWizard.example';
|
||||
import * as ListingCard from './components/ListingCard/ListingCard.example';
|
||||
import * as Map from './components/Map/Map.example';
|
||||
import * as Menu from './components/Menu/Menu.example';
|
||||
import * as Modal from './components/Modal/Modal.example';
|
||||
import * as ModalInMobile from './components/ModalInMobile/ModalInMobile.example';
|
||||
import * as NamedLink from './components/NamedLink/NamedLink.example';
|
||||
|
|
@ -61,6 +62,7 @@ export {
|
|||
LocationAutocompleteInput,
|
||||
LoginForm,
|
||||
Map,
|
||||
Menu,
|
||||
Modal,
|
||||
ModalInMobile,
|
||||
NamedLink,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,13 @@
|
|||
--boxShadowLight: 0 2px 4px 0 rgba(0, 0, 0, 0.05);
|
||||
--boxShadowPopup: 0 8px 16px 0 rgba(0, 0, 0, 0.3);
|
||||
--boxShadowPopupLight: 0 3px 6px 0 rgba(0, 0, 0, 0.3);
|
||||
|
||||
|
||||
/* z-index base levels */
|
||||
/* small popups on UI should use z-indexes above 50 */
|
||||
--zIndexPopup: 50;
|
||||
/* modals and UI overlays should use z-indexes above 100 */
|
||||
--zIndexModal: 100;
|
||||
}
|
||||
|
||||
/* FONTS */
|
||||
|
|
|
|||
|
|
@ -187,6 +187,7 @@
|
|||
"TopbarDesktop.createListing": "+ Add your sauna",
|
||||
"TopbarDesktop.inbox": "Inbox",
|
||||
"TopbarDesktop.login": "Log in",
|
||||
"TopbarDesktop.logout": "Log out",
|
||||
"TopbarDesktop.logo": "Logo",
|
||||
"TopbarDesktop.signup": "Sign up",
|
||||
"TopbarMobileMenu.inboxLink": "Inbox",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue