Merge pull request #663 from sharetribe/show-multi-value

Add a component for presenting multi value data
This commit is contained in:
Hannu Lyytikäinen 2018-01-23 17:32:14 +02:00 committed by GitHub
commit 2b4d56eb7d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 211 additions and 0 deletions

View file

@ -1,3 +1,12 @@
/*
* Renders a group of checkboxes that can be used to select
* multiple values from a set of options.
*
* The corresponding component when rendering the selected
* values is PropertyGroup.
*
*/
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

View file

@ -0,0 +1,84 @@
@import '../../marketplace.css';
:root {
--lineHeight: 24px;
--lineThroughTop: calc(var(--lineHeight) - 7px);
--lineThroughBottom: calc(var(--lineHeight) - 6px);
--lineHeightMobile: 20px;
--lineThroughTopMobile: calc(var(--lineHeightMobile) - 5px);
--lineThroughBottomMobile: calc(var(--lineHeightMobile) - 4px);
}
.root {
margin: 0;
}
.twoColumns {
@media (--viewportMedium) {
column-count: 2;
}
}
.item {
display: flex;
align-items: center;
padding: 2px 0;
@media (--viewportMedium) {
padding: 4px 0;
}
}
.checkIcon {
margin: 1px 8px -1px 0;
}
.hidden {
visibility: hidden;
}
.marketplaceFill {
fill: var(--marketplaceColor);
}
.iconWrapper {
display: inline-flex;
align-items: center;
}
.textWrapper {
display: inline-block;
}
.selectedText,
.notSelectedText {
@apply --marketplaceBodyFontStyles;
line-height: var(--lineHeight);
margin: 0;
}
.selectedText {
font-weight: var(--fontWeightSemiBold);
}
.notSelectedText {
color: var(--matterColorNegative);
position: relative;
/* line-through */
background-image: linear-gradient(
transparent var(--lineThroughTopMobile),
var(--matterColorNegative) var(--lineThroughTopMobile),
var(--matterColorNegative) var(--lineThroughBottomMobile),
transparent var(--lineThroughBottomMobile)
);
@media (--viewportMedium) {
background-image: linear-gradient(
transparent var(--lineThroughTop),
var(--matterColorNegative) var(--lineThroughTop),
var(--matterColorNegative) var(--lineThroughBottom),
transparent var(--lineThroughBottom)
);
}
}

View file

@ -0,0 +1,23 @@
import PropertyGroup from './PropertyGroup';
const exampleOptions = [
{ value: 'towels', text: 'Towels' },
{ value: 'bathroom', text: 'Bathroom' },
{ value: 'swimming', text: 'Swimming' },
{ value: 'own_drinks_allowed', text: 'Own drinks allowed' },
{ value: 'jacuzzi', text: 'Jacuzzi' },
{ value: 'audiovisual_entertainment', text: 'Audiovisual entertainment' },
{ value: 'barbeque', text: 'Barbeque' },
{ value: 'own_food_allowed', text: 'Own food allowed' },
];
export const WithSomeSelected = {
component: PropertyGroup,
props: {
id: 'amenities',
options: exampleOptions,
selectedOptions: ['towels', 'bathroom', 'barbeque'],
twoColumns: true,
},
group: 'misc',
};

View file

@ -0,0 +1,92 @@
/*
* Renders a set of options with selected and non-selected values.
*
* The corresponding component when selecting the values is
* FieldGroupCheckbox.
*
*/
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { includes } from 'lodash';
import css from './PropertyGroup.css';
const checkSelected = (options, selectedOptions) => {
return options.map(option => ({
text: option.text,
value: option.value,
isSelected: includes(selectedOptions, option.value),
}));
};
const IconCheck = props => {
const isVisible = props.isVisible;
const classes = isVisible ? css.checkIcon : classNames(css.checkIcon, css.hidden);
return (
<svg width="9" height="9" xmlns="http://www.w3.org/2000/svg" className={classes}>
<path
className={css.marketplaceFill}
d="M2.636621 7.7824771L.3573694 5.6447948c-.4764924-.4739011-.4764924-1.2418639 0-1.7181952.4777142-.473901 1.251098-.473901 1.7288122 0l1.260291 1.1254783L6.1721653.505847C6.565577-.0373166 7.326743-.1636902 7.8777637.227582c.5473554.3912721.6731983 1.150729.2797866 1.6951076L4.4924979 7.631801c-.2199195.306213-.5803433.5067096-.9920816.5067096-.3225487 0-.6328797-.1263736-.8637952-.3560334z"
fillRule="evenodd"
/>
</svg>
);
};
const Item = props => {
const { text, isSelected } = props;
const textClass = isSelected ? css.selectedText : css.notSelectedText;
return (
<li className={css.item}>
<div className={css.iconWrapper}>
<IconCheck isVisible={isSelected} />
</div>
<div className={css.textWrapper}>
<span className={textClass}>{text}</span>
</div>
</li>
);
};
const PropertyGroup = props => {
const { rootClassName, className, id, options, selectedOptions, twoColumns } = props;
const classes = classNames(rootClassName || css.root, className);
const listClasses = twoColumns ? classNames(classes, css.twoColumns) : classes;
const checked = checkSelected(options, selectedOptions);
return (
<ul className={listClasses}>
{checked.map(option => (
<Item key={`${id}.${option.value}`} text={option.text} isSelected={option.isSelected} />
))}
</ul>
);
};
PropertyGroup.defaultProps = {
rootClassName: null,
className: null,
twoColumns: false,
};
const { arrayOf, bool, node, shape, string } = PropTypes;
PropertyGroup.propTypes = {
rootClassName: string,
className: string,
id: string.isRequired,
options: arrayOf(
shape({
value: string.isRequired,
text: node.isRequired,
})
),
selectedOptions: arrayOf(string).isRequired,
twoColumns: bool,
};
export default PropertyGroup;

View file

@ -86,6 +86,7 @@ export { default as Page } from './Page/Page';
export { default as PaginationLinks } from './PaginationLinks/PaginationLinks';
export { default as PrivacyPolicy } from './PrivacyPolicy/PrivacyPolicy';
export { default as Promised } from './Promised/Promised';
export { default as PropertyGroup } from './PropertyGroup/PropertyGroup';
export { default as ResponsiveImage } from './ResponsiveImage/ResponsiveImage';
export { default as ReviewModal } from './ReviewModal/ReviewModal';
export { default as ReviewRating } from './ReviewRating/ReviewRating';

View file

@ -38,6 +38,7 @@ import * as Modal from './components/Modal/Modal.example';
import * as ModalInMobile from './components/ModalInMobile/ModalInMobile.example';
import * as NamedLink from './components/NamedLink/NamedLink.example';
import * as PaginationLinks from './components/PaginationLinks/PaginationLinks.example';
import * as PropertyGroup from './components/PropertyGroup/PropertyGroup.example';
import * as ResponsiveImage from './components/ResponsiveImage/ResponsiveImage.example';
import * as ReviewRating from './components/ReviewRating/ReviewRating.example';
import * as Reviews from './components/Reviews/Reviews.example';
@ -122,6 +123,7 @@ export {
PasswordRecoveryForm,
PasswordResetForm,
PayoutDetailsForm,
PropertyGroup,
ResponsiveImage,
ReviewForm,
ReviewRating,