diff --git a/src/components/ListingCard/ListingCard.css b/src/components/ListingCard/ListingCard.css index 5f7dacd9..a8259cc1 100644 --- a/src/components/ListingCard/ListingCard.css +++ b/src/components/ListingCard/ListingCard.css @@ -1,15 +1,21 @@ -.listing { +@import '../../marketplace.css'; + +.root { + /* Layout */ display: flex; flex-direction: column; - border-radius: 4px; - margin-bottom: 24px; - text-decoration: none; + + /* Remove link's hover effect */ + &:hover { + text-decoration: none; + } } .threeToTwoWrapper { + /* Layout */ display: block; - position: relative; width: 100%; + position: relative; } /* Firefox doesn't support image aspect ratio inside flexbox */ @@ -17,8 +23,8 @@ padding-bottom: 66.6667%; /* 3:2 Aspect Ratio */ } -.noImageContainer, -.thumbnail { +.rootForImage { + /* Layout - image will take space defined by aspect ratio wrapper */ position: absolute; top: 0; bottom: 0; @@ -27,58 +33,84 @@ width: 100%; } -.noImageContainer, -.noImageWrapper { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - background-color: #eee; -} - -.noImageText { - color: #666; -} - .info { - padding: 1rem; - -} - -.mainInfo { - width: 100%; + /* Layout */ display: flex; flex-direction: row; - justify-content: space-between; - margin-bottom: 10px; -} - -.title { - font-size: 18px; + padding: 15px 0 6px 0; } .price { + /* Layout */ display: flex; - flex-direction: row; - justify-content: flex-end; + flex-direction: column; + justify-content: flex-start; flex-shrink: 0; - padding-left: 1rem; + margin-right: 18px; } .priceValue { - font-size: 18px; + /* Font */ + @apply --marketplaceH4FontStyles; + + /* Remove default margins from font */ + margin-top: 0; + margin-bottom: 4px; + + @media (--desktopViewport) { + margin-top: 0; + margin-bottom: 4px; + } } .perNight { - margin-left: 5px; - font-size: 12px; + /* Font */ + @apply --marketplaceTinyFontStyles; + color: var(--matterColor); - /* Align small price unit text with bigger font (priceValue) */ - line-height: 23px; - padding-top: 2px; + /* Remove default margins from font */ + margin-top: 0; + margin-bottom: 0; + + @media (--desktopViewport) { + margin-top: 0; + margin-bottom: 0; + } } + +.mainInfo { + display: flex; + flex-direction: column; +} + +.title { + /* Font */ + @apply --marketplaceH4FontStyles; + color: var(--matterColor); + + /* Remove default margins from font */ + margin-top: 0; + margin-bottom: 4px; + + @media (--desktopViewport) { + margin-top: 0; + margin-bottom: 4px; + } +} + + .authorInfo { - width: 100%; - font-size: 12px; + /* Font */ + @apply --marketplaceTinyFontStyles; + color: var(--matterColor); + + /* Remove default margins from font */ + margin-top: 0; + margin-bottom: 0; + + @media (--desktopViewport) { + margin-top: 0; + margin-bottom: 0; + } } diff --git a/src/components/ListingCard/ListingCard.js b/src/components/ListingCard/ListingCard.js index 872cbf5a..840c3838 100644 --- a/src/components/ListingCard/ListingCard.js +++ b/src/components/ListingCard/ListingCard.js @@ -1,10 +1,12 @@ -import React from 'react'; +import React, { PropTypes } from 'react'; import { FormattedMessage, intlShape, injectIntl } from 'react-intl'; -import { NamedLink } from '../../components'; +import classNames from 'classnames'; +import { NamedLink, ResponsiveImage } from '../../components'; import * as propTypes from '../../util/propTypes'; import { convertMoneyToNumber } from '../../util/currency'; +import { ensureListing, ensureUser } from '../../util/data'; + import css from './ListingCard.css'; -import noImageIcon from './images/noImageIcon.svg'; const priceData = (price, currencyConfig, intl) => { if (price && price.currency === currencyConfig.currency) { @@ -13,87 +15,86 @@ const priceData = (price, currencyConfig, intl) => { return { formattedPrice, priceTitle: formattedPrice }; } else if (price) { return { - formattedPrice: `(${price.currency})`, - priceTitle: `Unsupported currency (${price.currency})`, + formattedPrice: intl.formatMessage( + { id: 'ListingCard.unsupportedPrice' }, + { currency: price.currency } + ), + priceTitle: intl.formatMessage( + { id: 'ListingCard.unsupportedPriceTitle' }, + { currency: price.currency } + ), }; } return {}; }; export const ListingCardComponent = props => { - const { currencyConfig, intl, listing } = props; - const id = listing.id.uuid; - const { title = '', price } = listing.attributes || {}; + const { className, rootClassName, currencyConfig, intl, listing } = props; + const classes = classNames(rootClassName || css.root, className); + const currentListing = ensureListing(listing); + const id = currentListing.id.uuid; + const { title = '', price } = currentListing.attributes; const slug = encodeURIComponent(title.split(' ').join('-')); - const authorName = listing.author && listing.author.attributes - ? `${listing.author.attributes.profile.firstName} ${listing.author.attributes.profile.lastName}` - : ''; + const author = ensureUser(listing.author); + const authorName = `${author.attributes.profile.firstName} ${author.attributes.profile.lastName}`; + const firstImage = currentListing.images && currentListing.images.length > 0 + ? currentListing.images[0] + : null; // TODO: Currently, API can return currencies that are not supported by starter app. const { formattedPrice, priceTitle } = priceData(price, currencyConfig, intl); - const images = listing.images - ? listing.images.map(i => ({ id: i.id, sizes: i.attributes.sizes })) - : []; - const mainImage = images.length > 0 ? images[0] : null; - const imageURL = mainImage ? mainImage.sizes.find(i => i.name === 'landscape-crop').url : null; - const image2XURL = mainImage - ? mainImage.sizes.find(i => i.name === 'landscape-crop2x').url - : null; - const higherRes = image2XURL ? { srcSet: `${image2XURL} 2x` } : null; - - // TODO: svg should have own loading strategy - // Now noImageIcon is imported with default configuration (gives url) - /* eslint-disable jsx-a11y/img-redundant-alt */ - const noListingImage = ( -