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 diff --git a/src/components/BookingPanel/BookingPanel.css b/src/components/BookingPanel/BookingPanel.css new file mode 100644 index 00000000..de2a7210 --- /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: -2px; + margin-bottom: 49px; + } +} + +.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: 312px; + 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.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 new file mode 100644 index 00000000..589027c1 --- /dev/null +++ b/src/components/BookingPanel/BookingPanel.example.js @@ -0,0 +1,31 @@ +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), + title: Booking title, + subTitle: 'Hosted by Author N', + authorDisplayName: 'Author Name', + onManageDisableScrolling: () => null, + }, +}; + +export const WithClosedListing = { + component: BookingPanel, + props: { + className: css.example, + listing: createListing('listing_1', { state: LISTING_STATE_CLOSED }), + handleBookingSubmit: values => console.log('Submit:', values), + 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 new file mode 100644 index 00000000..87ebd94a --- /dev/null +++ b/src/components/BookingPanel/BookingPanel.js @@ -0,0 +1,193 @@ +import React from 'react'; +import { compose } from 'redux'; +import { withRouter } from 'react-router-dom'; +import { intlShape, injectIntl } from 'react-intl'; +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 { 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'; + +// 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, + unitType, + handleBookingSubmit, + title, + subTitle, + authorDisplayName, + onManageDisableScrolling, + timeSlots, + fetchTimeSlotsError, + history, + location, + 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; + + const subTitleText = !!subTitle + ? subTitle + : showClosedListingHelpText + ? 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 ( +
+ closeBookModal(history, location)} + showAsModalMaxWidth={MODAL_BREAKPOINT} + onManageDisableScrolling={onManageDisableScrolling} + > +
+

{title}

+
+ +
+
+ +
+

{title}

+ {subTitleText ?
{subTitleText}
: null} +
+ {!isClosed ? ( + + ) : null} +
+
+
+
+ {formattedPrice} +
+
+ +
+
+ + {!isClosed ? ( + + ) : ( +
+ +
+ )} +
+
+ ); +}; + +BookingPanel.defaultProps = { + rootClassName: null, + className: null, + isOwnListing: false, + subTitle: null, + unitType: config.bookingUnitType, + timeSlots: null, + fetchTimeSlotsError: null, +}; + +BookingPanel.propTypes = { + rootClassName: string, + className: string, + listing: propTypes.listing.isRequired, + isOwnListing: bool, + unitType: propTypes.bookingUnitType, + handleBookingSubmit: func.isRequired, + title: oneOfType([node, string]).isRequired, + subTitle: oneOfType([node, string]), + 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/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/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/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/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/CheckoutPage/CheckoutPage.js b/src/containers/CheckoutPage/CheckoutPage.js index 5a526339..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 7a8adecb..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
- - - + } + />
diff --git a/src/containers/ListingPage/ListingPage.css b/src/containers/ListingPage/ListingPage.css index 62410f22..f0700cb8 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; @@ -213,8 +216,26 @@ @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; + } +} + +.bookingPanel { + @media (--viewportLarge) { + display: block; + margin-top: 87px; + margin-left: 60px; + border-left-style: solid; + border-left-width: 1px; + border-left-color: var(--matterColorNegative); + + padding-left: 60px; + padding-right: 60px; + + /* The calendar needs 312px */ + flex-basis: 312px; flex-shrink: 0; } } @@ -548,200 +569,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..b5d5b5cb 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; @@ -264,6 +229,12 @@ export class ListingPageComponent extends Component { ); + const bookingTitle = intl.formatMessage( + { id: 'ListingPage.bookingSubTitle' }, + { title: richTitle } + ); + const bookingSubTitle = intl.formatMessage({ id: 'ListingPage.bookingSubTitle' }); + const topbar = ; if (showListingError && showListingError.status === 404) { @@ -328,7 +299,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; @@ -341,10 +311,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 +320,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,20 +437,15 @@ 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..e8df2d93 100644 --- a/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap +++ b/src/containers/ListingPage/__snapshots__/ListingPage.test.js.snap @@ -262,15 +262,10 @@ exports[`ListingPage matches snapshot 1`] = ` title="listing1 title" /> - - - listing1 - - title - - - } + subTitle="ListingPage.bookingSubTitle" timeSlots={null} + title="ListingPage.bookingSubTitle" unitType="line-item/night" /> 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, 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; } } diff --git a/src/translations/en.json b/src/translations/en.json index 5c915ebf..2f31d57a 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.closedListingButtonText": "Sorry, this listing has been closed.", + "BookingPanel.ctaButtonMessage": "Request to book", + "BookingPanel.hostedBy": "Hosted by {name}", + "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", "CheckoutPage.goToLandingPage": "Go to homepage", @@ -287,13 +294,10 @@ "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.bookingSubTitle": "Start by choosing your dates.", "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..1b8efe51 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.closedListingButtonText": "Navré, cette annonce est close.", + "BookingPanel.ctaButtonMessage": "Réserver", + "BookingPanel.hostedBy": "Proposé par {name}", + "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", "CheckoutPage.goToLandingPage": "Aller sur la page d'accueil", @@ -287,13 +294,10 @@ "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.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.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", @@ -796,4 +800,4 @@ "UserCard.heading": "Bonjour, je suis {name}.", "UserCard.showFullBioLink": "plus", "UserCard.viewProfileLink": "Voir le profil" -} \ No newline at end of file +}