SectionThumbnailLinks

This commit is contained in:
Vesa Luusua 2018-01-17 13:42:15 +02:00
parent d62aa61fe0
commit 82c1330d13
6 changed files with 154 additions and 110 deletions

View file

@ -1,86 +0,0 @@
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

@ -1,22 +1,22 @@
import SectionSearchLinks from './SectionSearchLinks';
import SectionThumbnailLinks from './SectionThumbnailLinks';
const imageAltText = 'styleguide alt text';
export const TwoLinksWithHeadings = {
component: SectionSearchLinks,
export const TwoNamedLinksWithHeadings = {
component: SectionThumbnailLinks,
props: {
linksPerRow: 2,
links: [
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?1',
linkProps: { type: 'NamedLink', name: 'SearchPage', to: { search: '?1' } },
text: 'Link 1',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?2',
linkProps: { type: 'NamedLink', name: 'SearchPage', to: { search: '?2' } },
text: 'Link 2',
},
],
@ -26,27 +26,27 @@ export const TwoLinksWithHeadings = {
group: 'sections',
};
export const ThreeLinksWithHeadings = {
component: SectionSearchLinks,
export const ThreeExternalLinksWithHeadings = {
component: SectionThumbnailLinks,
props: {
linksPerRow: 3,
links: [
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?1',
linkProps: { type: 'ExternalLink', href: 'http://example.com/1' },
text: 'Link 1',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?2',
linkProps: { type: 'ExternalLink', href: 'http://example.com/2' },
text: 'Link 2',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?3',
linkProps: { type: 'ExternalLink', href: 'http://example.com/3' },
text: 'Link 3',
},
],
@ -57,32 +57,32 @@ export const ThreeLinksWithHeadings = {
};
export const FourLinks = {
component: SectionSearchLinks,
component: SectionThumbnailLinks,
props: {
linksPerRow: 2,
links: [
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?1',
linkProps: { type: 'NamedLink', name: 'SearchPage', to: { search: '?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',
linkProps: { type: 'NamedLink', name: 'SearchPage', to: { search: '?2' } },
text: 'Link 2',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?3',
linkProps: { type: 'NamedLink', name: 'SearchPage', to: { search: '?3' } },
text: 'Link 3',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?4',
linkProps: { type: 'NamedLink', name: 'SearchPage', to: { search: '?4' } },
text: 'Link 4',
},
],
@ -91,44 +91,45 @@ export const FourLinks = {
};
export const SixLinks = {
component: SectionSearchLinks,
component: SectionThumbnailLinks,
props: {
linksPerRow: 3,
links: [
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?1',
linkProps: { type: 'NamedLink', name: 'SearchPage', to: { search: '?1' } },
text: 'Link 1',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
linkProps: { type: 'NamedLink', name: 'SearchPage', to: { search: '?2' } },
searchQuery: '?2',
text: 'Link 2',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?3',
linkProps: { type: 'NamedLink', name: 'SearchPage', to: { search: '?3' } },
text: 'Link 3',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?4',
linkProps: { type: 'NamedLink', name: 'SearchPage', to: { search: '?4' } },
text: 'Link 4',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?5',
linkProps: { type: 'NamedLink', name: 'SearchPage', to: { search: '?5' } },
text: 'Link 5',
},
{
imageUrl: 'http://lorempixel.com/648/448/',
imageAltText,
searchQuery: '?6',
linkProps: { type: 'NamedLink', name: 'SearchPage', to: { search: '?6' } },
text: 'Link 6',
},
],

View file

@ -0,0 +1,129 @@
import React from 'react';
import { string, arrayOf, shape, node, oneOf, oneOfType } from 'prop-types';
import classNames from 'classnames';
import { ExternalLink, NamedLink } from '../../components';
import css from './SectionThumbnailLinks.css';
const ThumbnailLink = props => {
const {
className,
rootClassName,
imageWrapperClassName,
linksPerRow,
imageUrl,
imageAltText,
linkProps,
text,
} = props;
const { type, name, to, href } = linkProps;
const classes = classNames(rootClassName || css.link, className, {
[css.link2Columns]: linksPerRow === 2,
[css.link3Columns]: linksPerRow === 3,
});
const imageWrapperClasses = classNames(css.imageWrapper, imageWrapperClassName);
const LinkComponentProps = type === 'NamedLink' ? { name, to } : { href };
const LinkComponent = type === 'NamedLink' ? NamedLink : ExternalLink;
return (
<LinkComponent {...LinkComponentProps} className={classes}>
<div className={imageWrapperClasses}>
<div className={css.aspectWrapper}>
<img src={imageUrl} alt={imageAltText} className={css.image} />
</div>
</div>
<div className={css.text}>{text}</div>
</LinkComponent>
);
};
const SectionThumbnailLinks = props => {
const {
rootClassName,
className,
linksPerRow,
links,
heading,
subHeading,
headingRootClassName,
subHeadingRootClassName,
linkClassName,
linkRootClassName,
imageWrapperClassName,
} = 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, i) => (
<ThumbnailLink
key={i}
linksPerRow={linksPerRow}
linkRootClassName={linkRootClassName}
className={linkClassName}
imageWrapperClassName={imageWrapperClassName}
{...link}
/>
))}
</div>
</div>
);
};
SectionThumbnailLinks.defaultProps = {
rootClassName: null,
className: null,
heading: null,
subHeading: null,
headingRootClassName: null,
subHeadingRootClassName: null,
imageWrapperClassName: null,
};
const namedLinkShape = shape({
type: oneOf(['NamedLink']).isRequired,
name: string.isRequired,
to: shape({
search: string,
hash: string,
}),
});
const externalLinkShape = shape({
type: oneOf(['ExternalLink']).isRequired,
href: string.isRequired,
});
SectionThumbnailLinks.propTypes = {
rootClassName: string,
className: string,
linksPerRow: oneOf([2, 3]).isRequired,
links: arrayOf(
shape({
imageUrl: string.isRequired,
imageAltText: string.isRequired,
linkProps: oneOfType([namedLinkShape, externalLinkShape]).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,
linkClassName: string,
linkRootClassName: string,
imageWrapperClassName: string,
};
export default SectionThumbnailLinks;

View file

@ -98,7 +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 SectionThumbnailLinks } from './SectionThumbnailLinks/SectionThumbnailLinks';
export { default as SelectField } from './SelectField/SelectField';
export { default as SelectSingleFilter } from './SelectSingleFilter/SelectSingleFilter';
export {

View file

@ -40,7 +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 SectionThumbnailLinks from './components/SectionThumbnailLinks/SectionThumbnailLinks.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';
@ -124,7 +124,7 @@ export {
ReviewForm,
ReviewRating,
Reviews,
SectionSearchLinks,
SectionThumbnailLinks,
SelectField,
SendMessageForm,
SignupForm,