From 60d5a44d5d9b24fdc5c11ec61ad30bc7e9c58b2d Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Tue, 4 Dec 2018 14:55:59 +0200 Subject: [PATCH 01/15] Extract SectionBooking into a distinct component Extract SectionBooking away from ListingPage to a distinct component called BookingPanel. --- src/components/BookingPanel/BookingPanel.css | 207 +++++++++++++++++ src/components/BookingPanel/BookingPanel.js | 192 ++++++++++++++++ src/components/index.js | 1 + src/containers/ListingPage/ListingPage.css | 214 ++---------------- src/containers/ListingPage/ListingPage.js | 56 +---- src/containers/ListingPage/SectionBooking.js | 113 --------- .../__snapshots__/ListingPage.test.js.snap | 5 +- 7 files changed, 424 insertions(+), 364 deletions(-) create mode 100644 src/components/BookingPanel/BookingPanel.css create mode 100644 src/components/BookingPanel/BookingPanel.js delete mode 100644 src/containers/ListingPage/SectionBooking.js diff --git a/src/components/BookingPanel/BookingPanel.css b/src/components/BookingPanel/BookingPanel.css new file mode 100644 index 00000000..4d0e58e3 --- /dev/null +++ b/src/components/BookingPanel/BookingPanel.css @@ -0,0 +1,207 @@ +@import '../../marketplace.css'; + +/* Booking form inside modalContainer needs special handling */ +/* since "inMobile" breakpoint is actually --viewportLarge */ +.modalContainer { + @apply --marketplaceModalInMobileBaseStyles; + + height: 100vh; + + @media (--viewportMedium) { + flex-basis: 576px; + height: 100%; + padding: var(--modalPaddingMedium); + background-color: var(--matterColorLight); + margin-top: 12.5vh; + margin-bottom: 12.5vh; + } + + @media (--viewportLarge) { + padding: 0; + background-color: transparent; + margin-top: 0; + margin-bottom: 0; + } +} + +.modalHeading { + margin-top: 89px; + margin-bottom: 36px; + padding: 0 24px; + + @media (--viewportMedium) { + padding: 0; + } + + @media (--viewportLarge) { + display: none; + } +} + +.title { + /* Font */ + @apply --marketplaceH1FontStyles; + color: var(--matterColor); + + /* Layout */ + width: 100%; + margin-top: 0; + margin-bottom: 0; + @media (--viewportMedium) { + margin-top: 0; + margin-bottom: 5px; + } +} + +.author { + width: 100%; + @apply --marketplaceH4FontStyles; + margin-top: 7px; + margin-bottom: 0; + + @media (--viewportMedium) { + margin-top: 0; + margin-bottom: 0; + color: var(--matterColor); + } +} + +.bookingHeading { + display: none; + + @media (--viewportLarge) { + display: block; + margin-top: 6px; + margin-bottom: 47px; + } +} + +.bookingTitle { + @media (--viewportMedium) { + /* Font */ + color: var(--matterColor); + + margin-top: 0; + margin-bottom: 9px; + } +} + +.bookingHelp { + display: none; + @apply --marketplaceH5FontStyles; + + @media (--viewportMedium) { + color: var(--matterColor); + display: block; + margin-top: 0; + } +} + +.bookingForm { + flex-grow: 1; + display: flex; + flex-direction: column; + margin: 0 0 84px 0; + + @media (--viewportMedium) { + margin: 0; + min-height: 400px; + } + + @media (--viewportLarge) { + min-width: 311px; + min-height: auto; + } +} + +.bookingDatesSubmitButtonWrapper { + flex-shrink: 0; + padding: 0 24px 24px 24px; + width: 100%; + position: fixed; + bottom: 0; + background-color: var(--matterColorLight); + + @media (--viewportMedium) { + padding: 0; + width: 100%; + position: static; + bottom: unset; + background-color: transparent; + } +} + +.openBookingForm { + /* Ensure that mobile button is over Footer too */ + z-index: 9; + position: fixed; + bottom: 0; + left: 0; + width: 100vw; + padding: 18px 24px 18px 16px; + background-color: var(--matterColorLight); + box-shadow: var(--boxShadowTop); + display: flex; + + /* Contain repainting to this component only */ + /* 3D painting container helps scrolling */ + transform: translate3d(0, 0, 0); + + @media (--viewportMedium) { + padding: 18px 60px 18px 60px; + } + + @media (--viewportLarge) { + display: none; + } +} + +.priceContainer { + /* Layout */ + display: flex; + flex-direction: column; + align-items: center; + flex-shrink: 0; + margin-right: 22px; + padding: 5px 12px; +} + +.priceValue { + /* Font */ + @apply --marketplaceH2FontStyles; + color: var(--matterColor); + + margin-top: 0; + margin-bottom: 0px; + @media (--viewportMedium) { + margin-top: 0; + margin-bottom: 0; + } +} + +.perUnit { + /* Font */ + @apply --marketplaceTinyFontStyles; + color: var(--matterColor); + + margin-top: 0; + margin-bottom: 0px; + @media (--viewportMedium) { + margin-top: 0; + margin-bottom: 0; + } +} + +.bookButton { + @apply --marketplaceButtonStylesPrimary; + + /* Clear padding that is set for link elements looking like buttons */ + padding: 0; +} + +.closedListingButton { + border-left: 1px solid var(--matterColorNegative); + width: 100%; + padding: 15px 24px 15px 24px; + text-align: center; +} diff --git a/src/components/BookingPanel/BookingPanel.js b/src/components/BookingPanel/BookingPanel.js new file mode 100644 index 00000000..51141e9a --- /dev/null +++ b/src/components/BookingPanel/BookingPanel.js @@ -0,0 +1,192 @@ +import React from 'react'; +import { compose } from 'redux'; +import { withRouter } from 'react-router-dom'; +import { intlShape, injectIntl } from 'react-intl'; +import { arrayOf, bool, func, object, shape, string } from 'prop-types'; +import { FormattedMessage } from 'react-intl'; +import classNames from 'classnames'; +import omit from 'lodash/omit'; +import { ModalInMobile, Button } from '../../components'; +import { BookingDatesForm } from '../../forms'; +import { propTypes } from '../../util/types'; +import { formatMoney } from '../../util/currency'; +import { parse, stringify } from '../../util/urlHelpers'; +import config from '../../config'; + +import css from './BookingPanel.css'; + +// This defines when ModalInMobile shows content as Modal +const MODAL_BREAKPOINT = 1023; + +const priceData = (price, intl) => { + if (price && price.currency === config.currency) { + const formattedPrice = formatMoney(intl, price); + return { formattedPrice, priceTitle: formattedPrice }; + } else if (price) { + return { + formattedPrice: `(${price.currency})`, + priceTitle: `Unsupported currency (${price.currency})`, + }; + } + return {}; +}; + +const openBookModal = (isOwnListing, isClosed, history, location) => { + if (isOwnListing || isClosed) { + window.scrollTo(0, 0); + } else { + const { pathname, search, state } = location; + const searchString = `?${stringify({ ...parse(search), book: true })}`; + history.push(`${pathname}${searchString}`, state); + } +}; + +const closeBookModal = (history, location) => { + const { pathname, search, state } = location; + const searchParams = omit(parse(search), 'book'); + const searchString = `?${stringify(searchParams)}`; + history.push(`${pathname}${searchString}`, state); +}; + +const BookingPanel = props => { + const { + rootClassName, + className, + listing, + isOwnListing, + isClosed, + unitType, + price, + handleBookingSubmit, + richTitle, + authorDisplayName, + onManageDisableScrolling, + timeSlots, + fetchTimeSlotsError, + history, + location, + intl, + } = props; + + const showClosedListingHelpText = listing.id && isClosed; + const { formattedPrice, priceTitle } = priceData(price, intl); + const isBook = !!parse(location.search).book; + + const classes = classNames(rootClassName || css.root, className); + + return ( +
+ closeBookModal(history, location)} + showAsModalMaxWidth={MODAL_BREAKPOINT} + onManageDisableScrolling={onManageDisableScrolling} + > +
+

{richTitle}

+
+ { + // TOOD: check is class is required + } + + + +
+
+ +
+

+ +

+
+ +
+
+ {!isClosed ? ( + + ) : null} +
+
+
+
+ {formattedPrice} +
+
+ +
+
+ + {!isClosed ? ( + + ) : ( +
+ +
+ )} +
+
+ ); +}; + +BookingPanel.defaultProps = { + rootClassName: null, + className: null, + isOwnListing: false, + unitType: config.bookingUnitType, + timeSlots: null, + fetchTimeSlotsError: null, +}; + +BookingPanel.propTypes = { + rootClassName: string, + className: string, + listing: propTypes.listing.isRequired, + isOwnListing: bool, + isClosed: bool.isRequired, + unitType: propTypes.bookingUnitType, + price: propTypes.money.isRequired, + handleBookingSubmit: func.isRequired, + richTitle: object, + authorDisplayName: string.isRequired, + onManageDisableScrolling: func.isRequired, + timeSlots: arrayOf(propTypes.timeSlot), + fetchTimeSlotsError: propTypes.error, + + // from withRouter + history: shape({ + push: func.isRequired, + }).isRequired, + location: shape({ + search: string, + }).isRequired, + + // from injectIntl + intl: intlShape.isRequired, +}; + +export default compose( + withRouter, + injectIntl +)(BookingPanel); diff --git a/src/components/index.js b/src/components/index.js index a59280c9..3a3e195f 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -3,6 +3,7 @@ export { default as AddImages } from './AddImages/AddImages'; export { default as Avatar, AvatarMedium, AvatarLarge } from './Avatar/Avatar'; export { default as BookingBreakdown } from './BookingBreakdown/BookingBreakdown'; export { default as Button, PrimaryButton, SecondaryButton, InlineTextButton } from './Button/Button'; +export { default as BookingPanel } from './BookingPanel/BookingPanel'; export { default as CookieConsent } from './CookieConsent/CookieConsent'; export { default as Discussion } from './Discussion/Discussion'; export { default as EditListingDescriptionPanel } from './EditListingDescriptionPanel/EditListingDescriptionPanel'; diff --git a/src/containers/ListingPage/ListingPage.css b/src/containers/ListingPage/ListingPage.css index 62410f22..8084867d 100644 --- a/src/containers/ListingPage/ListingPage.css +++ b/src/containers/ListingPage/ListingPage.css @@ -1,5 +1,8 @@ @import '../../marketplace.css'; +.root { +} + .pageRoot { display: flex; padding-bottom: 90px; @@ -219,6 +222,23 @@ } } +.bookingPanel { + @media (--viewportLarge) { + display: block; + margin-top: 87px; + margin-left: 60px; + border-left-style: solid; + border-left-width: 1px; + border-left-color: var(--matterColorNegative); + + /* Calendar needs 311px width and therefore padding can't be wider than 58px */ + padding-left: 60px; + padding-right: 60px; + flex-basis: 311px; + flex-shrink: 0; + } +} + .sectionAvatar { /* Position (over the listing image)*/ margin-left: 24px; @@ -548,200 +568,6 @@ } } -.openBookingForm { - /* Ensure that mobile button is over Footer too */ - z-index: 9; - position: fixed; - bottom: 0; - left: 0; - width: 100vw; - padding: 18px 24px 18px 16px; - background-color: var(--matterColorLight); - box-shadow: var(--boxShadowTop); - display: flex; - - /* Contain repainting to this component only */ - /* 3D painting container helps scrolling */ - transform: translate3d(0, 0, 0); - - @media (--viewportMedium) { - padding: 18px 60px 18px 60px; - } - - @media (--viewportLarge) { - display: none; - } -} - -.priceContainer { - /* Layout */ - display: flex; - flex-direction: column; - align-items: center; - flex-shrink: 0; - margin-right: 22px; - padding: 5px 12px; -} - -.priceValue { - /* Font */ - @apply --marketplaceH2FontStyles; - color: var(--matterColor); - - margin-top: 0; - margin-bottom: 0px; - @media (--viewportMedium) { - margin-top: 0; - margin-bottom: 0; - } -} - -.perUnit { - /* Font */ - @apply --marketplaceTinyFontStyles; - color: var(--matterColor); - - margin-top: 0; - margin-bottom: 0px; - @media (--viewportMedium) { - margin-top: 0; - margin-bottom: 0; - } -} - -.modalInMobile { - @media (--viewportLarge) { - display: block; - margin-top: 87px; - margin-left: 60px; - border-left-style: solid; - border-left-width: 1px; - border-left-color: var(--matterColorNegative); - - /* Calendar needs 311px width and therefore padding can't be wider than 58px */ - padding-left: 60px; - padding-right: 60px; - flex-basis: 311px; - flex-shrink: 0; - } -} - -/* Booking form inside modalContainer needs special handling */ -/* since "inMobile" breakpoint is actually --viewportLarge */ -.modalContainer { - @apply --marketplaceModalInMobileBaseStyles; - - height: 100vh; - - @media (--viewportMedium) { - flex-basis: 576px; - height: 100%; - padding: var(--modalPaddingMedium); - background-color: var(--matterColorLight); - margin-top: 12.5vh; - margin-bottom: 12.5vh; - } - - @media (--viewportLarge) { - padding: 0; - background-color: transparent; - margin-top: 0; - margin-bottom: 0; - } -} - -.modalHeading { - margin-top: 89px; - margin-bottom: 36px; - padding: 0 24px; - - @media (--viewportMedium) { - padding: 0; - } - - @media (--viewportLarge) { - display: none; - } -} - -.bookingHeading { - display: none; - - @media (--viewportLarge) { - display: block; - margin-top: 6px; - margin-bottom: 47px; - } -} - -.bookingTitle { - @media (--viewportMedium) { - /* Font */ - color: var(--matterColor); - - margin-top: 0; - margin-bottom: 9px; - } -} - -.bookingHelp { - display: none; - @apply --marketplaceH5FontStyles; - - @media (--viewportMedium) { - color: var(--matterColor); - display: block; - margin-top: 0; - } -} - -.bookingForm { - flex-grow: 1; - display: flex; - flex-direction: column; - margin: 0 0 84px 0; - - @media (--viewportMedium) { - margin: 0; - min-height: 400px; - } - - @media (--viewportLarge) { - min-width: 311px; - min-height: auto; - } -} -.bookingDatesSubmitButtonWrapper { - flex-shrink: 0; - padding: 0 24px 24px 24px; - width: 100%; - position: fixed; - bottom: 0; - background-color: var(--matterColorLight); - - @media (--viewportMedium) { - padding: 0; - width: 100%; - position: static; - bottom: unset; - background-color: transparent; - } -} - -.bookButton { - @apply --marketplaceButtonStylesPrimary; - - /* Clear padding that is set for link elements looking like buttons */ - padding: 0; -} - -.closedListingButton { - border-left: 1px solid var(--matterColorNegative); - width: 100%; - padding: 15px 24px 15px 24px; - text-align: center; -} - .longWord { /* fallback option */ word-break: break-all; diff --git a/src/containers/ListingPage/ListingPage.js b/src/containers/ListingPage/ListingPage.js index 6d8cf841..746c7357 100644 --- a/src/containers/ListingPage/ListingPage.js +++ b/src/containers/ListingPage/ListingPage.js @@ -15,7 +15,6 @@ import { LISTING_PAGE_PARAM_TYPE_DRAFT, LISTING_PAGE_PARAM_TYPE_EDIT, createSlug, - parse, } from '../../util/urlHelpers'; import { formatMoney } from '../../util/currency'; import { createResourceLocatorString, findRouteByRouteName } from '../../util/routes'; @@ -32,6 +31,7 @@ import { LayoutWrapperMain, LayoutWrapperFooter, Footer, + BookingPanel, } from '../../components'; import { TopbarContainer, NotFoundPage } from '../../containers'; @@ -45,7 +45,6 @@ import SectionReviews from './SectionReviews'; import SectionHost from './SectionHost'; import SectionRulesMaybe from './SectionRulesMaybe'; import SectionMapMaybe from './SectionMapMaybe'; -import SectionBooking from './SectionBooking'; import css from './ListingPage.css'; const MIN_LENGTH_FOR_LONG_WORDS_IN_TITLE = 16; @@ -65,38 +64,6 @@ const priceData = (price, intl) => { return {}; }; -const openBookModal = (history, listing) => { - if (!listing.id) { - // Listing not fully loaded yet - return; - } - const routes = routeConfiguration(); - history.push( - createResourceLocatorString( - 'ListingPage', - routes, - { id: listing.id.uuid, slug: createSlug(listing.attributes.title) }, - { book: true } - ) - ); -}; - -const closeBookModal = (history, listing) => { - if (!listing.id) { - // Listing not fully loaded yet - return; - } - const routes = routeConfiguration(); - history.push( - createResourceLocatorString( - 'ListingPage', - routes, - { id: listing.id.uuid, slug: createSlug(listing.attributes.title) }, - {} - ) - ); -}; - const categoryLabel = (categories, key) => { const cat = categories.find(c => c.key === key); return cat ? cat.label : key; @@ -199,7 +166,6 @@ export class ListingPageComponent extends Component { location, scrollingDisabled, showListingError, - history, reviews, fetchReviewsError, sendEnquiryInProgress, @@ -210,7 +176,6 @@ export class ListingPageComponent extends Component { amenitiesConfig, } = this.props; - const isBook = !!parse(location.search).book; const listingId = new UUID(rawParams.id); const isPendingApprovalVariant = rawParams.variant === LISTING_PAGE_PENDING_APPROVAL_VARIANT; const isDraftVariant = rawParams.variant === LISTING_PAGE_DRAFT_VARIANT; @@ -341,10 +306,6 @@ export class ListingPageComponent extends Component { const { formattedPrice, priceTitle } = priceData(price, intl); - const handleMobileBookModalClose = () => { - closeBookModal(history, currentListing); - }; - const handleBookingSubmit = values => { const isCurrentlyClosed = currentListing.attributes.state === LISTING_STATE_CLOSED; if (isOwnListing || isCurrentlyClosed) { @@ -354,15 +315,6 @@ export class ListingPageComponent extends Component { } }; - const handleBookButtonClick = () => { - const isCurrentlyClosed = currentListing.attributes.state === LISTING_STATE_CLOSED; - if (isOwnListing || isCurrentlyClosed) { - window.scrollTo(0, 0); - } else { - openBookModal(history, currentListing); - } - }; - const listingImages = (listing, variantName) => (listing.images || []) .map(image => { @@ -480,11 +432,11 @@ export class ListingPageComponent extends Component { onManageDisableScrolling={onManageDisableScrolling} /> - { - const { - listing, - isOwnListing, - isClosed, - isBook, - unitType, - price, - formattedPrice, - priceTitle, - handleBookingSubmit, - richTitle, - authorDisplayName, - handleBookButtonClick, - handleMobileBookModalClose, - onManageDisableScrolling, - timeSlots, - fetchTimeSlotsError, - } = props; - const showClosedListingHelpText = listing.id && isClosed; - - const isNightly = unitType === LINE_ITEM_NIGHT; - const isDaily = unitType === LINE_ITEM_DAY; - - const unitTranslationKey = isNightly - ? 'ListingPage.perNight' - : isDaily - ? 'ListingPage.perDay' - : 'ListingPage.perUnit'; - - return ( -
- -
-

{richTitle}

-
- - - -
-
- -
-

- -

-
- -
-
- {!isClosed ? ( - - ) : null} -
-
-
-
- {formattedPrice} -
-
- -
-
- - {!isClosed ? ( - - ) : ( -
- -
- )} -
-
- ); -}; - -export default SectionBooking; diff --git a/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap b/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap index 072c0e2a..5d6866ae 100644 --- a/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap +++ b/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap @@ -262,14 +262,11 @@ exports[`ListingPage matches snapshot 1`] = ` title="listing1 title" /> - Date: Tue, 4 Dec 2018 17:24:35 +0200 Subject: [PATCH 02/15] Update translation keys --- src/components/BookingPanel/BookingPanel.js | 12 ++++++------ src/translations/en.json | 12 +++++++----- src/translations/fr.json | 12 +++++++----- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/components/BookingPanel/BookingPanel.js b/src/components/BookingPanel/BookingPanel.js index 51141e9a..083600d6 100644 --- a/src/components/BookingPanel/BookingPanel.js +++ b/src/components/BookingPanel/BookingPanel.js @@ -91,21 +91,21 @@ const BookingPanel = props => { // TOOD: check is class is required } - +

- +

@@ -129,7 +129,7 @@ const BookingPanel = props => { {formattedPrice}
- +
@@ -142,7 +142,7 @@ const BookingPanel = props => { ) : (
- +
)} diff --git a/src/translations/en.json b/src/translations/en.json index 5c915ebf..006490fb 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -67,6 +67,13 @@ "BookingDatesForm.requiredDate": "Oops, make sure your date is correct!", "BookingDatesForm.timeSlotsError": "Loading listing availability failed. Please refresh the page.", "BookingDatesForm.youWontBeChargedInfo": "You won't be charged yet", + "BookingPanel.bookingHelp": "Start by choosing your dates.", + "BookingPanel.bookingHelpClosedListing": "Sorry, this listing has been closed.", + "BookingPanel.bookingTitle": "Book {title}", + "BookingPanel.closedListingButtonText": "Sorry, this listing has been closed.", + "BookingPanel.ctaButtonMessage": "Request to book", + "BookingPanel.hostedBy": "Hosted by {name}", + "BookingPanel.perUnit": "per night", "CheckoutPage.bookingTimeNotAvailableMessage": "Unfortunately, the requested time is already booked.", "CheckoutPage.errorlistingLinkText": "the sauna page", "CheckoutPage.goToLandingPage": "Go to homepage", @@ -287,13 +294,8 @@ "ListingCard.unsupportedPrice": "({currency})", "ListingCard.unsupportedPriceTitle": "Unsupported currency ({currency})", "ListingPage.bannedUserDisplayName": "Banned user", - "ListingPage.bookingHelp": "Start by choosing your dates.", - "ListingPage.bookingHelpClosedListing": "Sorry, this listing has been closed.", - "ListingPage.bookingTitle": "Book {title}", "ListingPage.closedListing": "This listing has been closed and can't be booked.", - "ListingPage.closedListingButtonText": "Sorry, this listing has been closed.", "ListingPage.contactUser": "Contact", - "ListingPage.ctaButtonMessage": "Request to book", "ListingPage.descriptionTitle": "About this sauna", "ListingPage.editListing": "Edit listing", "ListingPage.editProfileLink": "Edit profile", diff --git a/src/translations/fr.json b/src/translations/fr.json index 8f26df4a..56a7a50f 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -67,6 +67,13 @@ "BookingDatesForm.requiredDate": "Oups, vérifiez que la date est correcte", "BookingDatesForm.timeSlotsError": "Le chargement des disponibilités pour cette annonce a échoué. Veuillez rafraîchir la page.", "BookingDatesForm.youWontBeChargedInfo": "Vous ne serez pas facturé immédiatement", + "BookingPanel.bookingHelp": "À quelle date souhaiteriez-vous réserver ?", + "BookingPanel.bookingHelpClosedListing": "Navré, cette annonce est close.", + "BookingPanel.bookingTitle": "Réserver {title}", + "BookingPanel.closedListingButtonText": "Navré, cette annonce est close.", + "BookingPanel.ctaButtonMessage": "Réserver", + "BookingPanel.hostedBy": "Proposé par {name}", + "BookingPanel.perUnit": "par nuit", "CheckoutPage.bookingTimeNotAvailableMessage": "Hélas, le créneau souhaité est déjà réservé.", "CheckoutPage.errorlistingLinkText": "la page de l'annonce", "CheckoutPage.goToLandingPage": "Aller sur la page d'accueil", @@ -287,13 +294,8 @@ "ListingCard.unsupportedPrice": "({currency})", "ListingCard.unsupportedPriceTitle": "Devise non supportée ({currency})", "ListingPage.bannedUserDisplayName": "Utilisateur banni", - "ListingPage.bookingHelp": "À quelle date souhaiteriez-vous réserver ?", - "ListingPage.bookingHelpClosedListing": "Navré, cette annonce est close.", - "ListingPage.bookingTitle": "Réserver {title}", "ListingPage.closedListing": "Cette annonce est close et ne peut être réservée.", - "ListingPage.closedListingButtonText": "Navré, cette annonce est close.", "ListingPage.contactUser": "Envoyer un message", - "ListingPage.ctaButtonMessage": "Réserver", "ListingPage.descriptionTitle": "À propos de ce sauna", "ListingPage.editListing": "Modifier l'annonce", "ListingPage.editProfileLink": "Modifier le profil", From 936d2955d9017f84af7b8888b8ebfb9002896953 Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Tue, 4 Dec 2018 15:00:29 +0200 Subject: [PATCH 03/15] Add BookingPanel styleguide example --- .../BookingPanel/BookingPanel.example.js | 21 +++++++++++++++++++ src/examples.js | 2 ++ 2 files changed, 23 insertions(+) create mode 100644 src/components/BookingPanel/BookingPanel.example.js diff --git a/src/components/BookingPanel/BookingPanel.example.js b/src/components/BookingPanel/BookingPanel.example.js new file mode 100644 index 00000000..d87d1a56 --- /dev/null +++ b/src/components/BookingPanel/BookingPanel.example.js @@ -0,0 +1,21 @@ +import React from 'react'; +import { createListing } from '../../util/test-data'; +import { types as sdkTypes } from '../../util/sdkLoader'; +import config from '../../config'; +import BookingPanel from './BookingPanel'; + +const { Money } = sdkTypes; +const CURRENCY = config.currency; + +export const Default = { + component: BookingPanel, + props: { + listing: createListing('listing_1'), + isClosed: false, + price: new Money(4500, CURRENCY), + handleBookingSubmit: values => console.log('Submit:', values), + richTitle: title, + authorDisplayName: 'Author Name', + onManageDisableScrolling: () => null, + }, +}; diff --git a/src/examples.js b/src/examples.js index 0c75352a..ea21892f 100644 --- a/src/examples.js +++ b/src/examples.js @@ -3,6 +3,7 @@ import * as ActivityFeed from './components/ActivityFeed/ActivityFeed.example'; import * as AddImages from './components/AddImages/AddImages.example'; import * as Avatar from './components/Avatar/Avatar.example'; import * as BookingBreakdown from './components/BookingBreakdown/BookingBreakdown.example'; +import * as BookingPanel from './components/BookingPanel/BookingPanel.example'; import * as Button from './components/Button/Button.example'; import * as ExpandingTextarea from './components/ExpandingTextarea/ExpandingTextarea.example'; import * as FieldBirthdayInput from './components/FieldBirthdayInput/FieldBirthdayInput.example'; @@ -89,6 +90,7 @@ export { Avatar, BookingBreakdown, BookingDatesForm, + BookingPanel, Button, Colors, EditListingDescriptionForm, From a032d06eea20cd3105bd4cc59e8f745d3d140e6d Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Tue, 4 Dec 2018 17:39:39 +0200 Subject: [PATCH 04/15] Use page specific message key on CheckoutPage --- src/containers/CheckoutPage/CheckoutPage.js | 2 +- .../CheckoutPage/__snapshots__/CheckoutPage.test.js.snap | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/containers/CheckoutPage/CheckoutPage.js b/src/containers/CheckoutPage/CheckoutPage.js index 5a526339..20f6bac8 100644 --- a/src/containers/CheckoutPage/CheckoutPage.js +++ b/src/containers/CheckoutPage/CheckoutPage.js @@ -395,7 +395,7 @@ export class CheckoutPageComponent extends Component {
diff --git a/src/containers/CheckoutPage/__snapshots__/CheckoutPage.test.js.snap b/src/containers/CheckoutPage/__snapshots__/CheckoutPage.test.js.snap index 7a8adecb..8afacecb 100644 --- a/src/containers/CheckoutPage/__snapshots__/CheckoutPage.test.js.snap +++ b/src/containers/CheckoutPage/__snapshots__/CheckoutPage.test.js.snap @@ -67,7 +67,7 @@ exports[`CheckoutPage matches snapshot 1`] = `
Date: Wed, 5 Dec 2018 13:41:40 +0200 Subject: [PATCH 05/15] Fix listing page layout --- src/components/BookingPanel/BookingPanel.css | 2 +- src/components/FieldDateRangeInput/DateRangeInput.css | 4 ++++ src/containers/ListingPage/ListingPage.css | 9 +++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/components/BookingPanel/BookingPanel.css b/src/components/BookingPanel/BookingPanel.css index 4d0e58e3..99091ee6 100644 --- a/src/components/BookingPanel/BookingPanel.css +++ b/src/components/BookingPanel/BookingPanel.css @@ -109,7 +109,7 @@ } @media (--viewportLarge) { - min-width: 311px; + min-width: 312px; min-height: auto; } } diff --git a/src/components/FieldDateRangeInput/DateRangeInput.css b/src/components/FieldDateRangeInput/DateRangeInput.css index 8e084d0c..d3807836 100644 --- a/src/components/FieldDateRangeInput/DateRangeInput.css +++ b/src/components/FieldDateRangeInput/DateRangeInput.css @@ -110,6 +110,10 @@ font-weight: 400; } + & :global(.DayPicker__withBorder) { + border-radius: 0; + } + & :global(.CalendarMonth_caption) { color: var(--matterColorLight); @apply --marketplaceH2FontStyles; diff --git a/src/containers/ListingPage/ListingPage.css b/src/containers/ListingPage/ListingPage.css index 8084867d..f0700cb8 100644 --- a/src/containers/ListingPage/ListingPage.css +++ b/src/containers/ListingPage/ListingPage.css @@ -216,8 +216,8 @@ @media (--viewportLarge) { margin-top: 50px; margin-bottom: 0px; - /* contentContainer's full width - (bookingColumn + gutters) */ - flex-basis: calc(100% - 491px); + /* contentContainer's full width - (bookingColumn + two paddings + margin + border) */ + flex-basis: calc(100% - 493px); flex-shrink: 0; } } @@ -231,10 +231,11 @@ border-left-width: 1px; border-left-color: var(--matterColorNegative); - /* Calendar needs 311px width and therefore padding can't be wider than 58px */ padding-left: 60px; padding-right: 60px; - flex-basis: 311px; + + /* The calendar needs 312px */ + flex-basis: 312px; flex-shrink: 0; } } From 9cd3c0c5adb91d8cd68f26b39dc7aefc446bf8ed Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Fri, 7 Dec 2018 11:19:56 +0200 Subject: [PATCH 06/15] Remove redundant style class definitions --- src/components/BookingPanel/BookingPanel.js | 7 +------ src/components/ListingCard/ListingCard.js | 6 +----- src/containers/CheckoutPage/CheckoutPage.js | 10 ++++------ .../__snapshots__/CheckoutPage.test.js.snap | 16 +++++++--------- 4 files changed, 13 insertions(+), 26 deletions(-) diff --git a/src/components/BookingPanel/BookingPanel.js b/src/components/BookingPanel/BookingPanel.js index 083600d6..103caad6 100644 --- a/src/components/BookingPanel/BookingPanel.js +++ b/src/components/BookingPanel/BookingPanel.js @@ -87,12 +87,7 @@ const BookingPanel = props => {

{richTitle}

- { - // TOOD: check is class is required - } - - - +
diff --git a/src/components/ListingCard/ListingCard.js b/src/components/ListingCard/ListingCard.js index f5b0f001..d8fdb0f0 100644 --- a/src/components/ListingCard/ListingCard.js +++ b/src/components/ListingCard/ListingCard.js @@ -99,11 +99,7 @@ export const ListingCardComponent = props => { })}
- +
diff --git a/src/containers/CheckoutPage/CheckoutPage.js b/src/containers/CheckoutPage/CheckoutPage.js index 20f6bac8..4b3d8d63 100644 --- a/src/containers/CheckoutPage/CheckoutPage.js +++ b/src/containers/CheckoutPage/CheckoutPage.js @@ -393,12 +393,10 @@ export class CheckoutPageComponent extends Component {

{title}

- - - +
diff --git a/src/containers/CheckoutPage/__snapshots__/CheckoutPage.test.js.snap b/src/containers/CheckoutPage/__snapshots__/CheckoutPage.test.js.snap index 8afacecb..0e3759ce 100644 --- a/src/containers/CheckoutPage/__snapshots__/CheckoutPage.test.js.snap +++ b/src/containers/CheckoutPage/__snapshots__/CheckoutPage.test.js.snap @@ -65,16 +65,14 @@ exports[`CheckoutPage matches snapshot 1`] = ` CheckoutPage.title
- - - + } + />
From e894dead63a149628cc8febb86b61d61cff799af Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Fri, 7 Dec 2018 15:26:42 +0200 Subject: [PATCH 07/15] Clean up BookingPanel props Remove isClosed and price from the BookingPanel props as they can be resolved from the listing. --- .../BookingPanel/BookingPanel.example.js | 19 ++++++++++++------- src/components/BookingPanel/BookingPanel.js | 8 +++----- src/containers/ListingPage/ListingPage.js | 3 --- .../__snapshots__/ListingPage.test.js.snap | 7 ------- 4 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/components/BookingPanel/BookingPanel.example.js b/src/components/BookingPanel/BookingPanel.example.js index d87d1a56..5779235a 100644 --- a/src/components/BookingPanel/BookingPanel.example.js +++ b/src/components/BookingPanel/BookingPanel.example.js @@ -1,18 +1,23 @@ import React from 'react'; import { createListing } from '../../util/test-data'; -import { types as sdkTypes } from '../../util/sdkLoader'; -import config from '../../config'; +import { LISTING_STATE_CLOSED } from '../../util/types'; import BookingPanel from './BookingPanel'; -const { Money } = sdkTypes; -const CURRENCY = config.currency; - export const Default = { component: BookingPanel, props: { listing: createListing('listing_1'), - isClosed: false, - price: new Money(4500, CURRENCY), + handleBookingSubmit: values => console.log('Submit:', values), + richTitle: title, + authorDisplayName: 'Author Name', + onManageDisableScrolling: () => null, + }, +}; + +export const WithClosedListing = { + component: BookingPanel, + props: { + listing: createListing('listing_1', { state: LISTING_STATE_CLOSED }), handleBookingSubmit: values => console.log('Submit:', values), richTitle: title, authorDisplayName: 'Author Name', diff --git a/src/components/BookingPanel/BookingPanel.js b/src/components/BookingPanel/BookingPanel.js index 103caad6..d89fb639 100644 --- a/src/components/BookingPanel/BookingPanel.js +++ b/src/components/BookingPanel/BookingPanel.js @@ -8,7 +8,7 @@ import classNames from 'classnames'; import omit from 'lodash/omit'; import { ModalInMobile, Button } from '../../components'; import { BookingDatesForm } from '../../forms'; -import { propTypes } from '../../util/types'; +import { propTypes, LISTING_STATE_CLOSED } from '../../util/types'; import { formatMoney } from '../../util/currency'; import { parse, stringify } from '../../util/urlHelpers'; import config from '../../config'; @@ -54,9 +54,7 @@ const BookingPanel = props => { className, listing, isOwnListing, - isClosed, unitType, - price, handleBookingSubmit, richTitle, authorDisplayName, @@ -68,6 +66,8 @@ const BookingPanel = props => { intl, } = props; + const price = listing.attributes.price; + const isClosed = listing.attributes.state === LISTING_STATE_CLOSED; const showClosedListingHelpText = listing.id && isClosed; const { formattedPrice, priceTitle } = priceData(price, intl); const isBook = !!parse(location.search).book; @@ -159,9 +159,7 @@ BookingPanel.propTypes = { className: string, listing: propTypes.listing.isRequired, isOwnListing: bool, - isClosed: bool.isRequired, unitType: propTypes.bookingUnitType, - price: propTypes.money.isRequired, handleBookingSubmit: func.isRequired, richTitle: object, authorDisplayName: string.isRequired, diff --git a/src/containers/ListingPage/ListingPage.js b/src/containers/ListingPage/ListingPage.js index 746c7357..83595ee0 100644 --- a/src/containers/ListingPage/ListingPage.js +++ b/src/containers/ListingPage/ListingPage.js @@ -293,7 +293,6 @@ export class ListingPageComponent extends Component { const userAndListingAuthorAvailable = !!(currentUser && authorAvailable); const isOwnListing = userAndListingAuthorAvailable && currentListing.author.id.uuid === currentUser.id.uuid; - const isClosed = currentListing.attributes.state === LISTING_STATE_CLOSED; const showContactUser = !currentUser || (currentUser && !isOwnListing); const currentAuthor = authorAvailable ? currentListing.author : null; @@ -436,9 +435,7 @@ export class ListingPageComponent extends Component { className={css.bookingPanel} listing={currentListing} isOwnListing={isOwnListing} - isClosed={isClosed} unitType={unitType} - price={price} formattedPrice={formattedPrice} priceTitle={priceTitle} handleBookingSubmit={handleBookingSubmit} diff --git a/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap b/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap index 5d6866ae..bdd6a215 100644 --- a/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap +++ b/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap @@ -267,7 +267,6 @@ exports[`ListingPage matches snapshot 1`] = ` fetchTimeSlotsError={null} formattedPrice={55} handleBookingSubmit={[Function]} - isClosed={false} isOwnListing={false} listing={ Object { @@ -307,12 +306,6 @@ exports[`ListingPage matches snapshot 1`] = ` } } onManageDisableScrolling={[Function]} - price={ - Money { - "amount": 5500, - "currency": "USD", - } - } priceTitle={55} richTitle={ From 2a886131c429bb02d8d5ec00b964711ec3bc6cd9 Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Fri, 7 Dec 2018 15:29:22 +0200 Subject: [PATCH 08/15] Remove some unused props passed to BookingPanel --- src/containers/ListingPage/ListingPage.js | 2 -- .../ListingPage/__snapshots__/ListingPage.test.js.snap | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/containers/ListingPage/ListingPage.js b/src/containers/ListingPage/ListingPage.js index 83595ee0..c5f8ead8 100644 --- a/src/containers/ListingPage/ListingPage.js +++ b/src/containers/ListingPage/ListingPage.js @@ -436,8 +436,6 @@ export class ListingPageComponent extends Component { listing={currentListing} isOwnListing={isOwnListing} unitType={unitType} - formattedPrice={formattedPrice} - priceTitle={priceTitle} handleBookingSubmit={handleBookingSubmit} richTitle={richTitle} authorDisplayName={authorDisplayName} diff --git a/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap b/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap index bdd6a215..34db24be 100644 --- a/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap +++ b/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap @@ -265,7 +265,6 @@ exports[`ListingPage matches snapshot 1`] = ` From 01b1830cbd9a894407af172611f55dae466c0c80 Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Mon, 10 Dec 2018 10:58:49 +0200 Subject: [PATCH 09/15] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d35aec20..f03d7a79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ way to update this template, but currently, we follow a pattern: [#985](https://github.com/sharetribe/flex-template-web/pull/985) - [remove] Remove the default built-in email templates. Built-in email templates can be edited in Console. [#983](https://github.com/sharetribe/flex-template-web/pull/983) +- [change] Extract SectionBooking to a distinct component from ListingPage. + [#969](https://github.com/sharetribe/flex-template-web/pull/969) ## [v2.5.0] 2018-12-17 From 3ea77385644538026efd57dceecb26351eaea338 Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Mon, 10 Dec 2018 14:36:29 +0200 Subject: [PATCH 10/15] Fix BookingPanel baseline adjustments --- src/components/BookingPanel/BookingPanel.css | 4 ++-- src/components/BookingPanel/BookingPanel.example.css | 3 +++ src/components/BookingPanel/BookingPanel.example.js | 3 +++ src/components/FieldDateRangeInput/FieldDateRangeInput.css | 4 ++++ src/forms/BookingDatesForm/BookingDatesForm.css | 2 +- 5 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 src/components/BookingPanel/BookingPanel.example.css diff --git a/src/components/BookingPanel/BookingPanel.css b/src/components/BookingPanel/BookingPanel.css index 99091ee6..de2a7210 100644 --- a/src/components/BookingPanel/BookingPanel.css +++ b/src/components/BookingPanel/BookingPanel.css @@ -71,8 +71,8 @@ @media (--viewportLarge) { display: block; - margin-top: 6px; - margin-bottom: 47px; + margin-top: -2px; + margin-bottom: 49px; } } diff --git a/src/components/BookingPanel/BookingPanel.example.css b/src/components/BookingPanel/BookingPanel.example.css new file mode 100644 index 00000000..7349578d --- /dev/null +++ b/src/components/BookingPanel/BookingPanel.example.css @@ -0,0 +1,3 @@ +.example { + width: 312px; +} diff --git a/src/components/BookingPanel/BookingPanel.example.js b/src/components/BookingPanel/BookingPanel.example.js index 5779235a..4a3d68a4 100644 --- a/src/components/BookingPanel/BookingPanel.example.js +++ b/src/components/BookingPanel/BookingPanel.example.js @@ -2,10 +2,12 @@ import React from 'react'; import { createListing } from '../../util/test-data'; import { LISTING_STATE_CLOSED } from '../../util/types'; import BookingPanel from './BookingPanel'; +import css from './BookingPanel.example.css'; export const Default = { component: BookingPanel, props: { + className: css.example, listing: createListing('listing_1'), handleBookingSubmit: values => console.log('Submit:', values), richTitle: title, @@ -17,6 +19,7 @@ export const Default = { export const WithClosedListing = { component: BookingPanel, props: { + className: css.example, listing: createListing('listing_1', { state: LISTING_STATE_CLOSED }), handleBookingSubmit: values => console.log('Submit:', values), richTitle: title, diff --git a/src/components/FieldDateRangeInput/FieldDateRangeInput.css b/src/components/FieldDateRangeInput/FieldDateRangeInput.css index 1aaf063c..c7352b16 100644 --- a/src/components/FieldDateRangeInput/FieldDateRangeInput.css +++ b/src/components/FieldDateRangeInput/FieldDateRangeInput.css @@ -23,6 +23,10 @@ margin-bottom: 0; font-weight: 600; } + + @media (--viewportMedium) { + padding-bottom: 1px; + } } .labelSuccess { diff --git a/src/forms/BookingDatesForm/BookingDatesForm.css b/src/forms/BookingDatesForm/BookingDatesForm.css index e30d160f..a54aa6e8 100644 --- a/src/forms/BookingDatesForm/BookingDatesForm.css +++ b/src/forms/BookingDatesForm/BookingDatesForm.css @@ -71,7 +71,7 @@ @media (--viewportLarge) { margin-top: 4px; - margin-bottom: 22px; + margin-bottom: 21px; } } From 167cc2608d14fb5523c1c5b5efd6f5cf3d1cc411 Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Mon, 10 Dec 2018 16:40:57 +0200 Subject: [PATCH 11/15] Change name and type for richText prop Rename the prop to title and allow it to be and object or a string. This way a normal string or a styled html element can be provided. --- src/components/BookingPanel/BookingPanel.example.js | 4 ++-- src/components/BookingPanel/BookingPanel.js | 10 +++++----- src/containers/ListingPage/ListingPage.js | 2 +- .../ListingPage/__snapshots__/ListingPage.test.js.snap | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/BookingPanel/BookingPanel.example.js b/src/components/BookingPanel/BookingPanel.example.js index 4a3d68a4..686a9ec3 100644 --- a/src/components/BookingPanel/BookingPanel.example.js +++ b/src/components/BookingPanel/BookingPanel.example.js @@ -10,7 +10,7 @@ export const Default = { className: css.example, listing: createListing('listing_1'), handleBookingSubmit: values => console.log('Submit:', values), - richTitle: title, + title: title, authorDisplayName: 'Author Name', onManageDisableScrolling: () => null, }, @@ -22,7 +22,7 @@ export const WithClosedListing = { className: css.example, listing: createListing('listing_1', { state: LISTING_STATE_CLOSED }), handleBookingSubmit: values => console.log('Submit:', values), - richTitle: title, + title: title, authorDisplayName: 'Author Name', onManageDisableScrolling: () => null, }, diff --git a/src/components/BookingPanel/BookingPanel.js b/src/components/BookingPanel/BookingPanel.js index d89fb639..3ff57a6c 100644 --- a/src/components/BookingPanel/BookingPanel.js +++ b/src/components/BookingPanel/BookingPanel.js @@ -2,7 +2,7 @@ import React from 'react'; import { compose } from 'redux'; import { withRouter } from 'react-router-dom'; import { intlShape, injectIntl } from 'react-intl'; -import { arrayOf, bool, func, object, shape, string } from 'prop-types'; +import { arrayOf, bool, func, object, oneOfType, shape, string } from 'prop-types'; import { FormattedMessage } from 'react-intl'; import classNames from 'classnames'; import omit from 'lodash/omit'; @@ -56,7 +56,7 @@ const BookingPanel = props => { isOwnListing, unitType, handleBookingSubmit, - richTitle, + title, authorDisplayName, onManageDisableScrolling, timeSlots, @@ -85,7 +85,7 @@ const BookingPanel = props => { onManageDisableScrolling={onManageDisableScrolling} >
-

{richTitle}

+

{title}

@@ -93,7 +93,7 @@ const BookingPanel = props => {

- +

listing1 @@ -314,7 +315,6 @@ exports[`ListingPage matches snapshot 1`] = ` } - timeSlots={null} unitType="line-item/night" />
From bee450d7862ca83bb23db5bcfdc8470108fa3c0c Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Tue, 11 Dec 2018 08:09:49 +0200 Subject: [PATCH 12/15] Change BookingPanel title handling Use title and sub title provided by a parent component. --- .../BookingPanel/BookingPanel.example.js | 6 +++-- src/components/BookingPanel/BookingPanel.js | 25 +++++++++---------- src/containers/ListingPage/ListingPage.js | 8 +++++- .../__snapshots__/ListingPage.test.js.snap | 22 ++++++++++------ src/translations/en.json | 6 ++--- src/translations/fr.json | 6 ++--- 6 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/components/BookingPanel/BookingPanel.example.js b/src/components/BookingPanel/BookingPanel.example.js index 686a9ec3..589027c1 100644 --- a/src/components/BookingPanel/BookingPanel.example.js +++ b/src/components/BookingPanel/BookingPanel.example.js @@ -10,7 +10,8 @@ export const Default = { className: css.example, listing: createListing('listing_1'), handleBookingSubmit: values => console.log('Submit:', values), - title: title, + title: Booking title, + subTitle: 'Hosted by Author N', authorDisplayName: 'Author Name', onManageDisableScrolling: () => null, }, @@ -22,7 +23,8 @@ export const WithClosedListing = { className: css.example, listing: createListing('listing_1', { state: LISTING_STATE_CLOSED }), handleBookingSubmit: values => console.log('Submit:', values), - title: title, + title: Booking title, + subTitle: 'Hosted by Author N', authorDisplayName: 'Author Name', onManageDisableScrolling: () => null, }, diff --git a/src/components/BookingPanel/BookingPanel.js b/src/components/BookingPanel/BookingPanel.js index 3ff57a6c..bec513b0 100644 --- a/src/components/BookingPanel/BookingPanel.js +++ b/src/components/BookingPanel/BookingPanel.js @@ -57,6 +57,7 @@ const BookingPanel = props => { unitType, handleBookingSubmit, title, + subTitle, authorDisplayName, onManageDisableScrolling, timeSlots, @@ -72,6 +73,12 @@ const BookingPanel = props => { const { formattedPrice, priceTitle } = priceData(price, intl); const isBook = !!parse(location.search).book; + const subTitleText = !!subTitle + ? subTitle + : showClosedListingHelpText + ? intl.formatMessage({ id: 'BookingPanel.subTitleClosedListing' }) + : null; + const classes = classNames(rootClassName || css.root, className); return ( @@ -92,18 +99,8 @@ const BookingPanel = props => {
-

- -

-
- -
+

{title}

+ {subTitleText ?
{subTitleText}
: null}
{!isClosed ? ( ); + const bookingTitle = ( + + ); + const bookingSubTitle = intl.formatMessage({ id: 'ListingPage.bookingSubTitle' }); + const topbar = ; if (showListingError && showListingError.status === 404) { @@ -437,7 +442,8 @@ export class ListingPageComponent extends Component { isOwnListing={isOwnListing} unitType={unitType} handleBookingSubmit={handleBookingSubmit} - title={richTitle} + title={bookingTitle} + subTitle={bookingSubTitle} authorDisplayName={authorDisplayName} onManageDisableScrolling={onManageDisableScrolling} timeSlots={timeSlots} diff --git a/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap b/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap index 09ad0914..847f3667 100644 --- a/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap +++ b/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap @@ -305,15 +305,23 @@ exports[`ListingPage matches snapshot 1`] = ` } } onManageDisableScrolling={[Function]} + subTitle="ListingPage.bookingSubTitle" timeSlots={null} title={ - - - listing1 - - title - - + + + listing1 + + title + + , + } + } + /> } unitType="line-item/night" /> diff --git a/src/translations/en.json b/src/translations/en.json index 006490fb..e83f68f7 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -67,13 +67,11 @@ "BookingDatesForm.requiredDate": "Oops, make sure your date is correct!", "BookingDatesForm.timeSlotsError": "Loading listing availability failed. Please refresh the page.", "BookingDatesForm.youWontBeChargedInfo": "You won't be charged yet", - "BookingPanel.bookingHelp": "Start by choosing your dates.", - "BookingPanel.bookingHelpClosedListing": "Sorry, this listing has been closed.", - "BookingPanel.bookingTitle": "Book {title}", "BookingPanel.closedListingButtonText": "Sorry, this listing has been closed.", "BookingPanel.ctaButtonMessage": "Request to book", "BookingPanel.hostedBy": "Hosted by {name}", "BookingPanel.perUnit": "per night", + "BookingPanel.subTitleClosedListing": "Sorry, this listing has been closed.", "CheckoutPage.bookingTimeNotAvailableMessage": "Unfortunately, the requested time is already booked.", "CheckoutPage.errorlistingLinkText": "the sauna page", "CheckoutPage.goToLandingPage": "Go to homepage", @@ -294,6 +292,8 @@ "ListingCard.unsupportedPrice": "({currency})", "ListingCard.unsupportedPriceTitle": "Unsupported currency ({currency})", "ListingPage.bannedUserDisplayName": "Banned user", + "ListingPage.bookingSubTitle": "Start by choosing your dates.", + "ListingPage.bookingTitle": "Book {title}", "ListingPage.closedListing": "This listing has been closed and can't be booked.", "ListingPage.contactUser": "Contact", "ListingPage.descriptionTitle": "About this sauna", diff --git a/src/translations/fr.json b/src/translations/fr.json index 56a7a50f..dccf7625 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -67,13 +67,11 @@ "BookingDatesForm.requiredDate": "Oups, vérifiez que la date est correcte", "BookingDatesForm.timeSlotsError": "Le chargement des disponibilités pour cette annonce a échoué. Veuillez rafraîchir la page.", "BookingDatesForm.youWontBeChargedInfo": "Vous ne serez pas facturé immédiatement", - "BookingPanel.bookingHelp": "À quelle date souhaiteriez-vous réserver ?", - "BookingPanel.bookingHelpClosedListing": "Navré, cette annonce est close.", - "BookingPanel.bookingTitle": "Réserver {title}", "BookingPanel.closedListingButtonText": "Navré, cette annonce est close.", "BookingPanel.ctaButtonMessage": "Réserver", "BookingPanel.hostedBy": "Proposé par {name}", "BookingPanel.perUnit": "par nuit", + "BookingPanel.subTitleClosedListing": "Navré, cette annonce est close.", "CheckoutPage.bookingTimeNotAvailableMessage": "Hélas, le créneau souhaité est déjà réservé.", "CheckoutPage.errorlistingLinkText": "la page de l'annonce", "CheckoutPage.goToLandingPage": "Aller sur la page d'accueil", @@ -294,6 +292,8 @@ "ListingCard.unsupportedPrice": "({currency})", "ListingCard.unsupportedPriceTitle": "Devise non supportée ({currency})", "ListingPage.bannedUserDisplayName": "Utilisateur banni", + "ListingPage.bookingSubTitle": "À quelle date souhaiteriez-vous réserver ?", + "ListingPage.bookingTitle": "Réserver {title}", "ListingPage.closedListing": "Cette annonce est close et ne peut être réservée.", "ListingPage.contactUser": "Envoyer un message", "ListingPage.descriptionTitle": "À propos de ce sauna", From 080ae345a6ebea44fd6c2b178373fb569cb6a38e Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Wed, 19 Dec 2018 17:22:55 +0200 Subject: [PATCH 13/15] Change unit type translation resolving --- src/components/BookingPanel/BookingPanel.js | 13 +++++++++++-- src/translations/en.json | 4 +++- src/translations/fr.json | 6 ++++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/components/BookingPanel/BookingPanel.js b/src/components/BookingPanel/BookingPanel.js index bec513b0..01b22923 100644 --- a/src/components/BookingPanel/BookingPanel.js +++ b/src/components/BookingPanel/BookingPanel.js @@ -8,7 +8,7 @@ import classNames from 'classnames'; import omit from 'lodash/omit'; import { ModalInMobile, Button } from '../../components'; import { BookingDatesForm } from '../../forms'; -import { propTypes, LISTING_STATE_CLOSED } from '../../util/types'; +import { propTypes, LISTING_STATE_CLOSED, LINE_ITEM_NIGHT, LINE_ITEM_DAY } from '../../util/types'; import { formatMoney } from '../../util/currency'; import { parse, stringify } from '../../util/urlHelpers'; import config from '../../config'; @@ -79,6 +79,15 @@ const BookingPanel = props => { ? intl.formatMessage({ id: 'BookingPanel.subTitleClosedListing' }) : null; + const isNightly = unitType === LINE_ITEM_NIGHT; + const isDaily = unitType === LINE_ITEM_DAY; + + const unitTranslationKey = isNightly + ? 'BookingPanel.perNight' + : isDaily + ? 'BookingPanel.perDay' + : 'BookingPanel.perUnit'; + const classes = classNames(rootClassName || css.root, className); return ( @@ -121,7 +130,7 @@ const BookingPanel = props => { {formattedPrice}
- +
diff --git a/src/translations/en.json b/src/translations/en.json index e83f68f7..2f31d57a 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -70,7 +70,9 @@ "BookingPanel.closedListingButtonText": "Sorry, this listing has been closed.", "BookingPanel.ctaButtonMessage": "Request to book", "BookingPanel.hostedBy": "Hosted by {name}", - "BookingPanel.perUnit": "per night", + "BookingPanel.perDay": "per day", + "BookingPanel.perNight": "per night", + "BookingPanel.perUnit": "per unit", "BookingPanel.subTitleClosedListing": "Sorry, this listing has been closed.", "CheckoutPage.bookingTimeNotAvailableMessage": "Unfortunately, the requested time is already booked.", "CheckoutPage.errorlistingLinkText": "the sauna page", diff --git a/src/translations/fr.json b/src/translations/fr.json index dccf7625..1b8efe51 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -70,7 +70,9 @@ "BookingPanel.closedListingButtonText": "Navré, cette annonce est close.", "BookingPanel.ctaButtonMessage": "Réserver", "BookingPanel.hostedBy": "Proposé par {name}", - "BookingPanel.perUnit": "par nuit", + "BookingPanel.perDay": "par jour", + "BookingPanel.perNight": "par nuit", + "BookingPanel.perUnit": "par unité", "BookingPanel.subTitleClosedListing": "Navré, cette annonce est close.", "CheckoutPage.bookingTimeNotAvailableMessage": "Hélas, le créneau souhaité est déjà réservé.", "CheckoutPage.errorlistingLinkText": "la page de l'annonce", @@ -798,4 +800,4 @@ "UserCard.heading": "Bonjour, je suis {name}.", "UserCard.showFullBioLink": "plus", "UserCard.viewProfileLink": "Voir le profil" -} \ No newline at end of file +} From f4f67be66180f5b9070ebb7abe19e03a2f8fb71e Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Wed, 19 Dec 2018 17:27:40 +0200 Subject: [PATCH 14/15] Change formatted title and sub title type Change formatted title and sub title type to node in BookingPanel. Also remove FormattedText component which renders a redundant span element. --- src/components/BookingPanel/BookingPanel.js | 6 +++--- src/containers/ListingPage/ListingPage.js | 5 +++-- .../__snapshots__/ListingPage.test.js.snap | 17 +---------------- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/src/components/BookingPanel/BookingPanel.js b/src/components/BookingPanel/BookingPanel.js index 01b22923..42b3d138 100644 --- a/src/components/BookingPanel/BookingPanel.js +++ b/src/components/BookingPanel/BookingPanel.js @@ -2,7 +2,7 @@ import React from 'react'; import { compose } from 'redux'; import { withRouter } from 'react-router-dom'; import { intlShape, injectIntl } from 'react-intl'; -import { arrayOf, bool, func, object, oneOfType, shape, string } from 'prop-types'; +import { arrayOf, bool, func, node, oneOfType, shape, string } from 'prop-types'; import { FormattedMessage } from 'react-intl'; import classNames from 'classnames'; import omit from 'lodash/omit'; @@ -168,8 +168,8 @@ BookingPanel.propTypes = { isOwnListing: bool, unitType: propTypes.bookingUnitType, handleBookingSubmit: func.isRequired, - title: oneOfType([object, string]).isRequired, - subTitle: oneOfType([object, string]), + title: oneOfType([node, string]).isRequired, + subTitle: oneOfType([node, string]), authorDisplayName: string.isRequired, onManageDisableScrolling: func.isRequired, timeSlots: arrayOf(propTypes.timeSlot), diff --git a/src/containers/ListingPage/ListingPage.js b/src/containers/ListingPage/ListingPage.js index 2d02d3a6..b5d5b5cb 100644 --- a/src/containers/ListingPage/ListingPage.js +++ b/src/containers/ListingPage/ListingPage.js @@ -229,8 +229,9 @@ export class ListingPageComponent extends Component { ); - const bookingTitle = ( - + const bookingTitle = intl.formatMessage( + { id: 'ListingPage.bookingSubTitle' }, + { title: richTitle } ); const bookingSubTitle = intl.formatMessage({ id: 'ListingPage.bookingSubTitle' }); diff --git a/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap b/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap index 847f3667..e8df2d93 100644 --- a/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap +++ b/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap @@ -307,22 +307,7 @@ exports[`ListingPage matches snapshot 1`] = ` onManageDisableScrolling={[Function]} subTitle="ListingPage.bookingSubTitle" timeSlots={null} - title={ - - - listing1 - - title - - , - } - } - /> - } + title="ListingPage.bookingSubTitle" unitType="line-item/night" /> From 3d11d733e02e1dc387ab43b94589a8e43b4b8602 Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Wed, 19 Dec 2018 17:39:00 +0200 Subject: [PATCH 15/15] Change BookingPanel dependency import order Place components and forms as last imports before the ones from the same folder. --- src/components/BookingPanel/BookingPanel.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/BookingPanel/BookingPanel.js b/src/components/BookingPanel/BookingPanel.js index 42b3d138..87ebd94a 100644 --- a/src/components/BookingPanel/BookingPanel.js +++ b/src/components/BookingPanel/BookingPanel.js @@ -6,12 +6,12 @@ import { arrayOf, bool, func, node, oneOfType, shape, string } from 'prop-types' import { FormattedMessage } from 'react-intl'; import classNames from 'classnames'; import omit from 'lodash/omit'; -import { ModalInMobile, Button } from '../../components'; -import { BookingDatesForm } from '../../forms'; import { propTypes, LISTING_STATE_CLOSED, LINE_ITEM_NIGHT, LINE_ITEM_DAY } from '../../util/types'; import { formatMoney } from '../../util/currency'; import { parse, stringify } from '../../util/urlHelpers'; import config from '../../config'; +import { ModalInMobile, Button } from '../../components'; +import { BookingDatesForm } from '../../forms'; import css from './BookingPanel.css';