Merge pull request #647 from sharetribe/search-link-section

Search links section
This commit is contained in:
Kimmo Puputti 2018-01-16 12:26:25 +02:00 committed by GitHub
commit 0004e9837d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 341 additions and 0 deletions

View file

@ -0,0 +1,115 @@
@import '../../marketplace.css';
.root {
}
.heading {
@apply --marketplaceH1FontStyles;
margin: 0 0 18px 0;
@media (--viewportMedium) {
margin: 0 0 23px 0;
}
}
.subHeading {
margin: 0 0 57px 0;
@media (--viewportMedium) {
margin: 0 0 57px 0;
}
}
.links {
display: flex;
flex-direction: column;
justify-content: space-between;
flex-wrap: wrap;
@media (--viewportMedium) {
flex-direction: row;
}
}
.link {
width: 100%;
margin-top: 25px;
&:hover {
text-decoration: none;
}
/* First link should not have top margin */
&:nth-of-type(1) {
margin-top: 0;
}
}
.link2Columns {
@media (--viewportMedium) {
width: calc(50% - 20px);
/* First row should not have top margin */
&:nth-of-type(1),
&:nth-of-type(2) {
margin-top: 0;
}
}
}
.link3Columns {
@media (--viewportMedium) {
width: calc(33.333% - 20px);
/* First row should not have top margin */
&:nth-of-type(1),
&:nth-of-type(2),
&:nth-of-type(3) {
margin-top: 0;
}
}
}
.imageWrapper {
position: relative;
width: 100%;
border-radius: 4px;
transition: var(--transitionStyleButton);
&:hover {
transform: scale(1.02);
box-shadow: var(--boxShadowSectionLocationHover);
}
}
.aspectWrapper {
padding-bottom: calc(6 / 13 * 100%); /* 13:6 Aspect Ratio */
@media (--viewportMedium) {
padding-bottom: calc(2 / 3 * 100%); /* 3:2 Aspect Ratio */
}
}
.image {
/* Layout - image will take space defined by aspect ratio wrapper */
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 100%;
border-radius: 4px;
object-fit: cover;
}
.text {
@apply --marketplaceH2FontStyles;
color: var(--matterColor);
margin-top: 15px;
margin-bottom: 0;
@media (--viewportMedium) {
margin-top: 21px;
}
}

View file

@ -0,0 +1,137 @@
import SectionSearchLinks from './SectionSearchLinks';
const imageAltText = 'styleguide alt text';
export const TwoLinksWithHeadings = {
component: SectionSearchLinks,
props: {
linksPerRow: 2,
links: [
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?1',
text: 'Link 1',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?2',
text: 'Link 2',
},
],
heading: 'Two links side by side',
subHeading: 'One column in mobile, two columns in desktop.',
},
group: 'sections',
};
export const ThreeLinksWithHeadings = {
component: SectionSearchLinks,
props: {
linksPerRow: 3,
links: [
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?1',
text: 'Link 1',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?2',
text: 'Link 2',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?3',
text: 'Link 3',
},
],
heading: 'Three links side by side',
subHeading: 'One column in mobile, three columns in desktop.',
},
group: 'sections',
};
export const FourLinks = {
component: SectionSearchLinks,
props: {
linksPerRow: 2,
links: [
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?1',
text: 'Link 1 with quite a long text that tests how the items below align',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?2',
text: 'Link 2',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?3',
text: 'Link 3',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?4',
text: 'Link 4',
},
],
},
group: 'sections',
};
export const SixLinks = {
component: SectionSearchLinks,
props: {
linksPerRow: 3,
links: [
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?1',
text: 'Link 1',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?2',
text: 'Link 2',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?3',
text: 'Link 3',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?4',
text: 'Link 4',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?5',
text: 'Link 5',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?6',
text: 'Link 6',
},
],
},
group: 'sections',
};

View file

@ -0,0 +1,86 @@
import React from 'react';
import { string, arrayOf, shape, node, oneOf } from 'prop-types';
import classNames from 'classnames';
import { NamedLink } from '../../components';
import css from './SectionSearchLinks.css';
const SearchLink = props => {
const { linksPerRow, imageUrl, imageAltText, searchQuery, text } = props;
const classes = classNames(css.link, {
[css.link2Columns]: linksPerRow === 2,
[css.link3Columns]: linksPerRow === 3,
});
return (
<NamedLink name="SearchPage" to={{ search: searchQuery }} className={classes}>
<div className={css.imageWrapper}>
<div className={css.aspectWrapper}>
<img src={imageUrl} alt={imageAltText} className={css.image} />
</div>
</div>
<div className={css.text}>{text}</div>
</NamedLink>
);
};
const SectionSearchLinks = props => {
const {
rootClassName,
className,
linksPerRow,
links,
heading,
subHeading,
headingRootClassName,
subHeadingRootClassName,
} = props;
const classes = classNames(rootClassName || css.root, className);
const headingClasses = headingRootClassName || css.heading;
const subHeadingClasses = subHeadingRootClassName || css.subHeading;
return (
<div className={classes}>
{heading ? <h2 className={headingClasses}>{heading}</h2> : null}
{subHeading ? <p className={subHeadingClasses}>{subHeading}</p> : null}
<div className={css.links}>
{links.map(link => (
<SearchLink key={link.searchQuery} linksPerRow={linksPerRow} {...link} />
))}
</div>
</div>
);
};
SectionSearchLinks.defaultProps = {
rootClassName: null,
className: null,
heading: null,
subHeading: null,
headingRootClassName: null,
subHeadingRootClassName: null,
};
SectionSearchLinks.propTypes = {
rootClassName: string,
className: string,
linksPerRow: oneOf([2, 3]).isRequired,
links: arrayOf(
shape({
imageUrl: string.isRequired,
imageAltText: string.isRequired,
searchQuery: string.isRequired,
text: node.isRequired,
})
).isRequired,
// Styles are defined with the assumption that either both the
// heading and the subHeading are given, or neither of them are. If
// only one of them is given, the margins most likely won't make
// sense.
heading: node,
subHeading: node,
headingRootClassName: string,
subHeadingRootClassName: string,
};
export default SectionSearchLinks;

View file

@ -98,6 +98,7 @@ export { default as SearchResultsPanel } from './SearchResultsPanel/SearchResult
export { default as SectionHero } from './SectionHero/SectionHero';
export { default as SectionHowItWorks } from './SectionHowItWorks/SectionHowItWorks';
export { default as SectionLocations } from './SectionLocations/SectionLocations';
export { default as SectionSearchLinks } from './SectionSearchLinks/SectionSearchLinks';
export { default as SelectField } from './SelectField/SelectField';
export { default as SelectSingleFilter } from './SelectSingleFilter/SelectSingleFilter';
export {

View file

@ -40,6 +40,7 @@ import * as PaginationLinks from './components/PaginationLinks/PaginationLinks.e
import * as ResponsiveImage from './components/ResponsiveImage/ResponsiveImage.example';
import * as ReviewRating from './components/ReviewRating/ReviewRating.example';
import * as Reviews from './components/Reviews/Reviews.example';
import * as SectionSearchLinks from './components/SectionSearchLinks/SectionSearchLinks.example';
import * as SelectField from './components/SelectField/SelectField.example';
import * as StripeBankAccountTokenInputField from './components/StripeBankAccountTokenInputField/StripeBankAccountTokenInputField.example';
import * as TabNav from './components/TabNav/TabNav.example';
@ -123,6 +124,7 @@ export {
ReviewForm,
ReviewRating,
Reviews,
SectionSearchLinks,
SelectField,
SendMessageForm,
SignupForm,