From de484275e80f6e7c5e2c7d5da00c25eeb77d5cc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Andr=C3=A9=20Lelis?= Date: Fri, 27 Mar 2020 16:28:36 -0300 Subject: [PATCH] Creating combo input with label, naming and input for Text, Select and DateRange (#1553) * Issues: OHIF-46, OHIF-47, OHIF-48 --- platform/ui/index.js | 4 + .../InputDateRange/InputDateRange.jsx | 52 ++++ .../InputDateRange/InputDateRange.mdx | 43 +++ .../ui/src/components/InputDateRange/index.js | 2 + .../InputLabelWrapper/InputLabelWrapper.jsx | 63 ++++ .../src/components/InputLabelWrapper/index.js | 2 + .../InputMultiSelect/InputMultiSelect.jsx | 84 ++++++ .../InputMultiSelect/InputMultiSelect.mdx | 48 +++ .../src/components/InputMultiSelect/index.js | 2 + .../ui/src/components/InputText/InputText.jsx | 48 +++ .../ui/src/components/InputText/InputText.mdx | 41 +++ platform/ui/src/components/InputText/index.js | 2 + platform/ui/src/components/Select/Select.css | 25 +- platform/ui/src/components/Select/Select.jsx | 81 ++++- platform/ui/src/components/Select/Select.mdx | 22 +- platform/ui/src/components/index.js | 10 +- platform/ui/src/views/StudyList/StudyList.js | 52 +++- .../StudyList/components/StudyListFilter.js | 283 +++++++----------- 18 files changed, 646 insertions(+), 218 deletions(-) create mode 100644 platform/ui/src/components/InputDateRange/InputDateRange.jsx create mode 100644 platform/ui/src/components/InputDateRange/InputDateRange.mdx create mode 100644 platform/ui/src/components/InputDateRange/index.js create mode 100644 platform/ui/src/components/InputLabelWrapper/InputLabelWrapper.jsx create mode 100644 platform/ui/src/components/InputLabelWrapper/index.js create mode 100644 platform/ui/src/components/InputMultiSelect/InputMultiSelect.jsx create mode 100644 platform/ui/src/components/InputMultiSelect/InputMultiSelect.mdx create mode 100644 platform/ui/src/components/InputMultiSelect/index.js create mode 100644 platform/ui/src/components/InputText/InputText.jsx create mode 100644 platform/ui/src/components/InputText/InputText.mdx create mode 100644 platform/ui/src/components/InputText/index.js diff --git a/platform/ui/index.js b/platform/ui/index.js index 853de0534..164ee9850 100644 --- a/platform/ui/index.js +++ b/platform/ui/index.js @@ -15,6 +15,10 @@ export { Button, ButtonGroup, DateRange, + InputDateRange, + InputMultiSelect, + InputText, + InputLabelWrapper, EmptyStudies, Icon, IconButton, diff --git a/platform/ui/src/components/InputDateRange/InputDateRange.jsx b/platform/ui/src/components/InputDateRange/InputDateRange.jsx new file mode 100644 index 000000000..3424ab01f --- /dev/null +++ b/platform/ui/src/components/InputDateRange/InputDateRange.jsx @@ -0,0 +1,52 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { DateRange, InputLabelWrapper } from '@ohif/ui'; + +const InputDateRange = ({ + label, + isSortable, + sortDirection, + onLabelClick, + value, + onChange, +}) => { + const { startDate, endDate } = value; + return ( + +
+ +
+
+ ); +}; + +InputDateRange.defaultProps = { + value: {}, +}; + +InputDateRange.propTypes = { + label: PropTypes.string.isRequired, + isSortable: PropTypes.bool.isRequired, + sortDirection: PropTypes.oneOf(['ascending', 'descending', 'none']) + .isRequired, + onLabelClick: PropTypes.func.isRequired, + value: PropTypes.shape({ + /** Start date moment object */ + startDate: PropTypes.object, // moment date is an object + /** End date moment object */ + endDate: PropTypes.object, // moment date is an object + }), + onChange: PropTypes.func.isRequired, +}; + +export default InputDateRange; diff --git a/platform/ui/src/components/InputDateRange/InputDateRange.mdx b/platform/ui/src/components/InputDateRange/InputDateRange.mdx new file mode 100644 index 000000000..c7458d8ae --- /dev/null +++ b/platform/ui/src/components/InputDateRange/InputDateRange.mdx @@ -0,0 +1,43 @@ +--- +name: InputDateRange +menu: Components +route: components/InputDateRange +--- + +import { useState } from 'react'; +import { Playground, Props } from 'docz'; +import InputDateRange from './'; + +# Input Date Range + +## Import + +```javascript +import { InputDateRange } from '@ohif/ui'; +``` + +## Basic usage + + + {() => { + const [dates, setDates] = useState({ + startDate: null, + endDate: null, + }); + return ( +
+
+ setDates(dates)} + /> +
+
+ ); + }} +
+ +## Properties + + diff --git a/platform/ui/src/components/InputDateRange/index.js b/platform/ui/src/components/InputDateRange/index.js new file mode 100644 index 000000000..b620c15bf --- /dev/null +++ b/platform/ui/src/components/InputDateRange/index.js @@ -0,0 +1,2 @@ +import InputDateRange from './InputDateRange'; +export default InputDateRange; diff --git a/platform/ui/src/components/InputLabelWrapper/InputLabelWrapper.jsx b/platform/ui/src/components/InputLabelWrapper/InputLabelWrapper.jsx new file mode 100644 index 000000000..710555ba7 --- /dev/null +++ b/platform/ui/src/components/InputLabelWrapper/InputLabelWrapper.jsx @@ -0,0 +1,63 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import classnames from 'classnames'; + +import { Icon } from '@ohif/ui'; + +const baseLabelClassName = + 'flex flex-col flex-1 text-white text-lg pl-1 select-none'; +const spanClassName = 'flex flex-row items-center cursor-pointer'; +const sortIconMap = { + ascending: 'sorting-active-up', + descending: 'sorting-active-down', + none: 'sorting', +}; + +const InputLabelWrapper = ({ + label, + isSortable, + sortDirection, + onLabelClick, + className, + children, +}) => { + return ( + + ); +}; + +InputLabelWrapper.defaultProps = { + className: '', +}; + +InputLabelWrapper.propTypes = { + label: PropTypes.string.isRequired, + isSortable: PropTypes.bool.isRequired, + sortDirection: PropTypes.oneOf(['ascending', 'descending', 'none']) + .isRequired, + onLabelClick: PropTypes.func.isRequired, + className: PropTypes.string, + children: PropTypes.node, +}; + +export default InputLabelWrapper; diff --git a/platform/ui/src/components/InputLabelWrapper/index.js b/platform/ui/src/components/InputLabelWrapper/index.js new file mode 100644 index 000000000..fc2667073 --- /dev/null +++ b/platform/ui/src/components/InputLabelWrapper/index.js @@ -0,0 +1,2 @@ +import InputLabelWrapper from './InputLabelWrapper'; +export default InputLabelWrapper; diff --git a/platform/ui/src/components/InputMultiSelect/InputMultiSelect.jsx b/platform/ui/src/components/InputMultiSelect/InputMultiSelect.jsx new file mode 100644 index 000000000..69008dded --- /dev/null +++ b/platform/ui/src/components/InputMultiSelect/InputMultiSelect.jsx @@ -0,0 +1,84 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { Select, InputLabelWrapper } from '@ohif/ui'; + +const InputMultiSelect = ({ + label, + isSortable, + sortDirection, + onLabelClick, + value, + placeholder, + options, + onChange, +}) => { + return ( + + { + onChange(event.target.value); + }} + /> + + ); +}; + +InputText.defaultProps = { + value: '', +}; + +InputText.propTypes = { + label: PropTypes.string.isRequired, + isSortable: PropTypes.bool.isRequired, + sortDirection: PropTypes.oneOf(['ascending', 'descending', 'none']) + .isRequired, + onLabelClick: PropTypes.func.isRequired, + value: PropTypes.string, + onChange: PropTypes.func.isRequired, +}; + +export default InputText; diff --git a/platform/ui/src/components/InputText/InputText.mdx b/platform/ui/src/components/InputText/InputText.mdx new file mode 100644 index 000000000..8f7d27b27 --- /dev/null +++ b/platform/ui/src/components/InputText/InputText.mdx @@ -0,0 +1,41 @@ +--- +name: InputText +menu: Components +route: components/InputText +--- + +import { useState } from 'react'; +import { Playground, Props } from 'docz'; +import InputText from './'; + +# Input Text + +## Import + +```javascript +import { InputText } from '@ohif/ui'; +``` + +## Basic usage + + + {() => { + const [text, setText] = useState(''); + return ( +
+
+ {setText(value)}} + /> +
+
+ ) + }} + +
+ +## Properties + + diff --git a/platform/ui/src/components/InputText/index.js b/platform/ui/src/components/InputText/index.js new file mode 100644 index 000000000..174f89fe1 --- /dev/null +++ b/platform/ui/src/components/InputText/index.js @@ -0,0 +1,2 @@ +import InputText from './InputText'; +export default InputText; diff --git a/platform/ui/src/components/Select/Select.css b/platform/ui/src/components/Select/Select.css index f20565142..694bb287c 100644 --- a/platform/ui/src/components/Select/Select.css +++ b/platform/ui/src/components/Select/Select.css @@ -6,12 +6,13 @@ @apply border-gray-500; } -.customSelect__wrapper .customSelect__control:focus { +.customSelect__wrapper .customSelect__control:focus, +.customSelect__wrapper .customSelect__control--menu-is-open { @apply border-gray-500 outline-none; } -.customSelect__wrapper .customSelect__control--menu-is-open { - @apply border-gray-500 outline-none; +.customSelect--is-disabled .customSelect__control--is-disabled { + @apply pointer-events-none; } .customSelect__wrapper .customSelect__indicator-separator { @@ -23,17 +24,25 @@ } .customSelect__wrapper .customSelect__option { - @apply text-black; + @apply text-black flex flex-row items-center; +} + +.customSelect__wrapper .customSelect__option:focus { + @apply bg-custom-blue; +} + +.customSelect__wrapper .customSelect__option--is-selected { + @apply bg-transparent; } .customSelect__wrapper .customSelect__single-value { @apply text-white; } -.customSelect--is-disabled .customSelect__control--is-disabled { - @apply pointer-events-none; -} - .customSelect__wrapper.customSelect--is-disabled { @apply cursor-not-allowed pointer-events-auto; } + +.customSelect__wrapper .customSelect__value-container--is-multi { + @apply px-3 py-2 inline-block truncate; +} diff --git a/platform/ui/src/components/Select/Select.jsx b/platform/ui/src/components/Select/Select.jsx index 04a513c2d..c8c84638a 100644 --- a/platform/ui/src/components/Select/Select.jsx +++ b/platform/ui/src/components/Select/Select.jsx @@ -1,52 +1,93 @@ import React from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; -import ReactSelect from 'react-select'; +import ReactSelect, { components } from 'react-select'; import './Select.css'; +const MultiValue = props => { + const values = props.selectProps.value; + const lastValue = values[values.length - 1]; + let label = props.data.label; + if (lastValue.label !== label) { + label += ', '; + } + + return {label}; +}; + +const Option = props => { + return ( +
+ + null} + /> + + +
+ ); +}; + const Select = ({ - autoFocus, className, + closeMenuOnSelect, + hideSelectedOptions, + isClearable, isDisabled, isMulti, isSearchable, - name, onChange, options, placeholder, - noOptionsMessage, value, }) => { + const _components = isMulti ? { Option, MultiValue } : {}; + return ( ); }; +Select.defaultProps = { + className: '', + closeMenuOnSelect: true, + hideSelectedOptions: true, + isClearable: true, + isDisabled: false, + isMulti: false, + isSearchable: true, +}; + Select.propTypes = { - autoFocus: PropTypes.bool, className: PropTypes.string, + closeMenuOnSelect: PropTypes.bool, + hideSelectedOptions: PropTypes.bool, + isClearable: PropTypes.bool, isDisabled: PropTypes.bool, isMulti: PropTypes.bool, isSearchable: PropTypes.bool, - name: PropTypes.string, - onChange: PropTypes.func, + onChange: PropTypes.func.isRequired, options: PropTypes.arrayOf( PropTypes.shape({ value: PropTypes.string, @@ -54,8 +95,18 @@ Select.propTypes = { }) ), placeholder: PropTypes.string, - noOptionsMessage: PropTypes.func, - value: PropTypes.string, + value: PropTypes.oneOfType([ + PropTypes.arrayOf( + PropTypes.shape({ + value: PropTypes.string, + label: PropTypes.string, + }) + ), + PropTypes.shape({ + value: PropTypes.string, + label: PropTypes.string, + }), + ]), }; export default Select; diff --git a/platform/ui/src/components/Select/Select.mdx b/platform/ui/src/components/Select/Select.mdx index 29ab9701c..1a8f732f7 100644 --- a/platform/ui/src/components/Select/Select.mdx +++ b/platform/ui/src/components/Select/Select.mdx @@ -19,13 +19,13 @@ import { Select } from '@ohif/ui';
-
+
@@ -54,7 +54,7 @@ import { Select } from '@ohif/ui';
-
+
; - } - case 'text': { - return ( - handleFilterValueChange(event, name)} + { + setcurrentFiltersValues(prevState => ({ + ...prevState, + [name]: newValue, + })); + }} + /> + ); + break; + case 'MultiSelect': + return ( + { + setcurrentFiltersValues(prevState => ({ + ...prevState, + [name]: newValue, + })); + }} + /> + ); + case 'DateRange': + return ( + { + setcurrentFiltersValues(prevState => ({ + ...prevState, + [name]: newValue, + })); + }} + /> + ); + case 'None': + return ( + ); - } default: break; } @@ -194,7 +160,7 @@ const StudyListFilter = ({
- {isFiltering() && ( + {isFiltering(currentFiltersValues, filtersValues) && (
@@ -275,20 +220,24 @@ const StudyListFilter = ({ ); }; +StudyListFilter.defaultProps = { + filtersMeta: [], + filtersValues: {}, + numOfStudies: 0, +}; + StudyListFilter.propTypes = { filtersMeta: PropTypes.arrayOf( PropTypes.shape({ name: PropTypes.string, dsplayName: PropTypes.string, - inputType: PropTypes.oneOf(['text', 'select', 'date-range', 'none']), + inputType: PropTypes.oneOf(['Text', 'MultiSelect', 'DateRange', 'None']), isSortable: PropTypes.bool, gridCol: PropTypes.oneOf([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]), }) ), filtersValues: PropTypes.object, numOfStudies: PropTypes.number, - startDate: PropTypes.string, - endDate: PropTypes.string, }; export default StudyListFilter;