Merge pull request #194 from sharetribe/desktop-topbar-draft

Desktop topbar draft
This commit is contained in:
Vesa Luusua 2017-05-30 15:57:30 +03:00 committed by GitHub
commit fbc61acd65
12 changed files with 347 additions and 2 deletions

View file

@ -0,0 +1,127 @@
@import '../../marketplace.css';
/* Desktop */
.root {
/* Size */
width: 100%;
height: 72px;
/* Layout for child components */
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: baseline;
padding: 0 36px;
/* fill */
background-color: var(--matterColorLight);
/* shadows */
box-shadow: var(--boxShadowLight);
}
/* Compose text label items on top of this */
/* This stretches inline element (link) to take available vertical space (big hover area),
* and align baselines, and passes text-decoration hover effect
*/
.topbarDesktopLabel {
display: inline-block;
margin: 24px 0 16px 0;
text-decoration: inherit;
}
/* logo */
.logoLink {
flex-shrink: 0;
display: flex;
align-items: center;
height: 100%;
margin-right: 24px;
}
.logo {
width: 155px;
height: 27px;
}
/* Search */
/* TODO This is placeholder, it needs Search component */
.searchLink {
min-width: 220px;
align-self: flex-start;
height: 100%;
margin-right: 24px;
}
.search {
composes: topbarDesktopLabel;
composes: bodyFont from '../../marketplace.css';
color: var(--matterColor);
}
/* Spacer */
/* Extra space is gathered between search and createListingLink */
.spacer {
flex-grow: 1;
}
/* Create listing (CTA for providers) */
.createListingLink {
align-self: flex-start;
height: 100%;
margin-right: 24px;
}
.createListing {
composes: topbarDesktopLabel;
composes: bodyFont from '../../marketplace.css';
color: var(--marketplaceColor);
}
/* Inbox */
.inboxLink {
align-self: flex-start;
height: 100%;
margin-right: 24px;
}
.inbox {
composes: topbarDesktopLabel;
composes: bodyFont from '../../marketplace.css';
color: var(--matterColor);
}
/* Profile menu */
/* TODO This is placeholder, it needs Menu component */
.avatarLink {
align-self: flex-start;
flex-shrink: 0;
display: flex;
align-items: center;
height: 100%;
cursor: pointer;
}
.avatar {
width: 41px;
height: 41px;
}
/* Authentication */
.signupLink {
align-self: flex-start;
height: 100%;
margin-right: 24px;
}
.loginLink {
align-self: flex-start;
height: 100%;
}
.signup,
.login {
composes: topbarDesktopLabel;
composes: bodyFont from '../../marketplace.css';
color: var(--matterColor);
}

View file

@ -0,0 +1,14 @@
import { fakeIntl } from '../../util/test-data';
import TopbarDesktop from './TopbarDesktop';
export const AuthenticatedDesktopTopbar = {
component: TopbarDesktop,
props: {
isAuthenticated: true,
currentUserHasListings: true,
name: 'John Doe',
intl: fakeIntl,
},
group: 'navigation',
};

View file

@ -0,0 +1,82 @@
import React, { PropTypes } from 'react';
import { FormattedMessage, intlShape, injectIntl } from 'react-intl';
import classNames from 'classnames';
import { Avatar, 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 rootClass = rootClassName || css.root;
const classes = classNames(rootClass, className);
// TODO This is just a placeholder
const search = (
<div className={css.searchLink}>
<span className={css.search}>Search should be here</span>
</div>
);
const inboxLink = isAuthenticated
? <NamedLink
className={css.inboxLink}
name="InboxPage"
params={{ tab: currentUserHasListings ? 'sales' : 'orders' }}
>
<span className={css.inbox}><FormattedMessage id="TopbarDesktop.inbox" /></span>
</NamedLink>
: null;
// TODO This is just a placeholder
const profileMenu = isAuthenticated
? <div className={css.avatarLink}> <Avatar className={css.avatar} name={name} /></div>
: null;
const signupLink = isAuthenticated
? null
: <NamedLink name="SignupPage" className={css.signupLink}>
<span className={css.signup}><FormattedMessage id="TopbarDesktop.signup" /></span>
</NamedLink>;
const loginLink = isAuthenticated
? null
: <NamedLink name="LoginPage" className={css.loginLink}>
<span className={css.login}><FormattedMessage id="TopbarDesktop.login" /></span>
</NamedLink>;
return (
<div className={classes}>
<NamedLink className={css.logoLink} name="LandingPage">
<img className={css.logo} src={logo} alt={intl.formatMessage({ id: 'TopbarDesktop.logo' })} />
</NamedLink>
{search}
<div className={css.spacer} />
<NamedLink className={css.createListingLink} name="NewListingPage">
<span className={css.createListing}><FormattedMessage id="TopbarDesktop.createListing" /></span>
</NamedLink>
{inboxLink}
{profileMenu}
{signupLink}
{loginLink}
</div>
);
};
TopbarDesktop.defaultProps = { name: '', className: null, rootClassName: '' };
const { bool, string } = PropTypes;
TopbarDesktop.propTypes = {
className: string,
currentUserHasListings: bool.isRequired,
isAuthenticated: bool.isRequired,
name: string,
rootClassName: string,
// from injectIntl
intl: intlShape.isRequired,
};
export default injectIntl(TopbarDesktop);

View file

@ -0,0 +1,25 @@
import React from 'react';
import { fakeIntl } from '../../util/test-data';
import { renderDeep } from '../../util/test-helpers';
import { RoutesProvider } from '../../components';
import routesConfiguration from '../../routesConfiguration';
import { flattenRoutes } from '../../util/routes';
import TopbarDesktop from './TopbarDesktop';
describe('TopbarDesktop', () => {
it('data matches snapshot', () => {
const flattenedRoutes = flattenRoutes(routesConfiguration);
const tree = renderDeep(
<RoutesProvider flattenedRoutes={flattenedRoutes}>
<TopbarDesktop
isAuthenticated
currentUserHasListings
name="John Doe"
intl={fakeIntl}
/>
</RoutesProvider>
);
expect(tree).toMatchSnapshot();
});
});

View file

@ -0,0 +1,56 @@
exports[`TopbarDesktop data matches snapshot 1`] = `
<div
className="">
<a
className="NamedLink_active"
href="/"
onClick={[Function]}
style={Object {}}>
<img
alt="Logo"
className={undefined}
src="saunatime-logo.png" />
</a>
<div
className={undefined}>
<span
className={undefined}>
Search should be here
</span>
</div>
<div
className={undefined} />
<a
className=""
href="/l/new"
onClick={[Function]}
style={Object {}}>
<span
className={undefined}>
<span>
+ Add your sauna
</span>
</span>
</a>
<a
className=""
href="/inbox/sales"
onClick={[Function]}
style={Object {}}>
<span
className={undefined}>
<span>
Inbox
</span>
</span>
</a>
<div
className={undefined}>
<img
alt="John Doe"
className=""
src="wireframeAvatar.svg" />
</div>
</div>
`;

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View file

@ -40,6 +40,7 @@ import Select from './Select/Select';
import StripeBankAccountToken from './StripeBankAccountToken/StripeBankAccountToken';
import TabNav from './TabNav/TabNav';
import Tabs from './Tabs/Tabs';
import TopbarDesktop from './TopbarDesktop/TopbarDesktop';
import ValidationError from './ValidationError/ValidationError';
export {
@ -87,5 +88,6 @@ export {
StripeBankAccountToken,
TabNav,
Tabs,
TopbarDesktop,
ValidationError,
};

View file

@ -1,3 +1,5 @@
@import '../../marketplace.css';
.root {
width: 100%;
}
@ -20,6 +22,10 @@
/* shadows */
box-shadow: 0 1px 1px 0 #dcdcdc;
@media (min-width: 768px) {
display: none;
}
}
.home {
@ -55,3 +61,11 @@
.searchForm {
margin: 2rem;
}
.desktop {
display: none;
@media (min-width: 768px) {
display: block;
}
}

View file

@ -4,7 +4,7 @@ import { connect } from 'react-redux';
import { intlShape, injectIntl } from 'react-intl';
import { pickBy } from 'lodash';
import { logout, authenticationInProgress } from '../../ducks/Auth.duck';
import { FlatButton, MobileMenu, Modal, NamedLink } from '../../components';
import { FlatButton, MobileMenu, Modal, NamedLink, TopbarDesktop } from '../../components';
import { SearchForm } from '../../containers';
import { withFlattenedRoutes } from '../../util/contextHelpers';
import { parse, stringify } from '../../util/urlHelpers';
@ -135,6 +135,14 @@ class TopbarComponent extends Component {
<img src={searchIcon} alt={intl.formatMessage({ id: 'Topbar.searchIcon' })} />
</FlatButton>
</div>
<div className={css.desktop}>
<TopbarDesktop
currentUserHasListings={currentUserHasListings}
intl={intl}
isAuthenticated={isAuthenticated}
name={name}
/>
</div>
<Modal
isOpen={isMobileMenuOpen}
onClose={this.handleMobileMenuClose}

View file

@ -17,6 +17,7 @@ import * as StripeBankAccountToken
from './components/StripeBankAccountToken/StripeBankAccountToken.example';
import * as TabNav from './components/TabNav/TabNav.example';
import * as Tabs from './components/Tabs/Tabs.example';
import * as TopbarDesktop from './components/TopbarDesktop/TopbarDesktop.example';
// containers
import * as BookingDatesForm from './containers/BookingDatesForm/BookingDatesForm.example';
@ -72,5 +73,6 @@ export {
StripePaymentForm,
TabNav,
Tabs,
TopbarDesktop,
Typography,
};

View file

@ -133,6 +133,12 @@ pre,
letter-spacing: -0.1px;
/* TODO Desktop styles needs to be extracted */
@media (min-width: 768px) {
font-weight: 500;
font-size: 16px;
line-height: 24px;
padding: 2px 0 6px 0; /* 2px + 6px = 8px */
}
}
.tinyFont {
@ -155,6 +161,10 @@ html,
/* NORMALIZATIONS for other elements */
/* TODO */
html {
padding: 0;
}
a {
text-decoration: none;

View file

@ -188,5 +188,10 @@
"StripePaymentForm.stripe.validation_error.processing_error": "An error occurred while processing the card.",
"StripePaymentForm.submitPaymentInfo": "Send request",
"Topbar.menuIcon": "Open menu",
"Topbar.searchIcon": "Open search"
"Topbar.searchIcon": "Open search",
"TopbarDesktop.createListing": "+ Add your sauna",
"TopbarDesktop.inbox": "Inbox",
"TopbarDesktop.login": "Log in",
"TopbarDesktop.logo": "Logo",
"TopbarDesktop.signup": "Sign up"
}