Merge pull request #513 from sharetribe/footer-to-pages

Footer to different pages
This commit is contained in:
Vesa Luusua 2017-10-23 14:14:19 +03:00 committed by GitHub
commit aa9117e3ac
46 changed files with 1385 additions and 962 deletions

View file

@ -79,7 +79,6 @@ describe('Application', () => {
'/account/contact-details': defaultAuthPath,
'/account/change-password': defaultAuthPath,
'/account/payout-preferences': defaultAuthPath,
'/account/security': defaultAuthPath,
'/verify-email': loginPath,
};
forEach(urlRedirects, (redirectPath, url) => {

View file

@ -82,7 +82,7 @@
}
.organizationInfo {
margin-left: 35px;
margin-left: 36px;
width: 179px;
}

View file

@ -1,4 +1,7 @@
@import '../../marketplace.css';
.root {
width: 100%;
position: relative;
z-index: var(--zIndexTopbar);
}

View file

@ -186,6 +186,8 @@
}
.actionButtons {
/* Position action button row above the footer */
z-index: 9;
position: fixed;
bottom: 0;
width: 100%;
@ -201,6 +203,7 @@
background-color: white;
@media (--viewportLarge) {
z-index: unset;
position: static;
box-shadow: none;
width: auto;

View file

@ -1,5 +1,13 @@
@import '../../marketplace.css';
.layoutWrapperMain {
min-height: calc(100vh - var(--topbarHeight));
@media (--viewportMedium) {
min-height: calc(100vh - var(--topbarHeightDesktop));
}
}
.root {
@apply --marketplaceModalRootStyles;
}

View file

@ -20,6 +20,11 @@ import {
IconEmailSent,
InlineTextButton,
IconClose,
LayoutSingleColumn,
LayoutWrapperTopbar,
LayoutWrapperMain,
LayoutWrapperFooter,
Footer,
} from '../../components';
import { LoginForm, SignupForm, TopbarContainer } from '../../containers';
import { login, authenticationInProgress, signup } from '../../ducks/Auth.duck';
@ -216,10 +221,19 @@ export const AuthenticationPageComponent = props => {
name: schemaTitle,
}}
>
<TopbarContainer className={topbarClasses} />
<div className={css.root}>
{showEmailVerification ? emailVerificationContent : formContent}
</div>
<LayoutSingleColumn>
<LayoutWrapperTopbar>
<TopbarContainer className={topbarClasses} />
</LayoutWrapperTopbar>
<LayoutWrapperMain className={css.layoutWrapperMain}>
<div className={css.root}>
{showEmailVerification ? emailVerificationContent : formContent}
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter>
<Footer />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
);
};

View file

@ -14,61 +14,82 @@ exports[`AuthenticationPageComponent matches snapshot 1`] = `
scrollingDisabled={false}
title="AuthenticationPage.schemaTitleLogin"
>
<withRouter(Connect(TopbarContainerComponent))
className=""
/>
<div>
<div>
<TabNavHorizontal
className={null}
rootClassName={null}
skin="light"
tabClassName={null}
tabRootClassName={null}
tabs={
Array [
Object {
"linkProps": Object {
"name": "SignupPage",
"to": Object {
"state": Object {
"from": "/protected",
},
},
},
"selected": false,
"text": <h1>
<FormattedMessage
id="AuthenticationPage.signupLinkText"
values={Object {}}
/>
</h1>,
},
Object {
"linkProps": Object {
"name": "LoginPage",
"to": Object {
"state": Object {
"from": "/protected",
},
},
},
"selected": true,
"text": <h1>
<FormattedMessage
id="AuthenticationPage.loginLinkText"
values={Object {}}
/>
</h1>,
},
]
}
<LayoutSingleColumn
className={null}
rootClassName={null}
>
<LayoutWrapperTopbar
className={null}
rootClassName={null}
>
<withRouter(Connect(TopbarContainerComponent))
className=""
/>
<ReduxForm
inProgress={false}
onSubmit={[Function]}
/>
</div>
</div>
</LayoutWrapperTopbar>
<LayoutWrapperMain
className={null}
rootClassName={null}
>
<div>
<div>
<TabNavHorizontal
className={null}
rootClassName={null}
skin="light"
tabClassName={null}
tabRootClassName={null}
tabs={
Array [
Object {
"linkProps": Object {
"name": "SignupPage",
"to": Object {
"state": Object {
"from": "/protected",
},
},
},
"selected": false,
"text": <h1>
<FormattedMessage
id="AuthenticationPage.signupLinkText"
values={Object {}}
/>
</h1>,
},
Object {
"linkProps": Object {
"name": "LoginPage",
"to": Object {
"state": Object {
"from": "/protected",
},
},
},
"selected": true,
"text": <h1>
<FormattedMessage
id="AuthenticationPage.loginLinkText"
values={Object {}}
/>
</h1>,
},
]
}
/>
<ReduxForm
inProgress={false}
onSubmit={[Function]}
/>
</div>
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter
className={null}
rootClassName={null}
>
<InjectIntl(Footer) />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
`;

View file

@ -11,6 +11,8 @@ import {
LayoutWrapperMain,
LayoutWrapperSideNav,
LayoutWrapperTopbar,
LayoutWrapperFooter,
Footer,
Page,
TabNav,
UserNav,
@ -99,6 +101,9 @@ export const ContactDetailsPageComponent = props => {
{changeEmailForm}
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter>
<Footer />
</LayoutWrapperFooter>
</LayoutSideNavigation>
</Page>
);

View file

@ -73,6 +73,12 @@ exports[`ContactDetailsPage matches snapshot 1`] = `
</h1>
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter
className={null}
rootClassName={null}
>
<InjectIntl(Footer) />
</LayoutWrapperFooter>
</LayoutSideNavigation>
</Page>
`;

View file

@ -1,5 +1,13 @@
@import '../../marketplace.css';
.layoutWrapperMain {
min-height: calc(100vh - var(--topbarHeight));
@media (--viewportMedium) {
min-height: calc(100vh - var(--topbarHeightDesktop));
}
}
.root {
@apply --marketplaceModalRootStyles;
}

View file

@ -8,7 +8,14 @@ import * as propTypes from '../../util/propTypes';
import { verify } from '../../ducks/EmailVerification.duck';
import { isScrollingDisabled } from '../../ducks/UI.duck';
import { parse } from '../../util/urlHelpers';
import { Page } from '../../components';
import {
Page,
LayoutSingleColumn,
LayoutWrapperTopbar,
LayoutWrapperMain,
LayoutWrapperFooter,
Footer,
} from '../../components';
import { EmailVerificationForm, TopbarContainer } from '../../containers';
import css from './EmailVerificationPage.css';
@ -60,22 +67,31 @@ export const EmailVerificationPageComponent = props => {
logoutError={logoutError}
scrollingDisabled={scrollingDisabled}
>
<TopbarContainer />
<div className={css.root}>
<div className={css.content}>
{currentUser ? (
<EmailVerificationForm
initialValues={initialValues}
onSubmit={submitVerification}
currentUser={currentUser}
inProgress={emailVerificationInProgress}
verificationError={verificationError}
/>
) : (
<FormattedMessage id="EmailVerificationPage.loadingUserInformation" />
)}
</div>
</div>
<LayoutSingleColumn>
<LayoutWrapperTopbar>
<TopbarContainer />
</LayoutWrapperTopbar>
<LayoutWrapperMain className={css.layoutWrapperMain}>
<div className={css.root}>
<div className={css.content}>
{currentUser ? (
<EmailVerificationForm
initialValues={initialValues}
onSubmit={submitVerification}
currentUser={currentUser}
inProgress={emailVerificationInProgress}
verificationError={verificationError}
/>
) : (
<FormattedMessage id="EmailVerificationPage.loadingUserInformation" />
)}
</div>
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter>
<Footer />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
);
};

View file

@ -35,38 +35,6 @@
}
}
.container {
width: 100%;
flex: 1;
display: flex;
flex-direction: column;
@media (--viewportLarge) {
flex-direction: row;
justify-content: center;
}
}
.content {
/* Expand to the full remaining width of the viewport */
flex: 1;
display: flex;
flex-direction: column;
@media (--viewportLarge) {
padding: 118px 15vw 82px 82px;
background-color: var(--matterColorLight);
border-left-width: 1px;
border-left-style: solid;
border-left-color: var(--matterColorNegative);
}
@media (--viewportXLarge) {
padding-right: 25vw;
}
}
.navigation {
/* Layout */
display: flex;

View file

@ -17,6 +17,12 @@ import {
Page,
PaginationLinks,
TabNav,
LayoutSideNavigation,
LayoutWrapperMain,
LayoutWrapperSideNav,
LayoutWrapperTopbar,
LayoutWrapperFooter,
Footer,
} from '../../components';
import { TopbarContainer } from '../../containers';
@ -261,28 +267,33 @@ export const InboxPageComponent = props => {
title={title}
scrollingDisabled={scrollingDisabled}
>
<TopbarContainer
className={css.topbar}
mobileRootClassName={css.mobileTopbar}
desktopClassName={css.desktopTopbar}
currentPage="InboxPage"
/>
<div className={css.container}>
<div className={css.navigation}>
<LayoutSideNavigation>
<LayoutWrapperTopbar>
<TopbarContainer
className={css.topbar}
mobileRootClassName={css.mobileTopbar}
desktopClassName={css.desktopTopbar}
currentPage="InboxPage"
/>
</LayoutWrapperTopbar>
<LayoutWrapperSideNav className={css.navigation}>
<h1 className={css.title}>
<FormattedMessage id="InboxPage.title" />
</h1>
{nav}
</div>
<div className={css.content}>
</LayoutWrapperSideNav>
<LayoutWrapperMain>
{error}
<ul className={css.itemList}>
{!fetchInProgress ? transactions.map(toTxItem) : null}
{noResults}
</ul>
{pagingLinks}
</div>
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter>
<Footer />
</LayoutWrapperFooter>
</LayoutSideNavigation>
</Page>
);
};

View file

@ -7,11 +7,23 @@ exports[`InboxPage matches snapshot 1`] = `
scrollingDisabled={false}
title="InboxPage.ordersTitle"
>
<withRouter(Connect(TopbarContainerComponent))
currentPage="InboxPage"
/>
<div>
<div>
<LayoutSideNavigation
className={null}
containerClassName={null}
rootClassName={null}
>
<LayoutWrapperTopbar
className={null}
rootClassName={null}
>
<withRouter(Connect(TopbarContainerComponent))
currentPage="InboxPage"
/>
</LayoutWrapperTopbar>
<LayoutWrapperSideNav
className={null}
rootClassName={null}
>
<h1>
<FormattedMessage
id="InboxPage.title"
@ -58,8 +70,11 @@ exports[`InboxPage matches snapshot 1`] = `
]
}
/>
</div>
<div>
</LayoutWrapperSideNav>
<LayoutWrapperMain
className={null}
rootClassName={null}
>
<ul>
<li>
<Component
@ -266,8 +281,14 @@ exports[`InboxPage matches snapshot 1`] = `
/>
</li>
</ul>
</div>
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter
className={null}
rootClassName={null}
>
<InjectIntl(Footer) />
</LayoutWrapperFooter>
</LayoutSideNavigation>
</Page>
`;
@ -342,11 +363,23 @@ exports[`InboxPage matches snapshot 3`] = `
scrollingDisabled={false}
title="InboxPage.salesTitle"
>
<withRouter(Connect(TopbarContainerComponent))
currentPage="InboxPage"
/>
<div>
<div>
<LayoutSideNavigation
className={null}
containerClassName={null}
rootClassName={null}
>
<LayoutWrapperTopbar
className={null}
rootClassName={null}
>
<withRouter(Connect(TopbarContainerComponent))
currentPage="InboxPage"
/>
</LayoutWrapperTopbar>
<LayoutWrapperSideNav
className={null}
rootClassName={null}
>
<h1>
<FormattedMessage
id="InboxPage.title"
@ -393,8 +426,11 @@ exports[`InboxPage matches snapshot 3`] = `
]
}
/>
</div>
<div>
</LayoutWrapperSideNav>
<LayoutWrapperMain
className={null}
rootClassName={null}
>
<ul>
<li>
<Component
@ -601,8 +637,14 @@ exports[`InboxPage matches snapshot 3`] = `
/>
</li>
</ul>
</div>
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter
className={null}
rootClassName={null}
>
<InjectIntl(Footer) />
</LayoutWrapperFooter>
</LayoutSideNavigation>
</Page>
`;

View file

@ -7,7 +7,15 @@ import { injectIntl, intlShape } from 'react-intl';
import * as propTypes from '../../util/propTypes';
import { isScrollingDisabled } from '../../ducks/UI.duck';
import config from '../../config';
import { Page, HeroSection } from '../../components';
import {
Page,
HeroSection,
LayoutSingleColumn,
LayoutWrapperTopbar,
LayoutWrapperMain,
LayoutWrapperFooter,
Footer,
} from '../../components';
import { TopbarContainer } from '../../containers';
import facebookImage from '../../assets/saunatimeFacebook-1200x630.jpg';
@ -44,10 +52,19 @@ export const LandingPageComponent = props => {
image: [schemaImage],
}}
>
<TopbarContainer />
<div className={css.heroContainer}>
<HeroSection className={css.hero} history={history} location={location} />
</div>
<LayoutSingleColumn>
<LayoutWrapperTopbar>
<TopbarContainer />
</LayoutWrapperTopbar>
<LayoutWrapperMain>
<div className={css.heroContainer}>
<HeroSection className={css.hero} history={history} location={location} />
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter>
<Footer />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
);
};

View file

@ -38,22 +38,43 @@ exports[`LandingPage matches snapshot 1`] = `
]
}
>
<withRouter(Connect(TopbarContainerComponent)) />
<div>
<HeroSection
<LayoutSingleColumn
className={null}
rootClassName={null}
>
<LayoutWrapperTopbar
className={null}
history={
Object {
"push": [Function],
}
}
location={
Object {
"search": "",
}
}
rootClassName={null}
/>
</div>
>
<withRouter(Connect(TopbarContainerComponent)) />
</LayoutWrapperTopbar>
<LayoutWrapperMain
className={null}
rootClassName={null}
>
<div>
<HeroSection
className={null}
history={
Object {
"push": [Function],
}
}
location={
Object {
"search": "",
}
}
rootClassName={null}
/>
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter
className={null}
rootClassName={null}
>
<InjectIntl(Footer) />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
`;

View file

@ -1,10 +1,10 @@
@import '../../marketplace.css';
.pageRoot {
display: block;
display: flex;
padding-bottom: 90px;
@media (--viewportLarge) {
padding-bottom: 24px;
padding-bottom: 0;
}
}
@ -393,6 +393,8 @@
}
.openBookingForm {
/* Ensure that mobile button is over Footer too */
z-index: 9;
position: fixed;
bottom: 0;
left: 0;

View file

@ -5,7 +5,6 @@ import { FormattedMessage, intlShape, injectIntl } from 'react-intl';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import classNames from 'classnames';
import config from '../../config';
import routeConfiguration from '../../routeConfiguration';
import * as propTypes from '../../util/propTypes';
@ -28,6 +27,11 @@ import {
NamedRedirect,
Modal,
ImageCarousel,
LayoutSingleColumn,
LayoutWrapperTopbar,
LayoutWrapperMain,
LayoutWrapperFooter,
Footer,
} from '../../components';
import { BookingDatesForm, TopbarContainer } from '../../containers';
@ -182,10 +186,17 @@ export class ListingPageComponent extends Component {
return (
<Page title={errorTitle}>
{topbar}
<p className={css.errorText}>
<FormattedMessage id="ListingPage.errorLoadingListingMessage" />
</p>
<LayoutSingleColumn className={css.pageRoot}>
<LayoutWrapperTopbar>{topbar}</LayoutWrapperTopbar>
<LayoutWrapperMain>
<p className={css.errorText}>
<FormattedMessage id="ListingPage.errorLoadingListingMessage" />
</p>
</LayoutWrapperMain>
<LayoutWrapperFooter>
<Footer />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
);
} else if (!currentListing.id) {
@ -193,10 +204,17 @@ export class ListingPageComponent extends Component {
return (
<Page title={loadingTitle}>
{topbar}
<p className={css.loadingText}>
<FormattedMessage id="ListingPage.loadingListingMessage" />
</p>
<LayoutSingleColumn className={css.pageRoot}>
<LayoutWrapperTopbar>{topbar}</LayoutWrapperTopbar>
<LayoutWrapperMain>
<p className={css.loadingText}>
<FormattedMessage id="ListingPage.loadingListingMessage" />
</p>
</LayoutWrapperMain>
<LayoutWrapperFooter>
<Footer />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
);
}
@ -275,8 +293,6 @@ export class ListingPageComponent extends Component {
const editParams = { id: listingId.uuid, slug: listingSlug, type: 'edit', tab: 'description' };
const listingClasses = classNames(css.pageRoot);
const handleBookButtonClick = () => {
const isClosed = currentListing.attributes.closed;
if (isOwnListing || isClosed) {
@ -343,130 +359,137 @@ export class ListingPageComponent extends Component {
image: schemaImages,
}}
>
{topbar}
<div className={listingClasses}>
<div className={css.threeToTwoWrapper}>
<div className={css.aspectWrapper} onClick={handleViewPhotosClick}>
{actionBar}
<ResponsiveImage
rootClassName={css.rootForImage}
alt={title}
image={firstImage}
nameSet={[
{ name: 'landscape-crop', size: '400w' },
{ name: 'landscape-crop2x', size: '800w' },
{ name: 'landscape-crop4x', size: '1600w' },
{ name: 'landscape-crop6x', size: '2400w' },
]}
sizes="100vw"
/>
{viewPhotosButton}
</div>
</div>
<Modal
id="ListingPage.imageCarousel"
scrollLayerClassName={css.carouselModalScrollLayer}
containerClassName={css.carouselModalContainer}
lightCloseButton
isOpen={this.state.imageCarouselOpen}
onClose={() => this.setState({ imageCarouselOpen: false })}
onManageDisableScrolling={onManageDisableScrolling}
>
<ImageCarousel images={currentListing.images} />
</Modal>
<div className={css.contentContainer}>
<div className={css.avatarWrapper}>
<AvatarLarge user={currentAuthor} className={css.avatarDesktop} />
<AvatarMedium user={currentAuthor} className={css.avatarMobile} />
</div>
<div className={css.mainContent}>
<div className={css.headingContainer}>
<div className={css.desktopPriceContainer}>
<div className={css.desktopPriceValue} title={priceTitle}>
{formattedPrice}
</div>
<div className={css.desktopPerNight}>
<FormattedMessage id="ListingPage.perNight" />
</div>
</div>
<div className={css.heading}>
<h1 className={css.title}>{title}</h1>
<div className={css.author}>
<span className={css.authorName}>
<FormattedMessage
id="ListingPage.hostedBy"
values={{ name: currentAuthorDisplayName }}
/>
</span>
</div>
<LayoutSingleColumn className={css.pageRoot}>
<LayoutWrapperTopbar>{topbar}</LayoutWrapperTopbar>
<LayoutWrapperMain>
<div>
<div className={css.threeToTwoWrapper}>
<div className={css.aspectWrapper} onClick={handleViewPhotosClick}>
{actionBar}
<ResponsiveImage
rootClassName={css.rootForImage}
alt={title}
image={firstImage}
nameSet={[
{ name: 'landscape-crop', size: '400w' },
{ name: 'landscape-crop2x', size: '800w' },
{ name: 'landscape-crop4x', size: '1600w' },
{ name: 'landscape-crop6x', size: '2400w' },
]}
sizes="100vw"
/>
{viewPhotosButton}
</div>
</div>
<Modal
id="ListingPage.imageCarousel"
scrollLayerClassName={css.carouselModalScrollLayer}
containerClassName={css.carouselModalContainer}
lightCloseButton
isOpen={this.state.imageCarouselOpen}
onClose={() => this.setState({ imageCarouselOpen: false })}
onManageDisableScrolling={onManageDisableScrolling}
>
<ImageCarousel images={currentListing.images} />
</Modal>
<div className={css.descriptionContainer}>
<h2 className={css.descriptionTitle}>
<FormattedMessage id="ListingPage.descriptionTitle" />
</h2>
<p className={css.description}>{description}</p>
</div>
<div className={css.contentContainer}>
<div className={css.avatarWrapper}>
<AvatarLarge user={currentAuthor} className={css.avatarDesktop} />
<AvatarMedium user={currentAuthor} className={css.avatarMobile} />
</div>
{map}
</div>
<div className={css.mainContent}>
<div className={css.headingContainer}>
<div className={css.desktopPriceContainer}>
<div className={css.desktopPriceValue} title={priceTitle}>
{formattedPrice}
</div>
<div className={css.desktopPerNight}>
<FormattedMessage id="ListingPage.perNight" />
</div>
</div>
<div className={css.heading}>
<h1 className={css.title}>{title}</h1>
<div className={css.author}>
<span className={css.authorName}>
<FormattedMessage
id="ListingPage.hostedBy"
values={{ name: currentAuthorDisplayName }}
/>
</span>
</div>
</div>
</div>
<ModalInMobile
className={css.modalInMobile}
containerClassName={css.modalContainer}
id="BookingDatesFormInModal"
isModalOpenOnMobile={this.state.isBookingModalOpenOnMobile}
onClose={() => this.setState({ isBookingModalOpenOnMobile: false })}
showAsModalMaxWidth={MODAL_BREAKPOINT}
onManageDisableScrolling={onManageDisableScrolling}
>
<div className={css.modalHeading}>
<h1 className={css.title}>{title}</h1>
<div className={css.author}>
<span className={css.authorName}>
<FormattedMessage
id="ListingPage.hostedBy"
values={{ name: currentAuthorDisplayName }}
<div className={css.descriptionContainer}>
<h2 className={css.descriptionTitle}>
<FormattedMessage id="ListingPage.descriptionTitle" />
</h2>
<p className={css.description}>{description}</p>
</div>
{map}
</div>
<ModalInMobile
className={css.modalInMobile}
containerClassName={css.modalContainer}
id="BookingDatesFormInModal"
isModalOpenOnMobile={this.state.isBookingModalOpenOnMobile}
onClose={() => this.setState({ isBookingModalOpenOnMobile: false })}
showAsModalMaxWidth={MODAL_BREAKPOINT}
onManageDisableScrolling={onManageDisableScrolling}
>
<div className={css.modalHeading}>
<h1 className={css.title}>{title}</h1>
<div className={css.author}>
<span className={css.authorName}>
<FormattedMessage
id="ListingPage.hostedBy"
values={{ name: currentAuthorDisplayName }}
/>
</span>
</div>
</div>
{bookingHeading}
{!currentListing.attributes.closed ? (
<BookingDatesForm
className={css.bookingForm}
onSubmit={handleBookingSubmit}
price={price}
isOwnListing={isOwnListing}
/>
</span>
) : null}
</ModalInMobile>
<div className={css.openBookingForm}>
<div className={css.priceContainer}>
<div className={css.priceValue} title={priceTitle}>
{formattedPrice}
</div>
<div className={css.perNight}>
<FormattedMessage id="ListingPage.perNight" />
</div>
</div>
{!currentListing.attributes.closed ? (
<Button rootClassName={css.bookButton} onClick={handleBookButtonClick}>
{bookBtnMessage}
</Button>
) : (
<div className={css.closedListingButton}>
<FormattedMessage id="ListingPage.closedListingButtonText" />
</div>
)}
</div>
</div>
{bookingHeading}
{!currentListing.attributes.closed ? (
<BookingDatesForm
className={css.bookingForm}
onSubmit={handleBookingSubmit}
price={price}
isOwnListing={isOwnListing}
/>
) : null}
</ModalInMobile>
<div className={css.openBookingForm}>
<div className={css.priceContainer}>
<div className={css.priceValue} title={priceTitle}>
{formattedPrice}
</div>
<div className={css.perNight}>
<FormattedMessage id="ListingPage.perNight" />
</div>
</div>
{!currentListing.attributes.closed ? (
<Button rootClassName={css.bookButton} onClick={handleBookButtonClick}>
{bookBtnMessage}
</Button>
) : (
<div className={css.closedListingButton}>
<FormattedMessage id="ListingPage.closedListingButtonText" />
</div>
)}
</div>
</div>
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter>
<Footer />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
);
}

View file

@ -22,254 +22,273 @@ exports[`ListingPage matches snapshot 1`] = `
title="ListingPage.schemaTitle"
twitterImages={Array []}
>
<withRouter(Connect(TopbarContainerComponent)) />
<div
className=""
<LayoutSingleColumn
className={null}
rootClassName={null}
>
<div>
<div
onClick={[Function]}
>
<div
onClick={[Function]}
>
<ActionBar
editParams={
Object {
"id": "listing1",
"slug": "listing1-title",
"tab": "description",
"type": "edit",
}
}
isClosed={false}
isOwnListing={false}
/>
</div>
<ResponsiveImage
alt="listing1 title"
className={null}
image={null}
nameSet={
Array [
Object {
"name": "landscape-crop",
"size": "400w",
},
Object {
"name": "landscape-crop2x",
"size": "800w",
},
Object {
"name": "landscape-crop4x",
"size": "1600w",
},
Object {
"name": "landscape-crop6x",
"size": "2400w",
},
]
}
noImageMessage={null}
rootClassName={null}
sizes="100vw"
/>
</div>
</div>
<InjectIntl(ModalComponent)
id="ListingPage.imageCarousel"
isOpen={false}
lightCloseButton={true}
onClose={[Function]}
onManageDisableScrolling={[Function]}
<LayoutWrapperTopbar
className={null}
rootClassName={null}
>
<withRouter(Connect(TopbarContainerComponent)) />
</LayoutWrapperTopbar>
<LayoutWrapperMain
className={null}
rootClassName={null}
>
<InjectIntl(ImageCarousel)
images={Array []}
/>
</InjectIntl(ModalComponent)>
<div>
<div>
<AvatarLarge
user={
Object {
"attributes": Object {
"banned": false,
"profile": Object {
"abbreviatedName": "user-1 abbreviated name",
"displayName": "user-1 display name",
},
},
"id": UUID {
"uuid": "user-1",
},
"type": "user",
}
}
/>
<AvatarMedium
user={
Object {
"attributes": Object {
"banned": false,
"profile": Object {
"abbreviatedName": "user-1 abbreviated name",
"displayName": "user-1 display name",
},
},
"id": UUID {
"uuid": "user-1",
},
"type": "user",
}
}
/>
</div>
<div>
<div>
<div>
<div
title={55}
>
55
</div>
<div>
<FormattedMessage
id="ListingPage.perNight"
values={Object {}}
/>
</div>
</div>
<div>
<h1>
listing1 title
</h1>
<div>
<span>
<FormattedMessage
id="ListingPage.hostedBy"
values={
Object {
"name": "user-1 display name",
}
}
/>
</span>
</div>
</div>
</div>
<div>
<h2>
<FormattedMessage
id="ListingPage.descriptionTitle"
values={Object {}}
/>
</h2>
<p>
listing1 description
</p>
</div>
<div>
<h2>
<FormattedMessage
id="ListingPage.locationTitle"
values={Object {}}
/>
</h2>
<div>
<Map
address="listing1 address"
center={
LatLng {
"lat": 40,
"lng": 60,
}
}
className=""
rootClassName={null}
zoom={11}
/>
</div>
</div>
</div>
<withViewport(ModalInMobileComponent)
id="BookingDatesFormInModal"
isModalOpenOnMobile={false}
onClose={[Function]}
onManageDisableScrolling={[Function]}
showAsModalMaxWidth={1023}
>
<div>
<h1>
listing1 title
</h1>
<div>
<span>
<FormattedMessage
id="ListingPage.hostedBy"
values={
Object {
"name": "user-1 display name",
}
}
/>
</span>
</div>
</div>
<div>
<h2>
<FormattedMessage
id="ListingPage.bookingTitle"
values={
Object {
"title": "listing1 title",
}
}
/>
</h2>
<div>
<FormattedMessage
id="ListingPage.bookingHelp"
values={Object {}}
/>
</div>
</div>
<BookingDatesForm
isOwnListing={false}
onSubmit={[Function]}
price={
Money {
"amount": 5500,
"currency": "USD",
}
}
/>
</withViewport(ModalInMobileComponent)>
<div>
<div>
<div
title={55}
onClick={[Function]}
>
55
</div>
<div>
<FormattedMessage
id="ListingPage.perNight"
values={Object {}}
<div
onClick={[Function]}
>
<ActionBar
editParams={
Object {
"id": "listing1",
"slug": "listing1-title",
"tab": "description",
"type": "edit",
}
}
isClosed={false}
isOwnListing={false}
/>
</div>
<ResponsiveImage
alt="listing1 title"
className={null}
image={null}
nameSet={
Array [
Object {
"name": "landscape-crop",
"size": "400w",
},
Object {
"name": "landscape-crop2x",
"size": "800w",
},
Object {
"name": "landscape-crop4x",
"size": "1600w",
},
Object {
"name": "landscape-crop6x",
"size": "2400w",
},
]
}
noImageMessage={null}
rootClassName={null}
sizes="100vw"
/>
</div>
</div>
<Button
className={null}
disabled={false}
inProgress={false}
onClick={[Function]}
ready={false}
rootClassName={null}
<InjectIntl(ModalComponent)
id="ListingPage.imageCarousel"
isOpen={false}
lightCloseButton={true}
onClose={[Function]}
onManageDisableScrolling={[Function]}
>
ListingPage.ctaButtonMessage
</Button>
<InjectIntl(ImageCarousel)
images={Array []}
/>
</InjectIntl(ModalComponent)>
<div>
<div>
<AvatarLarge
user={
Object {
"attributes": Object {
"banned": false,
"profile": Object {
"abbreviatedName": "user-1 abbreviated name",
"displayName": "user-1 display name",
},
},
"id": UUID {
"uuid": "user-1",
},
"type": "user",
}
}
/>
<AvatarMedium
user={
Object {
"attributes": Object {
"banned": false,
"profile": Object {
"abbreviatedName": "user-1 abbreviated name",
"displayName": "user-1 display name",
},
},
"id": UUID {
"uuid": "user-1",
},
"type": "user",
}
}
/>
</div>
<div>
<div>
<div>
<div
title={55}
>
55
</div>
<div>
<FormattedMessage
id="ListingPage.perNight"
values={Object {}}
/>
</div>
</div>
<div>
<h1>
listing1 title
</h1>
<div>
<span>
<FormattedMessage
id="ListingPage.hostedBy"
values={
Object {
"name": "user-1 display name",
}
}
/>
</span>
</div>
</div>
</div>
<div>
<h2>
<FormattedMessage
id="ListingPage.descriptionTitle"
values={Object {}}
/>
</h2>
<p>
listing1 description
</p>
</div>
<div>
<h2>
<FormattedMessage
id="ListingPage.locationTitle"
values={Object {}}
/>
</h2>
<div>
<Map
address="listing1 address"
center={
LatLng {
"lat": 40,
"lng": 60,
}
}
className=""
rootClassName={null}
zoom={11}
/>
</div>
</div>
</div>
<withViewport(ModalInMobileComponent)
id="BookingDatesFormInModal"
isModalOpenOnMobile={false}
onClose={[Function]}
onManageDisableScrolling={[Function]}
showAsModalMaxWidth={1023}
>
<div>
<h1>
listing1 title
</h1>
<div>
<span>
<FormattedMessage
id="ListingPage.hostedBy"
values={
Object {
"name": "user-1 display name",
}
}
/>
</span>
</div>
</div>
<div>
<h2>
<FormattedMessage
id="ListingPage.bookingTitle"
values={
Object {
"title": "listing1 title",
}
}
/>
</h2>
<div>
<FormattedMessage
id="ListingPage.bookingHelp"
values={Object {}}
/>
</div>
</div>
<BookingDatesForm
isOwnListing={false}
onSubmit={[Function]}
price={
Money {
"amount": 5500,
"currency": "USD",
}
}
/>
</withViewport(ModalInMobileComponent)>
<div>
<div>
<div
title={55}
>
55
</div>
<div>
<FormattedMessage
id="ListingPage.perNight"
values={Object {}}
/>
</div>
</div>
<Button
className={null}
disabled={false}
inProgress={false}
onClick={[Function]}
ready={false}
rootClassName={null}
>
ListingPage.ctaButtonMessage
</Button>
</div>
</div>
</div>
</div>
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter
className={null}
rootClassName={null}
>
<InjectIntl(Footer) />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
`;

View file

@ -6,7 +6,17 @@ import { FormattedMessage } from 'react-intl';
import * as propTypes from '../../util/propTypes';
import { parse } from '../../util/urlHelpers';
import { isScrollingDisabled } from '../../ducks/UI.duck';
import { ManageListingCard, Page, PaginationLinks, UserNav } from '../../components';
import {
ManageListingCard,
Page,
PaginationLinks,
UserNav,
LayoutSingleColumn,
LayoutWrapperTopbar,
LayoutWrapperMain,
LayoutWrapperFooter,
Footer,
} from '../../components';
import { TopbarContainer } from '../../containers';
import {
@ -110,30 +120,39 @@ export class ManageListingsPageComponent extends Component {
scrollingDisabled={scrollingDisabled}
title="Manage listings"
>
<TopbarContainer currentPage="ManageListingsPage" />
<UserNav selectedPageName="ManageListingsPage" />
{queryInProgress ? loadingResults : null}
{queryListingsError ? queryError : null}
<div className={css.listingPanel}>
{title}
<div className={css.listingCards}>
{listings.map(l => (
<ManageListingCard
className={css.listingCard}
key={l.id.uuid}
listing={l}
isMenuOpen={!!listingMenuOpen && listingMenuOpen.id.uuid === l.id.uuid}
actionsInProgressListingId={openingListing || closingListing}
onToggleMenu={this.onToggleMenu}
onCloseListing={onCloseListing}
onOpenListing={onOpenListing}
hasOpeningError={openingErrorListingId.uuid === l.id.uuid}
hasClosingError={closingErrorListingId.uuid === l.id.uuid}
/>
))}
</div>
{paginationLinks}
</div>
<LayoutSingleColumn>
<LayoutWrapperTopbar>
<TopbarContainer currentPage="ManageListingsPage" />
<UserNav selectedPageName="ManageListingsPage" />
</LayoutWrapperTopbar>
<LayoutWrapperMain>
{queryInProgress ? loadingResults : null}
{queryListingsError ? queryError : null}
<div className={css.listingPanel}>
{title}
<div className={css.listingCards}>
{listings.map(l => (
<ManageListingCard
className={css.listingCard}
key={l.id.uuid}
listing={l}
isMenuOpen={!!listingMenuOpen && listingMenuOpen.id.uuid === l.id.uuid}
actionsInProgressListingId={openingListing || closingListing}
onToggleMenu={this.onToggleMenu}
onCloseListing={onCloseListing}
onOpenListing={onOpenListing}
hasOpeningError={openingErrorListingId.uuid === l.id.uuid}
hasClosingError={closingErrorListingId.uuid === l.id.uuid}
/>
))}
</div>
{paginationLinks}
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter>
<Footer />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
);
}

View file

@ -7,16 +7,37 @@ exports[`ContactDetailsPage matches snapshot 1`] = `
scrollingDisabled={false}
title="Manage listings"
>
<withRouter(Connect(TopbarContainerComponent))
currentPage="ManageListingsPage"
/>
<UserNav
<LayoutSingleColumn
className={null}
rootClassName={null}
selectedPageName="ManageListingsPage"
/>
<div>
<div />
</div>
>
<LayoutWrapperTopbar
className={null}
rootClassName={null}
>
<withRouter(Connect(TopbarContainerComponent))
currentPage="ManageListingsPage"
/>
<UserNav
className={null}
rootClassName={null}
selectedPageName="ManageListingsPage"
/>
</LayoutWrapperTopbar>
<LayoutWrapperMain
className={null}
rootClassName={null}
>
<div>
<div />
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter
className={null}
rootClassName={null}
>
<InjectIntl(Footer) />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
`;

View file

@ -8,7 +8,14 @@ import routeConfiguration from '../../routeConfiguration';
import * as propTypes from '../../util/propTypes';
import { createResourceLocatorString } from '../../util/routes';
import { isScrollingDisabled } from '../../ducks/UI.duck';
import { Page } from '../../components';
import {
Page,
LayoutSingleColumn,
LayoutWrapperTopbar,
LayoutWrapperMain,
LayoutWrapperFooter,
Footer,
} from '../../components';
import { LocationSearchForm, TopbarContainer } from '../../containers';
import css from './NotFoundPage.css';
@ -45,19 +52,28 @@ export class NotFoundPageComponent extends Component {
title={title}
scrollingDisabled={scrollingDisabled}
>
<TopbarContainer />
<div className={css.root}>
<div className={css.content}>
<div className={css.number}>404</div>
<h1 className={css.heading}>
<FormattedMessage id="NotFoundPage.heading" />
</h1>
<p className={css.description}>
<FormattedMessage id="NotFoundPage.description" />
</p>
<LocationSearchForm className={css.searchForm} onSubmit={handleSearchSubmit} />
</div>
</div>
<LayoutSingleColumn>
<LayoutWrapperTopbar>
<TopbarContainer />
</LayoutWrapperTopbar>
<LayoutWrapperMain>
<div className={css.root}>
<div className={css.content}>
<div className={css.number}>404</div>
<h1 className={css.heading}>
<FormattedMessage id="NotFoundPage.heading" />
</h1>
<p className={css.description}>
<FormattedMessage id="NotFoundPage.description" />
</p>
<LocationSearchForm className={css.searchForm} onSubmit={handleSearchSubmit} />
</div>
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter>
<Footer />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
);
}

View file

@ -7,28 +7,49 @@ exports[`NotFoundPageComponent matches snapshot 1`] = `
scrollingDisabled={false}
title="NotFoundPage.title"
>
<withRouter(Connect(TopbarContainerComponent)) />
<div>
<div>
<LayoutSingleColumn
className={null}
rootClassName={null}
>
<LayoutWrapperTopbar
className={null}
rootClassName={null}
>
<withRouter(Connect(TopbarContainerComponent)) />
</LayoutWrapperTopbar>
<LayoutWrapperMain
className={null}
rootClassName={null}
>
<div>
404
<div>
<div>
404
</div>
<h1>
<FormattedMessage
id="NotFoundPage.heading"
values={Object {}}
/>
</h1>
<p>
<FormattedMessage
id="NotFoundPage.description"
values={Object {}}
/>
</p>
<ReduxForm
onSubmit={[Function]}
/>
</div>
</div>
<h1>
<FormattedMessage
id="NotFoundPage.heading"
values={Object {}}
/>
</h1>
<p>
<FormattedMessage
id="NotFoundPage.description"
values={Object {}}
/>
</p>
<ReduxForm
onSubmit={[Function]}
/>
</div>
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter
className={null}
rootClassName={null}
>
<InjectIntl(Footer) />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
`;

View file

@ -1,5 +1,9 @@
@import '../../marketplace.css';
.mainContent {
margin-bottom: 122px;
}
.loading {
margin-left: 24px;
margin-right: 24px;

View file

@ -8,7 +8,16 @@ import * as propTypes from '../../util/propTypes';
import { ensureListing, ensureTransaction } from '../../util/data';
import { getMarketplaceEntities } from '../../ducks/marketplaceData.duck';
import { isScrollingDisabled } from '../../ducks/UI.duck';
import { NamedRedirect, OrderDetailsPanel, Page } from '../../components';
import {
NamedRedirect,
OrderDetailsPanel,
Page,
LayoutSingleColumn,
LayoutWrapperTopbar,
LayoutWrapperMain,
LayoutWrapperFooter,
Footer,
} from '../../components';
import { TopbarContainer } from '../../containers';
import { loadData } from './OrderPage.duck';
@ -73,8 +82,15 @@ export const OrderPageComponent = props => {
title={intl.formatMessage({ id: 'OrderPage.title' }, { listingTitle })}
scrollingDisabled={scrollingDisabled}
>
<TopbarContainer />
{panel}
<LayoutSingleColumn>
<LayoutWrapperTopbar>
<TopbarContainer />
</LayoutWrapperTopbar>
<LayoutWrapperMain className={css.mainContent}>{panel}</LayoutWrapperMain>
<LayoutWrapperFooter>
<Footer />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
);
};

View file

@ -7,114 +7,135 @@ exports[`OrderPage matches snapshot 1`] = `
scrollingDisabled={false}
title="OrderPage.title"
>
<withRouter(Connect(TopbarContainerComponent)) />
<InjectIntl(Component)
className="undefined"
transaction={
Object {
"attributes": Object {
"createdAt": 2017-05-01T00:00:00.000Z,
"lastTransition": "transition/preauthorize",
"lastTransitionedAt": 2017-06-01T00:00:00.000Z,
"lineItems": Array [
Object {
"code": "line-item/night",
"lineTotal": Money {
<LayoutSingleColumn
className={null}
rootClassName={null}
>
<LayoutWrapperTopbar
className={null}
rootClassName={null}
>
<withRouter(Connect(TopbarContainerComponent)) />
</LayoutWrapperTopbar>
<LayoutWrapperMain
className={null}
rootClassName={null}
>
<InjectIntl(Component)
className="undefined"
transaction={
Object {
"attributes": Object {
"createdAt": 2017-05-01T00:00:00.000Z,
"lastTransition": "transition/preauthorize",
"lastTransitionedAt": 2017-06-01T00:00:00.000Z,
"lineItems": Array [
Object {
"code": "line-item/night",
"lineTotal": Money {
"amount": 1000,
"currency": "USD",
},
"quantity": "3",
"reversal": false,
"unitPrice": Money {
"amount": 333.3333333333333,
"currency": "USD",
},
},
Object {
"code": "line-item/provider-commission",
"lineTotal": Money {
"amount": -100,
"currency": "USD",
},
"reversal": false,
"unitPrice": Money {
"amount": -100,
"currency": "USD",
},
},
],
"payinTotal": Money {
"amount": 1000,
"currency": "USD",
},
"quantity": "3",
"reversal": false,
"unitPrice": Money {
"amount": 333.3333333333333,
"payoutTotal": Money {
"amount": 900,
"currency": "USD",
},
},
Object {
"code": "line-item/provider-commission",
"lineTotal": Money {
"amount": -100,
"currency": "USD",
"booking": Object {
"attributes": Object {
"end": 2017-06-13T00:00:00.000Z,
"start": 2017-06-10T00:00:00.000Z,
},
"reversal": false,
"unitPrice": Money {
"amount": -100,
"currency": "USD",
"id": UUID {
"uuid": "booking1",
},
"type": "booking",
},
],
"payinTotal": Money {
"amount": 1000,
"currency": "USD",
},
"payoutTotal": Money {
"amount": 900,
"currency": "USD",
},
},
"booking": Object {
"attributes": Object {
"end": 2017-06-13T00:00:00.000Z,
"start": 2017-06-10T00:00:00.000Z,
},
"id": UUID {
"uuid": "booking1",
},
"type": "booking",
},
"customer": Object {
"attributes": Object {
"banned": false,
"profile": Object {
"abbreviatedName": "customer1 abbreviated name",
"displayName": "customer1 display name",
"customer": Object {
"attributes": Object {
"banned": false,
"profile": Object {
"abbreviatedName": "customer1 abbreviated name",
"displayName": "customer1 display name",
},
},
"id": UUID {
"uuid": "customer1",
},
"type": "user",
},
},
"id": UUID {
"uuid": "customer1",
},
"type": "user",
},
"id": UUID {
"uuid": "tx-order-1",
},
"listing": Object {
"attributes": Object {
"address": "listing1 address",
"closed": false,
"deleted": false,
"description": "listing1 description",
"geolocation": LatLng {
"lat": 40,
"lng": 60,
"id": UUID {
"uuid": "tx-order-1",
},
"price": Money {
"amount": 5500,
"currency": "USD",
"listing": Object {
"attributes": Object {
"address": "listing1 address",
"closed": false,
"deleted": false,
"description": "listing1 description",
"geolocation": LatLng {
"lat": 40,
"lng": 60,
},
"price": Money {
"amount": 5500,
"currency": "USD",
},
"title": "listing1 title",
},
"id": UUID {
"uuid": "listing1",
},
"type": "listing",
},
"title": "listing1 title",
},
"id": UUID {
"uuid": "listing1",
},
"type": "listing",
},
"provider": Object {
"attributes": Object {
"banned": false,
"profile": Object {
"abbreviatedName": "provider1 abbreviated name",
"displayName": "provider1 display name",
"provider": Object {
"attributes": Object {
"banned": false,
"profile": Object {
"abbreviatedName": "provider1 abbreviated name",
"displayName": "provider1 display name",
},
},
"id": UUID {
"uuid": "provider1",
},
"type": "user",
},
},
"id": UUID {
"uuid": "provider1",
},
"type": "user",
},
"type": "transaction",
}
}
/>
"type": "transaction",
}
}
/>
</LayoutWrapperMain>
<LayoutWrapperFooter
className={null}
rootClassName={null}
>
<InjectIntl(Footer) />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
`;

View file

@ -10,6 +10,8 @@ import {
LayoutWrapperMain,
LayoutWrapperSideNav,
LayoutWrapperTopbar,
LayoutWrapperFooter,
Footer,
Page,
TabNav,
UserNav,
@ -89,6 +91,9 @@ export const PasswordChangePageComponent = props => {
{changePasswordForm}
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter>
<Footer />
</LayoutWrapperFooter>
</LayoutSideNavigation>
</Page>
);

View file

@ -73,6 +73,12 @@ exports[`PasswordChangePage matches snapshot 1`] = `
</h1>
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter
className={null}
rootClassName={null}
>
<InjectIntl(Footer) />
</LayoutWrapperFooter>
</LayoutSideNavigation>
</Page>
`;

View file

@ -4,6 +4,14 @@
@apply --marketplaceModalRootStyles;
}
.layoutWrapperMain {
min-height: calc(100vh - var(--topbarHeight));
@media (--viewportMedium) {
min-height: calc(100vh - var(--topbarHeightDesktop));
}
}
.submitEmailContent,
.genericErrorContent,
.emailSubmittedContent,

View file

@ -9,7 +9,16 @@ import {
isPasswordRecoveryEmailNotVerifiedError,
} from '../../util/errors';
import { isScrollingDisabled } from '../../ducks/UI.duck';
import { Page, InlineTextButton, IconKeys } from '../../components';
import {
Page,
InlineTextButton,
IconKeys,
LayoutSingleColumn,
LayoutWrapperMain,
LayoutWrapperTopbar,
LayoutWrapperFooter,
Footer,
} from '../../components';
import { PasswordRecoveryForm, TopbarContainer } from '../../containers';
import {
@ -152,8 +161,17 @@ export const PasswordRecoveryPageComponent = props => {
return (
<Page authInfoError={authInfoError} logoutError={logoutError} title={title}>
<TopbarContainer />
<div className={css.root}>{content}</div>
<LayoutSingleColumn>
<LayoutWrapperTopbar>
<TopbarContainer />
</LayoutWrapperTopbar>
<LayoutWrapperMain className={css.layoutWrapperMain}>
<div className={css.root}>{content}</div>
</LayoutWrapperMain>
<LayoutWrapperFooter>
<Footer />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
);
};

View file

@ -6,36 +6,57 @@ exports[`ContactDetailsPage matches snapshot 1`] = `
logoutError={null}
title="PasswordRecoveryPage.title"
>
<withRouter(Connect(TopbarContainerComponent)) />
<div>
<div>
<IconKeys
className={null}
/>
<h1>
<FormattedMessage
id="PasswordRecoveryPage.forgotPasswordTitle"
values={Object {}}
/>
</h1>
<p>
<FormattedMessage
id="PasswordRecoveryPage.forgotPasswordMessage"
values={Object {}}
/>
</p>
<ReduxForm
inProgress={false}
initialValues={
Object {
"email": null,
}
}
onChange={[Function]}
onSubmit={[Function]}
recoveryError={null}
/>
</div>
</div>
<LayoutSingleColumn
className={null}
rootClassName={null}
>
<LayoutWrapperTopbar
className={null}
rootClassName={null}
>
<withRouter(Connect(TopbarContainerComponent)) />
</LayoutWrapperTopbar>
<LayoutWrapperMain
className={null}
rootClassName={null}
>
<div>
<div>
<IconKeys
className={null}
/>
<h1>
<FormattedMessage
id="PasswordRecoveryPage.forgotPasswordTitle"
values={Object {}}
/>
</h1>
<p>
<FormattedMessage
id="PasswordRecoveryPage.forgotPasswordMessage"
values={Object {}}
/>
</p>
<ReduxForm
inProgress={false}
initialValues={
Object {
"email": null,
}
}
onChange={[Function]}
onSubmit={[Function]}
recoveryError={null}
/>
</div>
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter
className={null}
rootClassName={null}
>
<InjectIntl(Footer) />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
`;

View file

@ -1,5 +1,13 @@
@import '../../marketplace.css';
.layoutWrapperMain {
min-height: calc(100vh - var(--topbarHeight));
@media (--viewportMedium) {
min-height: calc(100vh - var(--topbarHeightDesktop));
}
}
.root {
@apply --marketplaceModalRootStyles;
}

View file

@ -7,7 +7,17 @@ import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
import * as propTypes from '../../util/propTypes';
import { parse } from '../../util/urlHelpers';
import { isScrollingDisabled } from '../../ducks/UI.duck';
import { Page, NamedLink, IconKeys, IconKeysSuccess } from '../../components';
import {
Page,
NamedLink,
IconKeys,
IconKeysSuccess,
LayoutSingleColumn,
LayoutWrapperTopbar,
LayoutWrapperMain,
LayoutWrapperFooter,
Footer,
} from '../../components';
import { PasswordResetForm, TopbarContainer } from '../../containers';
import { resetPassword } from './PasswordResetPage.duck';
@ -118,8 +128,17 @@ export class PasswordResetPageComponent extends Component {
logoutError={logoutError}
scrollingDisabled={scrollingDisabled}
>
<TopbarContainer />
<div className={css.root}>{content}</div>
<LayoutSingleColumn>
<LayoutWrapperTopbar>
<TopbarContainer />
</LayoutWrapperTopbar>
<LayoutWrapperMain className={css.layoutWrapperMain}>
<div className={css.root}>{content}</div>
</LayoutWrapperMain>
<LayoutWrapperFooter>
<Footer />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
);
}

View file

@ -4,7 +4,14 @@ import { compose } from 'redux';
import { connect } from 'react-redux';
import * as propTypes from '../../util/propTypes';
import { isScrollingDisabled } from '../../ducks/UI.duck';
import { Page } from '../../components';
import {
Page,
LayoutSingleColumn,
LayoutWrapperTopbar,
LayoutWrapperMain,
LayoutWrapperFooter,
Footer,
} from '../../components';
import { TopbarContainer } from '../../containers';
export const PayoutPreferencesPageComponent = props => {
@ -17,7 +24,15 @@ export const PayoutPreferencesPageComponent = props => {
title="Payout preferences"
scrollingDisabled={scrollingDisabled}
>
<TopbarContainer />
<LayoutSingleColumn>
<LayoutWrapperTopbar>
<TopbarContainer />
</LayoutWrapperTopbar>
<LayoutWrapperMain>Content</LayoutWrapperMain>
<LayoutWrapperFooter>
<Footer />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
);
};

View file

@ -7,6 +7,28 @@ exports[`PayoutPreferencesPage matches snapshot 1`] = `
scrollingDisabled={false}
title="Payout preferences"
>
<withRouter(Connect(TopbarContainerComponent)) />
<LayoutSingleColumn
className={null}
rootClassName={null}
>
<LayoutWrapperTopbar
className={null}
rootClassName={null}
>
<withRouter(Connect(TopbarContainerComponent)) />
</LayoutWrapperTopbar>
<LayoutWrapperMain
className={null}
rootClassName={null}
>
Content
</LayoutWrapperMain>
<LayoutWrapperFooter
className={null}
rootClassName={null}
>
<InjectIntl(Footer) />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
`;

View file

@ -5,7 +5,7 @@
}
.content {
width: calc(100% - 48px);
margin: 12px 24px;
margin: 12px 24px 24px 24px;
@media (--viewportMedium) {
max-width: 565px;

View file

@ -6,7 +6,15 @@ import { FormattedMessage } from 'react-intl';
import * as propTypes from '../../util/propTypes';
import { ensureCurrentUser } from '../../util/data';
import { isScrollingDisabled } from '../../ducks/UI.duck';
import { Page, UserNav } from '../../components';
import {
Page,
UserNav,
LayoutSingleColumn,
LayoutWrapperTopbar,
LayoutWrapperMain,
LayoutWrapperFooter,
Footer,
} from '../../components';
import { ProfileSettingsForm, TopbarContainer } from '../../containers';
import { clearUpdatedForm, updateProfile, uploadImage } from './ProfileSettingsPage.duck';
@ -91,15 +99,23 @@ export class ProfileSettingsPageComponent extends Component {
title="Profile settings"
scrollingDisabled={scrollingDisabled}
>
<TopbarContainer currentPage="ProfileSettingsPage" />
<UserNav selectedPageName="ProfileSettingsPage" />
<div className={css.content}>
<h1>
<FormattedMessage id="ProfileSettingsPage.title" />
</h1>
{profileSettingsForm}
</div>
<LayoutSingleColumn>
<LayoutWrapperTopbar>
<TopbarContainer currentPage="ProfileSettingsPage" />
<UserNav selectedPageName="ProfileSettingsPage" />
</LayoutWrapperTopbar>
<LayoutWrapperMain>
<div className={css.content}>
<h1>
<FormattedMessage id="ProfileSettingsPage.title" />
</h1>
{profileSettingsForm}
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter>
<Footer />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
);
}

View file

@ -7,21 +7,42 @@ exports[`ContactDetailsPage matches snapshot 1`] = `
scrollingDisabled={false}
title="Profile settings"
>
<withRouter(Connect(TopbarContainerComponent))
currentPage="ProfileSettingsPage"
/>
<UserNav
<LayoutSingleColumn
className={null}
rootClassName={null}
selectedPageName="ProfileSettingsPage"
/>
<div>
<h1>
<FormattedMessage
id="ProfileSettingsPage.title"
values={Object {}}
>
<LayoutWrapperTopbar
className={null}
rootClassName={null}
>
<withRouter(Connect(TopbarContainerComponent))
currentPage="ProfileSettingsPage"
/>
</h1>
</div>
<UserNav
className={null}
rootClassName={null}
selectedPageName="ProfileSettingsPage"
/>
</LayoutWrapperTopbar>
<LayoutWrapperMain
className={null}
rootClassName={null}
>
<div>
<h1>
<FormattedMessage
id="ProfileSettingsPage.title"
values={Object {}}
/>
</h1>
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter
className={null}
rootClassName={null}
>
<InjectIntl(Footer) />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
`;

View file

@ -6,6 +6,15 @@
flex: 1;
}
/* actionButtons are fixed positioned until viewportLarge is reached */
/* margin-bottom gives space for those buttons when scrolling to bottom */
.footerWrapper {
margin-bottom: 101px;
@media (--viewportLarge) {
margin-bottom: 0;
}
}
.loading {
margin-left: 24px;
margin-right: 24px;

View file

@ -5,11 +5,20 @@ import { connect } from 'react-redux';
import classNames from 'classnames';
import { FormattedMessage, intlShape, injectIntl } from 'react-intl';
import * as propTypes from '../../util/propTypes';
import { ensureListing, ensureTransaction } from '../../util/data';
import { ensureListing, ensureUser, ensureTransaction } from '../../util/data';
import { getMarketplaceEntities } from '../../ducks/marketplaceData.duck';
import { isScrollingDisabled } from '../../ducks/UI.duck';
import { acceptSale, declineSale, loadData } from './SalePage.duck';
import { NamedRedirect, SaleDetailsPanel, Page } from '../../components';
import {
NamedRedirect,
SaleDetailsPanel,
Page,
LayoutSingleColumn,
LayoutWrapperTopbar,
LayoutWrapperMain,
LayoutWrapperFooter,
Footer,
} from '../../components';
import { TopbarContainer } from '../../containers';
import css from './SalePage.css';
@ -64,6 +73,14 @@ export const SalePageComponent = props => {
</p>
);
// We need to know if the customer is in pending state -
// to show action buttons bar and reserve space below footer.
// (accept reject buttons are position: fixed on mobile layout)
const currentCustomer = ensureUser(currentTransaction.customer);
const customerLoaded = !!currentCustomer.id;
const isCustomerBanned = customerLoaded || currentCustomer.attributes.banned;
const isPending = propTypes.txIsPreauthorized(currentTransaction) && !isCustomerBanned;
const panel =
isDataAvailable && currentTransaction.id ? (
<SaleDetailsPanel
@ -87,8 +104,17 @@ export const SalePageComponent = props => {
title={intl.formatMessage({ id: 'SalePage.title' }, { title: listingTitle })}
scrollingDisabled={scrollingDisabled}
>
<TopbarContainer />
<div className={css.root}>{panel}</div>
<LayoutSingleColumn>
<LayoutWrapperTopbar>
<TopbarContainer />
</LayoutWrapperTopbar>
<LayoutWrapperMain>
<div className={css.root}>{panel}</div>
</LayoutWrapperMain>
<LayoutWrapperFooter className={classNames({ [css.footerWrapper]: isPending })}>
<Footer />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
);
};

View file

@ -7,122 +7,143 @@ exports[`SalePage matches snapshot 1`] = `
scrollingDisabled={false}
title="SalePage.title"
>
<withRouter(Connect(TopbarContainerComponent)) />
<div>
<InjectIntl(Component)
acceptInProgress={false}
acceptSaleError={null}
className="undefined"
declineInProgress={false}
declineSaleError={null}
onAcceptSale={[Function]}
onDeclineSale={[Function]}
transaction={
Object {
"attributes": Object {
"createdAt": 2017-05-01T00:00:00.000Z,
"lastTransition": "transition/preauthorize",
"lastTransitionedAt": 2017-06-01T00:00:00.000Z,
"lineItems": Array [
Object {
"code": "line-item/night",
"lineTotal": Money {
<LayoutSingleColumn
className={null}
rootClassName={null}
>
<LayoutWrapperTopbar
className={null}
rootClassName={null}
>
<withRouter(Connect(TopbarContainerComponent)) />
</LayoutWrapperTopbar>
<LayoutWrapperMain
className={null}
rootClassName={null}
>
<div>
<InjectIntl(Component)
acceptInProgress={false}
acceptSaleError={null}
className="undefined"
declineInProgress={false}
declineSaleError={null}
onAcceptSale={[Function]}
onDeclineSale={[Function]}
transaction={
Object {
"attributes": Object {
"createdAt": 2017-05-01T00:00:00.000Z,
"lastTransition": "transition/preauthorize",
"lastTransitionedAt": 2017-06-01T00:00:00.000Z,
"lineItems": Array [
Object {
"code": "line-item/night",
"lineTotal": Money {
"amount": 1000,
"currency": "USD",
},
"quantity": "3",
"reversal": false,
"unitPrice": Money {
"amount": 333.3333333333333,
"currency": "USD",
},
},
Object {
"code": "line-item/provider-commission",
"lineTotal": Money {
"amount": -100,
"currency": "USD",
},
"reversal": false,
"unitPrice": Money {
"amount": -100,
"currency": "USD",
},
},
],
"payinTotal": Money {
"amount": 1000,
"currency": "USD",
},
"quantity": "3",
"reversal": false,
"unitPrice": Money {
"amount": 333.3333333333333,
"payoutTotal": Money {
"amount": 900,
"currency": "USD",
},
},
Object {
"code": "line-item/provider-commission",
"lineTotal": Money {
"amount": -100,
"currency": "USD",
"booking": Object {
"attributes": Object {
"end": 2017-06-13T00:00:00.000Z,
"start": 2017-06-10T00:00:00.000Z,
},
"reversal": false,
"unitPrice": Money {
"amount": -100,
"currency": "USD",
"id": UUID {
"uuid": "booking1",
},
"type": "booking",
},
],
"payinTotal": Money {
"amount": 1000,
"currency": "USD",
},
"payoutTotal": Money {
"amount": 900,
"currency": "USD",
},
},
"booking": Object {
"attributes": Object {
"end": 2017-06-13T00:00:00.000Z,
"start": 2017-06-10T00:00:00.000Z,
},
"id": UUID {
"uuid": "booking1",
},
"type": "booking",
},
"customer": Object {
"attributes": Object {
"banned": false,
"profile": Object {
"abbreviatedName": "customer1 abbreviated name",
"displayName": "customer1 display name",
"customer": Object {
"attributes": Object {
"banned": false,
"profile": Object {
"abbreviatedName": "customer1 abbreviated name",
"displayName": "customer1 display name",
},
},
"id": UUID {
"uuid": "customer1",
},
"type": "user",
},
},
"id": UUID {
"uuid": "customer1",
},
"type": "user",
},
"id": UUID {
"uuid": "tx-sale-1",
},
"listing": Object {
"attributes": Object {
"address": "listing1 address",
"closed": false,
"deleted": false,
"description": "listing1 description",
"geolocation": LatLng {
"lat": 40,
"lng": 60,
"id": UUID {
"uuid": "tx-sale-1",
},
"price": Money {
"amount": 5500,
"currency": "USD",
"listing": Object {
"attributes": Object {
"address": "listing1 address",
"closed": false,
"deleted": false,
"description": "listing1 description",
"geolocation": LatLng {
"lat": 40,
"lng": 60,
},
"price": Money {
"amount": 5500,
"currency": "USD",
},
"title": "listing1 title",
},
"id": UUID {
"uuid": "listing1",
},
"type": "listing",
},
"title": "listing1 title",
},
"id": UUID {
"uuid": "listing1",
},
"type": "listing",
},
"provider": Object {
"attributes": Object {
"banned": false,
"profile": Object {
"abbreviatedName": "provider1 abbreviated name",
"displayName": "provider1 display name",
"provider": Object {
"attributes": Object {
"banned": false,
"profile": Object {
"abbreviatedName": "provider1 abbreviated name",
"displayName": "provider1 display name",
},
},
"id": UUID {
"uuid": "provider1",
},
"type": "user",
},
},
"id": UUID {
"uuid": "provider1",
},
"type": "user",
},
"type": "transaction",
}
}
/>
</div>
"type": "transaction",
}
}
/>
</div>
</LayoutWrapperMain>
<LayoutWrapperFooter
className=""
rootClassName={null}
>
<InjectIntl(Footer) />
</LayoutWrapperFooter>
</LayoutSingleColumn>
</Page>
`;

View file

@ -1,50 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { compose } from 'redux';
import { connect } from 'react-redux';
import * as propTypes from '../../util/propTypes';
import { isScrollingDisabled } from '../../ducks/UI.duck';
import { Page } from '../../components';
import { TopbarContainer } from '../../containers';
export const SecurityPageComponent = props => {
const { authInfoError, logoutError, scrollingDisabled } = props;
return (
<Page
authInfoError={authInfoError}
logoutError={logoutError}
title="Security"
scrollingDisabled={scrollingDisabled}
>
<TopbarContainer />
</Page>
);
};
SecurityPageComponent.defaultProps = {
authInfoError: null,
logoutError: null,
};
const { bool } = PropTypes;
SecurityPageComponent.propTypes = {
authInfoError: propTypes.error,
logoutError: propTypes.error,
scrollingDisabled: bool.isRequired,
};
const mapStateToProps = state => {
// Page needs authInfoError and logoutError
const { authInfoError, logoutError } = state.Auth;
return {
authInfoError,
logoutError,
scrollingDisabled: isScrollingDisabled(state),
};
};
const SecurityPage = compose(connect(mapStateToProps))(SecurityPageComponent);
export default SecurityPage;

View file

@ -1,26 +0,0 @@
import React from 'react';
import { renderShallow } from '../../util/test-helpers';
import { SecurityPageComponent } from './SecurityPage';
const noop = () => null;
describe('SecurityPage', () => {
it('matches snapshot', () => {
const tree = renderShallow(
<SecurityPageComponent
params={{ displayName: 'my-shop' }}
history={{ push: noop }}
location={{ search: '' }}
scrollingDisabled={false}
authInProgress={false}
currentUserHasListings={false}
isAuthenticated={false}
onLogout={noop}
onManageDisableScrolling={noop}
sendVerificationEmailInProgress={false}
onResendVerificationEmail={noop}
/>
);
expect(tree).toMatchSnapshot();
});
});

View file

@ -1,12 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SecurityPage matches snapshot 1`] = `
<Page
authInfoError={null}
logoutError={null}
scrollingDisabled={false}
title="Security"
>
<withRouter(Connect(TopbarContainerComponent)) />
</Page>
`;

View file

@ -35,7 +35,6 @@ export { default as ProfileSettingsForm } from './ProfileSettingsForm/ProfileSet
export { default as ProfileSettingsPage } from './ProfileSettingsPage/ProfileSettingsPage';
export { default as SalePage } from './SalePage/SalePage';
export { default as SearchPage } from './SearchPage/SearchPage';
export { default as SecurityPage } from './SecurityPage/SecurityPage';
export { default as SignupForm } from './SignupForm/SignupForm';
export { default as StaticPage } from './StaticPage/StaticPage';
export { default as StripePaymentForm } from './StripePaymentForm/StripePaymentForm';

View file

@ -18,7 +18,6 @@ import {
ProfileSettingsPage,
SalePage,
SearchPage,
SecurityPage,
StyleguidePage,
EmailVerificationPage,
} from './containers';
@ -232,12 +231,6 @@ const routeConfiguration = () => {
name: 'PayoutPreferencesPage',
component: props => <PayoutPreferencesPage {...props} />,
},
{
path: '/account/security',
auth: true,
name: 'SecurityPage',
component: props => <SecurityPage {...props} />,
},
{
path: '/styleguide',
name: 'Styleguide',