From 9d89b45771570abf1a3b028195b50f1633cfa219 Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Mon, 22 Jan 2018 15:41:09 +0200 Subject: [PATCH 1/5] Add PropertyGroup component --- .../PropertyGroup/PropertyGroup.css | 49 +++++++++++ .../PropertyGroup/PropertyGroup.example.js | 22 +++++ src/components/PropertyGroup/PropertyGroup.js | 86 +++++++++++++++++++ src/components/index.js | 1 + src/examples.js | 2 + 5 files changed, 160 insertions(+) create mode 100644 src/components/PropertyGroup/PropertyGroup.css create mode 100644 src/components/PropertyGroup/PropertyGroup.example.js create mode 100644 src/components/PropertyGroup/PropertyGroup.js diff --git a/src/components/PropertyGroup/PropertyGroup.css b/src/components/PropertyGroup/PropertyGroup.css new file mode 100644 index 00000000..13a30432 --- /dev/null +++ b/src/components/PropertyGroup/PropertyGroup.css @@ -0,0 +1,49 @@ +@import '../../marketplace.css'; + +.root { + margin: 0; +} + +.twoColumns { + @media (--viewportMedium) { + columns: 2; + } +} + +.item { + display: flex; + align-items: center; + + /* Fix broken multi-column layout in Chrome */ + page-break-inside: avoid; +} + +.check { + margin: 1px 8px -1px 0; +} + +.selectedText, +.notSelectedText { + @apply --marketplaceBodyFontStyles; + margin: 0; +} + +.selectedText { + font-weight: var(--fontWeightSemiBold); +} + +.notSelectedText { + color: var(--matterColorNegative); + position: relative; + margin-left: 17px; +} + +/* line-through */ +.notSelectedText::after { + position: absolute; + left: 0; + right: 0; + top: 58%; + content: ''; + border-bottom: solid 1px var(--matterColorNegative); +} diff --git a/src/components/PropertyGroup/PropertyGroup.example.js b/src/components/PropertyGroup/PropertyGroup.example.js new file mode 100644 index 00000000..ea9e4fc2 --- /dev/null +++ b/src/components/PropertyGroup/PropertyGroup.example.js @@ -0,0 +1,22 @@ +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: { + 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..55f80483 --- /dev/null +++ b/src/components/PropertyGroup/PropertyGroup.js @@ -0,0 +1,86 @@ +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, + isSelected: includes(selectedOptions, option.value), + })); +}; + +const iconCheck = ( + + + +); + +const SelectedItem = props => { + const text = props.text; + return ( +
  • + {iconCheck} + {text} +
  • + ); +}; + +const NotSelectedItem = props => { + const text = props.text; + return ( +
  • + {text} +
  • + ); +}; + +const PropertyGroup = props => { + const { rootClassName, className, 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, + 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, From 42c7df49439ac80e671265fdfe198350e32c8b20 Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Tue, 23 Jan 2018 12:39:43 +0200 Subject: [PATCH 2/5] Add component comments Add information to FieldGroupCheckbox and PropertyGroup about the relation between the two components. --- src/components/FieldGroupCheckbox/FieldGroupCheckbox.js | 9 +++++++++ src/components/PropertyGroup/PropertyGroup.js | 8 ++++++++ 2 files changed, 17 insertions(+) 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.js b/src/components/PropertyGroup/PropertyGroup.js index 55f80483..a41f6a62 100644 --- a/src/components/PropertyGroup/PropertyGroup.js +++ b/src/components/PropertyGroup/PropertyGroup.js @@ -1,3 +1,11 @@ +/* + * 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'; From f25fa879b4e7a7661a2c55f3fa098630f64a9116 Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Tue, 23 Jan 2018 15:58:22 +0200 Subject: [PATCH 3/5] Fix multiline items --- .../PropertyGroup/PropertyGroup.css | 71 +++++++++++++++---- .../PropertyGroup/PropertyGroup.example.js | 1 + src/components/PropertyGroup/PropertyGroup.js | 52 +++++++++----- 3 files changed, 94 insertions(+), 30 deletions(-) diff --git a/src/components/PropertyGroup/PropertyGroup.css b/src/components/PropertyGroup/PropertyGroup.css index 13a30432..2c2f0ed9 100644 --- a/src/components/PropertyGroup/PropertyGroup.css +++ b/src/components/PropertyGroup/PropertyGroup.css @@ -1,30 +1,71 @@ @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) { - columns: 2; + column-count: 2; } } .item { + display: flex; + padding: 2px 0; + + @media (--viewportMedium) { + padding: 4px 0; + } +} + +.itemSelected { + padding: 2px 0; display: flex; align-items: center; /* Fix broken multi-column layout in Chrome */ page-break-inside: avoid; + + @media (--viewportMedium) { + padding: 4px 0; + } } -.check { +.checkIcon { margin: 1px 8px -1px 0; } +.hidden { + visibility: hidden; +} + +.marketplaceFill { + fill: var(--marketplaceColor); +} + +.iconWrapper { + display: inline-flex; + align-content: center; +} + +.textWrapper { + display: inline-block; +} + .selectedText, .notSelectedText { @apply --marketplaceBodyFontStyles; + line-height: var(--lineHeight); margin: 0; } @@ -35,15 +76,21 @@ .notSelectedText { color: var(--matterColorNegative); position: relative; - margin-left: 17px; -} -/* line-through */ -.notSelectedText::after { - position: absolute; - left: 0; - right: 0; - top: 58%; - content: ''; - border-bottom: solid 1px var(--matterColorNegative); + /* 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 index ea9e4fc2..8a4bbfd8 100644 --- a/src/components/PropertyGroup/PropertyGroup.example.js +++ b/src/components/PropertyGroup/PropertyGroup.example.js @@ -14,6 +14,7 @@ const exampleOptions = [ export const WithSomeSelected = { component: PropertyGroup, props: { + id: 'amenities', options: exampleOptions, selectedOptions: ['towels', 'bathroom', 'barbeque'], twoColumns: true, diff --git a/src/components/PropertyGroup/PropertyGroup.js b/src/components/PropertyGroup/PropertyGroup.js index a41f6a62..d24493ed 100644 --- a/src/components/PropertyGroup/PropertyGroup.js +++ b/src/components/PropertyGroup/PropertyGroup.js @@ -16,41 +16,56 @@ 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 = ( - - - -); +const IconCheck = props => { + const isVisible = props.isVisible; + const classes = isVisible ? css.checkIcon : classNames(css.checkIcon, css.hidden); + + return ( + + + + ); +}; const SelectedItem = props => { - const text = props.text; + const { text } = props; return ( -
  • - {iconCheck} - {text} +
  • +
    + +
    +
    + {text} +
  • ); }; const NotSelectedItem = props => { - const text = props.text; + const { text } = props; return (
  • - {text} +
    + +
    +
    + {text} +
  • ); }; const PropertyGroup = props => { - const { rootClassName, className, options, selectedOptions, twoColumns } = props; + const { rootClassName, className, id, options, selectedOptions, twoColumns } = props; const classes = classNames(rootClassName || css.root, className); const listClasses = twoColumns ? classNames(classes, css.twoColumns) : classes; @@ -61,9 +76,9 @@ const PropertyGroup = props => { {checked.map( option => option.isSelected ? ( - + ) : ( - + ) )} @@ -81,6 +96,7 @@ const { arrayOf, bool, node, shape, string } = PropTypes; PropertyGroup.propTypes = { rootClassName: string, className: string, + id: string.isRequired, options: arrayOf( shape({ value: string.isRequired, From c5c9050c024bd11532f1a87a4c684a955ce98b1b Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Tue, 23 Jan 2018 16:19:04 +0200 Subject: [PATCH 4/5] Refactor items into one internal component --- .../PropertyGroup/PropertyGroup.css | 13 ++----- src/components/PropertyGroup/PropertyGroup.js | 34 +++++-------------- 2 files changed, 10 insertions(+), 37 deletions(-) diff --git a/src/components/PropertyGroup/PropertyGroup.css b/src/components/PropertyGroup/PropertyGroup.css index 2c2f0ed9..1fef61c7 100644 --- a/src/components/PropertyGroup/PropertyGroup.css +++ b/src/components/PropertyGroup/PropertyGroup.css @@ -20,18 +20,9 @@ } .item { - display: flex; - padding: 2px 0; - - @media (--viewportMedium) { - padding: 4px 0; - } -} - -.itemSelected { - padding: 2px 0; display: flex; align-items: center; + padding: 2px 0; /* Fix broken multi-column layout in Chrome */ page-break-inside: avoid; @@ -55,7 +46,7 @@ .iconWrapper { display: inline-flex; - align-content: center; + align-items: center; } .textWrapper { diff --git a/src/components/PropertyGroup/PropertyGroup.js b/src/components/PropertyGroup/PropertyGroup.js index d24493ed..4679b53b 100644 --- a/src/components/PropertyGroup/PropertyGroup.js +++ b/src/components/PropertyGroup/PropertyGroup.js @@ -36,29 +36,16 @@ const IconCheck = props => { ); }; -const SelectedItem = props => { - const { text } = props; - return ( -
  • -
    - -
    -
    - {text} -
    -
  • - ); -}; - -const NotSelectedItem = props => { - const { text } = props; +const Item = props => { + const { text, isSelected } = props; + const textClass = isSelected ? css.selectedText : css.notSelectedText; return (
  • - +
    - {text} + {text}
  • ); @@ -73,14 +60,9 @@ const PropertyGroup = props => { return (
      - {checked.map( - option => - option.isSelected ? ( - - ) : ( - - ) - )} + {checked.map(option => ( + + ))}
    ); }; From 3a1de44c070d746ec58e1982fa587dffc9425c04 Mon Sep 17 00:00:00 2001 From: Hannu Lyytikainen Date: Tue, 23 Jan 2018 16:22:53 +0200 Subject: [PATCH 5/5] Remove redundant multi-column fix --- src/components/PropertyGroup/PropertyGroup.css | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/PropertyGroup/PropertyGroup.css b/src/components/PropertyGroup/PropertyGroup.css index 1fef61c7..8576fc2d 100644 --- a/src/components/PropertyGroup/PropertyGroup.css +++ b/src/components/PropertyGroup/PropertyGroup.css @@ -24,9 +24,6 @@ align-items: center; padding: 2px 0; - /* Fix broken multi-column layout in Chrome */ - page-break-inside: avoid; - @media (--viewportMedium) { padding: 4px 0; }