ListingCard for SearchResultsPanel and ListingCardSmall for MapPanel

This commit is contained in:
Vesa Luusua 2017-01-26 16:13:24 +02:00
parent acf85d9af6
commit 1fdccf7f71
8 changed files with 419 additions and 0 deletions

View file

@ -0,0 +1,67 @@
.listing {
display: flex;
flex-direction: column;
border-radius: 4px;
}
.squareWrapper {
display: block;
position: relative;
width: 100%;
}
/* Firefox doesn't support image aspect ratio inside flexbox */
.aspectWrapper {
padding-bottom: 75%; /* 4:3 Aspect Ratio */
}
.thumbnail {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 100%;
}
.info {
padding: 1rem;
}
.mainInfo {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.details {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.authorInfo {
width: 100%;
display: flex;
flex-direction: row;
}
.authorDetails {
width: 100%;
display: flex;
flex-direction: column;
}
.avatarWrapper {
display: block;
flex-basis: 44px;
width: 44px;
height: 44px;
margin-right: 1rem;
}
.avatar {
border-radius: 22px;
}

View file

@ -0,0 +1,71 @@
import React, { PropTypes } from 'react';
import { NamedLink } from '../../components';
import css from './ListingCard.css';
const ListingCard = props => {
const { id, title, price, description, location, review, author } = props;
const slug = encodeURIComponent(title.split(' ').join('-'));
return (
<div className={css.listing}>
<div className={css.squareWrapper}>
<div className={css.aspectWrapper}>
<img className={css.thumbnail} src="http://placehold.it/400x300" alt="Listing Title" />
</div>
</div>
<div className={css.info}>
<div className={css.mainInfo}>
<NamedLink className={css.title} name="ListingPage" params={{ id, slug }}>
{title}
</NamedLink>
<div className={css.price}>
{price}
</div>
</div>
<div className={css.description}>
{description}
</div>
<div className={css.details}>
<div className={css.location}>
{location}
</div>
<div className={css.reviews}>
(<span>{review.rating}</span><span>/5</span>){' '}
<span>{review.count}</span>
</div>
</div>
<hr />
<div className={css.authorInfo}>
<div className={css.avatarWrapper}>
<img className={css.avatar} src={author.avatar} alt={author.name} />
</div>
<div className={css.authorDetails}>
<span className={css.authorName}>{author.name}</span>
<div className={css.authorReview}>
review: <span>{author.review.rating}</span><span>/5</span>
</div>
</div>
</div>
</div>
</div>
);
};
ListingCard.defaultProps = { location: null, review: {}, author: {} };
const { number, shape, string } = PropTypes;
ListingCard.propTypes = {
id: number.isRequired,
title: string.isRequired,
price: string.isRequired,
description: string.isRequired,
location: string,
review: shape({ rating: string.isRequired, count: string.isRequired }),
author: shape({
name: string.isRequired,
avatar: string.isRequired,
review: shape({ rating: string.isRequired }),
}),
};
export default ListingCard;

View file

@ -0,0 +1,35 @@
import React from 'react';
import renderer from 'react-test-renderer';
import { TestProvider } from '../../util/test-helpers';
import { RoutesProvider } from '../../components';
import routesConfiguration from '../../routesConfiguration';
import ListingCard from './ListingCard';
describe('ListingCard', () => {
it('matches snapshot', () => {
const listing = {
id: 123,
title: 'Banyan Studios',
price: '55\u20AC / day',
description: 'Organic Music Production in a Sustainable, Ethical and Professional Studio.',
location: 'New York, NY \u2022 40mi away',
review: { count: '8 reviews', rating: '4' },
author: {
name: 'The Stardust Collective',
avatar: 'http://placehold.it/44x44',
review: { rating: '4' },
},
};
const component = renderer.create(
(
<TestProvider>
<RoutesProvider routes={routesConfiguration}>
<ListingCard {...listing} />
</RoutesProvider>
</TestProvider>
),
);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
});

View file

@ -0,0 +1,86 @@
exports[`ListingCard matches snapshot 1`] = `
<div
className={undefined}>
<div
className={undefined}>
<div
className={undefined}>
<img
alt="Listing Title"
className={undefined}
src="http://placehold.it/400x300" />
</div>
</div>
<div
className={undefined}>
<div
className={undefined}>
<a
className=""
href="/l/Banyan-Studios/123"
onClick={[Function]}
style={Object {}}>
Banyan Studios
</a>
<div
className={undefined}>
55€ / day
</div>
</div>
<div
className={undefined}>
Organic Music Production in a Sustainable, Ethical and Professional Studio.
</div>
<div
className={undefined}>
<div
className={undefined}>
New York, NY • 40mi away
</div>
<div
className={undefined}>
(
<span>
4
</span>
<span>
/5
</span>
)
<span>
8 reviews
</span>
</div>
</div>
<hr />
<div
className={undefined}>
<div
className={undefined}>
<img
alt="The Stardust Collective"
className={undefined}
src="http://placehold.it/44x44" />
</div>
<div
className={undefined}>
<span
className={undefined}>
The Stardust Collective
</span>
<div
className={undefined}>
review:
<span>
4
</span>
<span>
/5
</span>
</div>
</div>
</div>
</div>
</div>
`;

View file

@ -0,0 +1,38 @@
.listing {
width: 130px;
display: flex;
flex-direction: column;
border-radius: 4px;
margin-right: 1rem;
}
.squareWrapper {
display: block;
position: relative;
width: 100%;
}
/* Firefox doesn't support image aspect ratio inside flexbox */
.aspectWrapper {
padding-bottom: 75%; /* 4:3 Aspect Ratio */
}
.thumbnail {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 100%;
}
.info {
width: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 0.5rem;
font-size: 0.8rem;
border: solid 1px #ccc;
}

View file

@ -0,0 +1,43 @@
import React, { PropTypes } from 'react';
import { NamedLink } from '../../components';
import css from './ListingCardSmall.css';
// <NamedLink name="SearchListingsPage">X</NamedLink>
const ListingCardSmall = props => {
const { id, title, price, review } = props;
const slug = encodeURIComponent(title.split(' ').join('-'));
return (
<div className={css.listing}>
<div className={css.squareWrapper}>
<div className={css.aspectWrapper}>
<img className={css.thumbnail} src="http://placehold.it/200x150" alt="Listing Title" />
</div>
</div>
<div className={css.info}>
<NamedLink className={css.title} name="ListingPage" params={{ id, slug }}>
{title}
</NamedLink>
<div className={css.reviews}>
(<span>{review.rating}</span><span>/5</span>){' '}
<span>{review.count}</span>
</div>
<div className={css.price}>
{price}
</div>
</div>
</div>
);
};
ListingCardSmall.defaultProps = { review: {} };
const { number, shape, string } = PropTypes;
ListingCardSmall.propTypes = {
id: number.isRequired,
title: string.isRequired,
price: string.isRequired,
review: shape({ rating: string.isRequired, count: string.isRequired }),
};
export default ListingCardSmall;

View file

@ -0,0 +1,35 @@
import React from 'react';
import renderer from 'react-test-renderer';
import { TestProvider } from '../../util/test-helpers';
import { RoutesProvider } from '../../components';
import routesConfiguration from '../../routesConfiguration';
import ListingCardSmall from './ListingCardSmall';
describe('ListingCardSmall', () => {
it('matches snapshot', () => {
const listing = {
id: 123,
title: 'Banyan Studios',
price: '55\u20AC / day',
description: 'Organic Music Production in a Sustainable, Ethical and Professional Studio.',
location: 'New York, NY \u2022 40mi away',
review: { count: '8 reviews', rating: '4' },
author: {
name: 'The Stardust Collective',
avatar: 'http://placehold.it/44x44',
review: { rating: '4' },
},
};
const component = renderer.create(
(
<TestProvider>
<RoutesProvider routes={routesConfiguration}>
<ListingCardSmall {...listing} />
</RoutesProvider>
</TestProvider>
),
);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
});

View file

@ -0,0 +1,44 @@
exports[`ListingCardSmall matches snapshot 1`] = `
<div
className={undefined}>
<div
className={undefined}>
<div
className={undefined}>
<img
alt="Listing Title"
className={undefined}
src="http://placehold.it/200x150" />
</div>
</div>
<div
className={undefined}>
<a
className=""
href="/l/Banyan-Studios/123"
onClick={[Function]}
style={Object {}}>
Banyan Studios
</a>
<div
className={undefined}>
(
<span>
4
</span>
<span>
/5
</span>
)
<span>
8 reviews
</span>
</div>
<div
className={undefined}>
55€ / day
</div>
</div>
</div>
`;