diff --git a/src/components/FieldGroupCheckbox/FieldGroupCheckbox.js b/src/components/FieldGroupCheckbox/FieldGroupCheckbox.js index 60310cad..9fff4927 100644 --- a/src/components/FieldGroupCheckbox/FieldGroupCheckbox.js +++ b/src/components/FieldGroupCheckbox/FieldGroupCheckbox.js @@ -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'; diff --git a/src/components/PropertyGroup/PropertyGroup.css b/src/components/PropertyGroup/PropertyGroup.css new file mode 100644 index 00000000..8576fc2d --- /dev/null +++ b/src/components/PropertyGroup/PropertyGroup.css @@ -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) + ); + } +} diff --git a/src/components/PropertyGroup/PropertyGroup.example.js b/src/components/PropertyGroup/PropertyGroup.example.js new file mode 100644 index 00000000..8a4bbfd8 --- /dev/null +++ b/src/components/PropertyGroup/PropertyGroup.example.js @@ -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', +}; diff --git a/src/components/PropertyGroup/PropertyGroup.js b/src/components/PropertyGroup/PropertyGroup.js new file mode 100644 index 00000000..4679b53b --- /dev/null +++ b/src/components/PropertyGroup/PropertyGroup.js @@ -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 ( + + + + ); +}; + +const Item = props => { + const { text, isSelected } = props; + const textClass = isSelected ? css.selectedText : css.notSelectedText; + return ( +
  • +
    + +
    +
    + {text} +
    +
  • + ); +}; + +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 ( + + ); +}; + +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; diff --git a/src/components/index.js b/src/components/index.js index 504b743b..9a7dea88 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -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'; diff --git a/src/examples.js b/src/examples.js index bcbae1e0..134fa4d5 100644 --- a/src/examples.js +++ b/src/examples.js @@ -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,