Merge pull request #149 from sharetribe/tabnav-component

TabNav component
This commit is contained in:
Kimmo Puputti 2017-05-08 15:21:25 +03:00 committed by GitHub
commit 3d1707d1f6
9 changed files with 174 additions and 95 deletions

View file

@ -0,0 +1,19 @@
.tab {
float: left;
margin-left: 1rem;
font-size: 20px;
letter-spacing: -0.5px;
&:first-child {
margin-left: 0;
}
}
.link {
text-decoration: none;
}
.selected {
font-weight: bold;
text-decoration: underline;
}

View file

@ -0,0 +1,16 @@
import TabNav from './TabNav';
const selfLinkProps = {
name: 'StyleguideComponent',
params: { component: 'TabNav' },
};
export const Empty = {
component: TabNav,
props: {
tabs: [
{ text: 'Normal', linkProps: selfLinkProps },
{ text: 'Selected', linkProps: selfLinkProps, selected: true },
],
},
};

View file

@ -0,0 +1,45 @@
import React, { PropTypes } from 'react';
import classNames from 'classnames';
import { NamedLink } from '../../components';
import css from './TabNav.css';
const Tab = props => {
const { text, selected, linkProps } = props;
const classes = classNames(css.tab, {
[css.selected]: selected,
});
return (
<div className={classes}>
<NamedLink className={css.link} {...linkProps}>{text}</NamedLink>
</div>
);
};
Tab.defaultProps = { selected: false };
const { arrayOf, object, string, bool } = PropTypes;
Tab.propTypes = {
text: string.isRequired,
selected: bool,
linkProps: object.isRequired,
};
const TabNav = props => {
const { className, tabs } = props;
return (
<nav className={className}>
{tabs.map(tab => <Tab key={tab.text} {...tab} />)}
</nav>
);
};
TabNav.defaultProps = { className: '' };
TabNav.propTypes = {
className: string,
tabs: arrayOf(object).isRequired,
};
export default TabNav;

View file

@ -32,6 +32,7 @@ import RoutesProvider from './RoutesProvider/RoutesProvider';
import SaleDetailsPanel from './SaleDetailsPanel/SaleDetailsPanel';
import SearchResultsPanel from './SearchResultsPanel/SearchResultsPanel';
import StripeBankAccountToken from './StripeBankAccountToken/StripeBankAccountToken';
import TabNav from './TabNav/TabNav';
import ValidationError from './ValidationError/ValidationError';
export {
@ -71,5 +72,6 @@ export {
SaleDetailsPanel,
SearchResultsPanel,
StripeBankAccountToken,
TabNav,
ValidationError,
};

View file

@ -2,17 +2,8 @@
margin: 1rem;
}
.tab {
float: left;
.tabs {
margin-left: 1rem;
font-size: 20px;
letter-spacing: -0.5px;
text-decoration: none;
}
.activeTab {
font-weight: bold;
text-decoration: underline;
}
.error {

View file

@ -2,7 +2,14 @@ import React, { PropTypes } from 'react';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
import { Avatar, NamedLink, NamedRedirect, PageLayout, PaginationLinks } from '../../components';
import {
Avatar,
NamedLink,
NamedRedirect,
PageLayout,
PaginationLinks,
TabNav,
} from '../../components';
import * as propTypes from '../../util/propTypes';
import { getMarketplaceEntities } from '../../ducks/marketplaceData.duck';
import { loadData } from './InboxPage.duck';
@ -86,8 +93,6 @@ export const InboxPageComponent = props => {
fetchOrdersOrSalesError,
pagination,
transactions,
currentUserHasListings,
currentUserHasListingsError,
intl,
params,
} = props;
@ -112,7 +117,7 @@ export const InboxPageComponent = props => {
);
};
const error = fetchOrdersOrSalesError || currentUserHasListingsError
const error = fetchOrdersOrSalesError
? <p className={css.error}>
<FormattedMessage id="InboxPage.fetchFailed" />
</p>
@ -133,31 +138,36 @@ export const InboxPageComponent = props => {
/>
: null;
const tabs = [
{
text: intl.formatMessage({
id: 'InboxPage.ordersTabTitle',
}),
selected: isOrders,
linkProps: {
name: 'InboxPage',
params: { tab: 'orders' },
},
},
{
text: intl.formatMessage({
id: 'InboxPage.salesTabTitle',
}),
selected: !isOrders,
linkProps: {
name: 'InboxPage',
params: { tab: 'sales' },
},
},
];
const nav = <TabNav className={css.tabs} tabs={tabs} />;
return (
<PageLayout title={title}>
<h1 className={css.title}>
<FormattedMessage id="InboxPage.title" />
</h1>
{currentUserHasListings
? <nav>
<NamedLink
className={css.tab}
name="InboxPage"
params={{ tab: 'orders' }}
activeClassName={isOrders ? css.activeTab : null}
>
<FormattedMessage id="InboxPage.ordersTabTitle" />
</NamedLink>
<NamedLink
className={css.tab}
name="InboxPage"
params={{ tab: 'sales' }}
activeClassName={!isOrders ? css.activeTab : null}
>
<FormattedMessage id="InboxPage.salesTabTitle" />
</NamedLink>
</nav>
: null}
{nav}
{error}
<ul>
{!fetchInProgress ? transactions.map(toTxItem) : null}
@ -171,8 +181,6 @@ export const InboxPageComponent = props => {
InboxPageComponent.defaultProps = {
fetchOrdersOrSalesError: null,
pagination: null,
currentUserHasListings: false,
currentUserHasListingsError: null,
};
InboxPageComponent.propTypes = {
@ -184,8 +192,6 @@ InboxPageComponent.propTypes = {
fetchOrdersOrSalesError: instanceOf(Error),
pagination: propTypes.pagination,
transactions: arrayOf(propTypes.transaction).isRequired,
currentUserHasListings: bool,
currentUserHasListingsError: instanceOf(Error),
// from injectIntl
intl: intlShape.isRequired,
@ -197,16 +203,12 @@ const mapStateToProps = state => {
fetchOrdersOrSalesError,
pagination,
transactionRefs,
currentUserHasListings,
currentUserHasListingsError,
} = state.InboxPage;
return {
fetchInProgress,
fetchOrdersOrSalesError,
pagination,
transactions: getMarketplaceEntities(state, transactionRefs),
currentUserHasListings,
currentUserHasListingsError,
};
};

View file

@ -6,31 +6,32 @@ exports[`InboxPage matches snapshot 1`] = `
id="InboxPage.title"
values={Object {}} />
</h1>
<nav>
<withFlattenedRoutes(withRouter(NamedLink))
name="InboxPage"
params={
<TabNav
className=""
tabs={
Array [
Object {
"tab": "orders",
}
}>
<FormattedMessage
id="InboxPage.ordersTabTitle"
values={Object {}} />
</withFlattenedRoutes(withRouter(NamedLink))>
<withFlattenedRoutes(withRouter(NamedLink))
activeClassName={null}
name="InboxPage"
params={
"linkProps": Object {
"name": "InboxPage",
"params": Object {
"tab": "orders",
},
},
"selected": true,
"text": "InboxPage.ordersTabTitle",
},
Object {
"tab": "sales",
}
}>
<FormattedMessage
id="InboxPage.salesTabTitle"
values={Object {}} />
</withFlattenedRoutes(withRouter(NamedLink))>
</nav>
"linkProps": Object {
"name": "InboxPage",
"params": Object {
"tab": "sales",
},
},
"selected": false,
"text": "InboxPage.salesTabTitle",
},
]
} />
<ul>
<li>
<Component
@ -182,31 +183,32 @@ exports[`InboxPage matches snapshot 3`] = `
id="InboxPage.title"
values={Object {}} />
</h1>
<nav>
<withFlattenedRoutes(withRouter(NamedLink))
activeClassName={null}
name="InboxPage"
params={
<TabNav
className=""
tabs={
Array [
Object {
"tab": "orders",
}
}>
<FormattedMessage
id="InboxPage.ordersTabTitle"
values={Object {}} />
</withFlattenedRoutes(withRouter(NamedLink))>
<withFlattenedRoutes(withRouter(NamedLink))
name="InboxPage"
params={
"linkProps": Object {
"name": "InboxPage",
"params": Object {
"tab": "orders",
},
},
"selected": false,
"text": "InboxPage.ordersTabTitle",
},
Object {
"tab": "sales",
}
}>
<FormattedMessage
id="InboxPage.salesTabTitle"
values={Object {}} />
</withFlattenedRoutes(withRouter(NamedLink))>
</nav>
"linkProps": Object {
"name": "InboxPage",
"params": Object {
"tab": "sales",
},
},
"selected": true,
"text": "InboxPage.salesTabTitle",
},
]
} />
<ul>
<li>
<Component

View file

@ -13,6 +13,7 @@ import * as LocationAutocompleteInput
import * as PaginationLinks from './components/PaginationLinks/PaginationLinks.example';
import * as StripeBankAccountToken
from './components/StripeBankAccountToken/StripeBankAccountToken.example';
import * as TabNav from './components/TabNav/TabNav.example';
// containers
import * as BookingDatesForm from './containers/BookingDatesForm/BookingDatesForm.example';
@ -36,7 +37,6 @@ export {
CurrencyInput,
DateInput,
EditListingForm,
SearchForm,
ListingCard,
LocationAutocompleteInput,
LoginForm,
@ -46,7 +46,9 @@ export {
NamedLink,
PaginationLinks,
PasswordForgottenForm,
SearchForm,
SignupForm,
StripeBankAccountToken,
StripePaymentForm,
TabNav,
};

View file

@ -45,12 +45,12 @@
"HeroSection.subTitle": "The largest online community to rent music studios",
"HeroSection.title": "Book Studiotime anywhere",
"InboxPage.fetchFailed": "Could not load all messages. Please try again.",
"InboxPage.noOrdersFound": "No orders found.",
"InboxPage.noSalesFound": "No sales found.",
"InboxPage.ordersTabTitle": "Guest",
"InboxPage.ordersTitle": "Inbox: Orders",
"InboxPage.salesTabTitle": "Host",
"InboxPage.salesTitle": "Inbox: Sales",
"InboxPage.noOrdersFound": "You haven't made any bookings.",
"InboxPage.noSalesFound": "Nobody has booked anything from you yet.",
"InboxPage.ordersTabTitle": "Bathing",
"InboxPage.ordersTitle": "Inbox: Bathing",
"InboxPage.salesTabTitle": "Hosting",
"InboxPage.salesTitle": "Inbox: Hosting",
"InboxPage.stateAccepted": "Accepted",
"InboxPage.statePending": "Pending",
"InboxPage.stateRejected": "Rejected",