diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 3475b6771..48b143948 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -357,7 +357,7 @@ } }, "appirio-tech-react-components": { - "version": "github:appirio-tech/react-components#923908dbb7ae84b1dabd7d9fe47994e5459e1af5", + "version": "github:appirio-tech/react-components#ac2f6c5474d0334f5bdc0b84ad37f939ba8d7cbf", "requires": { "appirio-tech-api-schemas": "5.0.70", "appirio-tech-client-app-layer": "0.1.3", diff --git a/src/components/Dropdown/Dropdown.jsx b/src/components/Dropdown/Dropdown.jsx deleted file mode 100644 index dc7391841..000000000 --- a/src/components/Dropdown/Dropdown.jsx +++ /dev/null @@ -1,106 +0,0 @@ -require('./Dropdown.scss') - -import React, { Component, PropTypes } from 'react' -import classNames from 'classnames' - -class Dropdown extends Component { - constructor(props) { - super(props) - - this.state = { isHidden: true } - - this.onClickOutside = this.onClickOutside.bind(this) - this.onClick = this.onClick.bind(this) - this.onClickOtherDropdown = this.onClickOtherDropdown.bind(this) - } - - onClickOutside(evt) { - let currNode = evt.target - let isDropdown = false - - do { - if(currNode.className - && currNode.className.indexOf - && currNode.className.indexOf('dropdown-wrap') > -1) { - isDropdown = true - break - } - - currNode = currNode.parentNode - - if(!currNode) - break - } while(currNode.tagName) - - if(!isDropdown) { - this.setState({ isHidden: true }) - } - } - - onClick(evt) { - const dropdownClicked = document.createEvent('Event') - dropdownClicked.initEvent('dropdownClicked', true, false) - - document.dispatchEvent(dropdownClicked) - - this.setState({ isHidden: !this.state.isHidden }) - evt.stopPropagation() - } - - onClickOtherDropdown() { - this.setState({ isHidden: true }) - } - - componentDidMount() { - document.removeEventListener('click', this.onClickOutside) - document.removeEventListener('dropdownClicked', this.onClickOtherDropdown) - - document.addEventListener('click', this.onClickOutside) - document.addEventListener('dropdownClicked', this.onClickOtherDropdown) - } - - componentWillUnmount() { - document.removeEventListener('click', this.onClickOutside) - document.removeEventListener('dropdownClicked', this.onClickOtherDropdown) - } - - render() { - const { className, pointerShadow, noPointer, pointerLeft } = this.props - const ddClasses = classNames('dropdown-wrap', { - [`${className}`] : true, - [`${ this.props.theme }`] : true - }) - const ndClasses = classNames('Dropdown', { - 'pointer-shadow' : pointerShadow, - 'pointer-hide' : noPointer, - 'pointer-left' : pointerLeft, - hide : this.state.isHidden - }) - - return ( -
- { - this.props.children.map((child) => { - if (child.props.className.indexOf('dropdown-menu-header') > -1) - return child - }) - } - -
- { - this.props.children.map((child) => { - if (child.props.className.indexOf('dropdown-menu-list') > -1) - return child - }) - } -
-
- ) - } -} - -Dropdown.propTypes = { - children: PropTypes.array.isRequired -} - -export default Dropdown diff --git a/src/components/Dropdown/Dropdown.scss b/src/components/Dropdown/Dropdown.scss deleted file mode 100644 index 980735e63..000000000 --- a/src/components/Dropdown/Dropdown.scss +++ /dev/null @@ -1,158 +0,0 @@ -@import 'tc-includes'; - - -.dropdown-wrap { - cursor: pointer; - position: relative; -} - -.Dropdown { - background-color: #fff; - box-shadow: 0 2px 7px rgba(0, 0, 0, 0.17); - border-radius: 5px; - display: inline-block; - position: absolute; - left: 0; - top: 5px; - width: 100%; - z-index: 2; - - ul { - height: 100%; - width: 100%; - position: relative; - z-index: 10; - background-color: #fff; - padding: 11px 20px; - border-radius: 5px; - - li { - list-style: none; - } - - li a { - color: #394146; - font-family: "Roboto", Arial, Helvetica, sans-serif; - font-size: 12px; - display: block; - line-height: 26px; - } - } -} - -.dropdown-wrap.default { - border: 1px solid $tc-gray-20; - display: flex; - align-items: center; - padding: calc(2 * #{$base_unit}); - position: relative; - - .Dropdown { - ul.dropdown-menu-list { - padding: 10px 0px; - li { - padding: 0 20px; - @include ellipsis; - } - - li:hover { - background-color: $tc-gray-neutral-dark; - } - } - } -} - -.dropdown-wrap.default::after { - content: " "; - width: 10px; - height: 10px; - display: block; - right: 10px; - top: 50%; - position: absolute; - transform: translateY(-50%) rotate(45deg); - border-bottom: 2px solid $tc-gray-20; - border-right: 2px solid $tc-gray-20; -} - -.Dropdown.hide { - display: none; -} - -.Dropdown.pointer-left:before { - right: initial; - left: 15px; -} - -.UserDropdownMenu .Dropdown.pointer-shadow { - margin-top: 35px; - - &:before { - content: ''; - display: block; - position: absolute; - top: -6px; - right: 24px; - width: 12px; - height: 12px; - background: #FFFFFF; - border-right: 1px solid $tc-gray-20;; - border-bottom: 1px solid $tc-gray-20;; - transform: rotate(-135deg); - z-index:999; - } -} - -.Dropdown.pointer-hide:before { - display: none; -} - -.new-theme { - text-align: left; - height: 30px; - color: $tc-black; - background: $tc-gray-neutral-light; - border: 1px solid $tc-gray-20; - @include roboto; - font-size: 13px; - line-height: 20px; - width: 100%; - border-radius: 2px; - position: relative; - - .dropdown-menu-header { - width: 100%; - border: 0; - height: 28px; - line-height: 28px; - margin: 0; - padding: 0 0 0 10px; - color: $tc-gray-50; - font-size: 13px; - } - &:after{ - display: block; - content: ''; - position: absolute; - width: 10px; - height: 14px; - right: 11px; - top: 50%; - margin-top: -7px; - background: url("./icon-select.png") left top no-repeat; - background-size: 10px 14px; - z-index:2; - } - .Dropdown { - ul.dropdown-menu-list { - padding: 10px 0px; - li { - padding: 0 20px; - @include ellipsis; - } - li:hover { - background-color: $tc-gray-neutral-dark; - } - } - } -} diff --git a/src/components/Dropdown/arrow-dropdown.png b/src/components/Dropdown/arrow-dropdown.png deleted file mode 100644 index 2aa5059e9..000000000 Binary files a/src/components/Dropdown/arrow-dropdown.png and /dev/null differ diff --git a/src/components/Dropdown/icon-select.png b/src/components/Dropdown/icon-select.png deleted file mode 100644 index 8b58102b3..000000000 Binary files a/src/components/Dropdown/icon-select.png and /dev/null differ diff --git a/src/components/ProjectStatus/ProjectStatus.jsx b/src/components/ProjectStatus/ProjectStatus.jsx index df842fdd7..6317df082 100644 --- a/src/components/ProjectStatus/ProjectStatus.jsx +++ b/src/components/ProjectStatus/ProjectStatus.jsx @@ -1,149 +1,44 @@ -import React, { Component, PropTypes } from 'react' +import React, { PropTypes } from 'react' import cn from 'classnames' -import { PROJECT_STATUS } from '../../config/constants' -import SVGIconImage from '../SVGIconImage' import './ProjectStatus.scss' -export const enhanceDropdown = (CompositeComponent) => class extends Component { - constructor(props) { - super(props) - this.state = { isOpen: false } - this.handleClick = this.handleClick.bind(this) - this.onSelect = this.onSelect.bind(this) - this.onClickOutside = this.onClickOutside.bind(this) - this.onClickOtherDropdown = this.onClickOtherDropdown.bind(this) - this.refreshEventHandlers = this.refreshEventHandlers.bind(this) - } - - refreshEventHandlers() { - if (this.state.isOpen) { - document.addEventListener('click', this.onClickOutside) - document.addEventListener('dropdownClicked', this.onClickOtherDropdown) - } else { - document.removeEventListener('click', this.onClickOutside) - document.removeEventListener('dropdownClicked', this.onClickOtherDropdown) - } - } - - handleClick() { - const dropdownClicked = document.createEvent('Event') - dropdownClicked.initEvent('dropdownClicked', true, false) - - document.dispatchEvent(dropdownClicked) - - this.setState({ isOpen: !this.state.isOpen }, () => { - this.refreshEventHandlers() - }) - } - - onSelect(value) { - this.handleClick() - if (this.props.onSelect) this.props.onSelect(value) - } - - onClickOutside(evt) { - let currNode = evt.target - let isDropdown = false - console.log('onClickOutside') - - do { - if (currNode.className - && currNode.className.indexOf - && currNode.className.indexOf('dropdown-wrap') > -1) { - isDropdown = true - break - } - - currNode = currNode.parentNode - - if (!currNode) - break - } while (currNode.tagName) - - if (!isDropdown) { - this.setState({ isOpen: false }, () => { - this.refreshEventHandlers() - }) - } - } - - onClickOtherDropdown() { - this.setState({ isOpen: false }, () => { - this.refreshEventHandlers() - }) - } - - componentDidMount() { - document.removeEventListener('click', this.onClickOutside) - document.removeEventListener('dropdownClicked', this.onClickOtherDropdown) - - if (this.state.isOpen) { - document.addEventListener('click', this.onClickOutside) - document.addEventListener('dropdownClicked', this.onClickOtherDropdown) - } - } - - componentWillUnmount() { - document.removeEventListener('click', this.onClickOutside) - document.removeEventListener('dropdownClicked', this.onClickOtherDropdown) - } - - render() { - const { isOpen } = this.state - return ( -
e.stopPropagation()} className="dropdown-wrap"> - -
- ) - } -} - - /*eslint-enable camelcase */ -const ProjectStatus = ({ canEdit, isOpen, status, handleClick, onSelect, showText, withoutLabel, unifiedHeader = true }) => { - const selected = PROJECT_STATUS.filter((opt) => opt.value === status)[0] +const ProjectStatus = ({ status, showText, withoutLabel, unifiedHeader = true }) => { return ( -
-
-
- {showText && ({withoutLabel ? selected.fullName : selected.name})} -
- {isOpen && canEdit &&
-
Project Status
- -
- } +
+
+ {showText && ({withoutLabel ? status.fullName : status.name})}
) } ProjectStatus.propTypes = { - status: PropTypes.oneOf(['draft', 'active', 'in_review', 'reviewed', 'completed', 'paused', 'cancelled']).isRequired + // status: PropTypes.oneOf(['draft', 'active', 'in_review', 'reviewed', 'completed', 'paused', 'cancelled']).isRequired + /** + * Status object, containing name, fullName and value fields + */ + status : PropTypes.arrayOf(PropTypes.object).isRequired, + /** + * Boolean flag to render the status text + */ + showText : PropTypes.bool, + /** + * Boolean flag to render the more detailed status text (fullName field form the status object). + * Its main use case is the place where we don't show a label like `Project Status` before rendering + * this component. + */ + withoutLabel : PropTypes.bool, + /** + * Boolean flag to render a unified(with common background color) project status. It is added for backward + * compaitability only. We are not rendering this type of view of project status anymore. + */ + unifiedHeader : PropTypes.bool } ProjectStatus.defaultProps = { + showText : true, + withoutLabel : false, + unifiedHeader : false } export default ProjectStatus diff --git a/src/components/ProjectStatus/ProjectStatus.scss b/src/components/ProjectStatus/ProjectStatus.scss index c0f4debb4..6cc53a69d 100644 --- a/src/components/ProjectStatus/ProjectStatus.scss +++ b/src/components/ProjectStatus/ProjectStatus.scss @@ -19,7 +19,7 @@ $status-height : 20px; } } - .ps-draft { + &.ps-draft { .status-icon { i { background: url('../../assets/images/ps-draft.svg') no-repeat -7px -4px; @@ -28,7 +28,7 @@ $status-height : 20px; } } - .ps-in_review { + &.ps-in_review { .status-icon { i { background: url('../../assets/images/ps-in_review.svg') no-repeat -7px -4px; @@ -37,7 +37,7 @@ $status-height : 20px; } } - .ps-reviewed { + &.ps-reviewed { .status-icon { i { background: url('../../assets/images/ps-reviewed.svg') no-repeat -7px -4px; @@ -46,7 +46,7 @@ $status-height : 20px; } } - .ps-active { + &.ps-active { .status-icon { i { background: url('../../assets/images/ps-active.svg') no-repeat; @@ -55,7 +55,7 @@ $status-height : 20px; } } - .ps-cancelled { + &.ps-cancelled { .status-icon { i { background: url('../../assets/images/ps-cancelled.svg') no-repeat -7px -4px; @@ -64,7 +64,7 @@ $status-height : 20px; } } - .ps-completed { + &.ps-completed { .status-icon { i { background: url('../../assets/images/ps-completed.svg') no-repeat -7px -4px; @@ -73,7 +73,7 @@ $status-height : 20px; } } - .ps-paused { + &.ps-paused { .status-icon { i { background: url('../../assets/images/ps-paused.svg') no-repeat -7px -4px; @@ -82,6 +82,21 @@ $status-height : 20px; } } + .status-label { + position: relative; + @include tc-label-xs; + line-height: $base-unit*4; + height: $base-unit*4; + padding-right: 4 * $base-unit; + margin-left: 10px; + } +} + +.EditableProjectStatus { + + .status-label { + vertical-align: top; + } .status-header { display: flex; // position: absolute; diff --git a/src/components/ProjectStatus/editableProjectStatus.js b/src/components/ProjectStatus/editableProjectStatus.js index 2ea599e1a..a20d2b1e9 100644 --- a/src/components/ProjectStatus/editableProjectStatus.js +++ b/src/components/ProjectStatus/editableProjectStatus.js @@ -1,9 +1,12 @@ -import React, { Component} from 'react' +import React, { Component, PropTypes } from 'react' import ProjectStatusChangeConfirmation from './ProjectStatusChangeConfirmation' +import ProjectStatus from './ProjectStatus' import cn from 'classnames' import _ from 'lodash' -import { enhanceDropdown} from './ProjectStatus' +import { enhanceDropdown } from 'appirio-tech-react-components' +import SVGIconImage from '../SVGIconImage' import { + PROJECT_STATUS, PROJECT_STATUS_COMPLETED, PROJECT_STATUS_CANCELLED } from '../../config/constants' @@ -50,16 +53,54 @@ const editableProjectStatus = (CompositeComponent) => class extends Component { this.setState({ statusChangeReason : _.get(reason, 'value') }) } + renderDropdown(props) { + const { canEdit, isOpen, handleClick, onItemSelect, showText, withoutLabel, unifiedHeader, status } = props + const selected = PROJECT_STATUS.filter((opt) => opt.value === status)[0] + return ( +
+
+ + { canEdit && } +
+ { isOpen && canEdit && +
+
Project Status
+ +
+ } +
+ ) + } + render() { const { showStatusChangeDialog, newStatus, statusChangeReason } = this.state - const EnhancedProjectStatus = enhanceDropdown(CompositeComponent) + const { canEdit } = this.props + const ProjectStatusDropdown = canEdit ? enhanceDropdown(this.renderDropdown) : this.renderDropdown return ( -
+
- + { showStatusChangeDialog && class extends Component { } } +editableProjectStatus.propTypes = { + /** + * Boolean flag to control editability of the project status. It does not render the dropdown if it is not editable. + */ + canEdit: PropTypes.bool +} + export default editableProjectStatus \ No newline at end of file diff --git a/src/components/SelectDropdown/SelectDropdown.jsx b/src/components/SelectDropdown/SelectDropdown.jsx index b80914316..5c30a8a2e 100644 --- a/src/components/SelectDropdown/SelectDropdown.jsx +++ b/src/components/SelectDropdown/SelectDropdown.jsx @@ -3,7 +3,7 @@ require('./SelectDropdown.scss') import _ from 'lodash' import React, { Component, PropTypes } from 'react' import { HOC as hoc } from 'formsy-react' -import Dropdown from '../Dropdown/Dropdown' +import { Dropdown } from 'appirio-tech-react-components' class SelectDropdown extends Component { constructor(props) { diff --git a/src/components/SliderInput/SliderInput.js b/src/components/SliderInput/SliderInput.js deleted file mode 100644 index 030f78610..000000000 --- a/src/components/SliderInput/SliderInput.js +++ /dev/null @@ -1,67 +0,0 @@ -'use strict' - -import React, { Component, PropTypes } from 'react' -import Slider from 'rc-slider' -import 'rc-slider/assets/index.css' -import cn from 'classnames' -import _ from 'lodash' -import { HOC as hoc } from 'formsy-react' -import './SliderInput.scss' - -class SliderInput extends Component { - constructor(props) { - super(props) - this.onChange = this.onChange.bind(this) - } - - onChange(value) { - const {name, options} = this.props - const newValue = options[value].value - this.props.setValue(newValue) - this.props.onChange(name, newValue) - } - - noOp() {} - - getIndexFromValue(val) { - return _.findIndex(this.props.options, (t) => t.value === val) - } - - render() { - const { options, min, max, step} = this.props - const value = this.props.getValue() - const valueIdx = this.getIndexFromValue(value) - const marks = {} - for(let i=0; i < options.length; i++) { - marks[i] = options[i].title - } - return ( -
- -
- ) - } -} - -SliderInput.propTypes = { - options: PropTypes.arrayOf(PropTypes.object.isRequired).isRequired, - min: PropTypes.number.isRequired, - max: PropTypes.number.isRequired, - step: PropTypes.number.isRequired -} -SliderInput.defaultProps = { - onChange: () => {} -} -export default hoc(SliderInput) diff --git a/src/components/SliderInput/SliderInput.scss b/src/components/SliderInput/SliderInput.scss deleted file mode 100644 index 95944cd53..000000000 --- a/src/components/SliderInput/SliderInput.scss +++ /dev/null @@ -1,43 +0,0 @@ -@import 'tc-includes'; - -.SliderInput { - margin: 25px auto 0px auto; - .rc-slider-dot, - .rc-slider-handle { - background: $tc-white; - border: 4px solid $tc-gray-10; - border-radius: 18px; - width: 20px; - height: 20px; - bottom: -7px; - } - - .rc-slider-handle { - border-color: $tc-dark-blue-90; - margin-left: -4px; - bottom: -2px; - display: none; - } - - &:not(.null-value) .rc-slider-dot-active { - border: none; - background: $tc-dark-blue-90 url('../../../assets/images/check-white.svg') no-repeat 6px 7px; - // bottom: -2px; - // margin-left: -5px; - } - - .rc-slider-track, - .rc-slider-rail { - background-color: $tc-gray-10; - } - - .rc-slider-mark { - top: -30px; - .rc-slider-mark-text { - @include tc-label-lg; - line-height: 5 * $base_unit; - color: $tc-gray-80; - letter-spacing: 0; - } - } -} \ No newline at end of file diff --git a/src/projects/detail/components/SpecQuestionList/SpecQuestionList.scss b/src/projects/detail/components/SpecQuestionList/SpecQuestionList.scss index 6413d7a60..54d0b9ca0 100644 --- a/src/projects/detail/components/SpecQuestionList/SpecQuestionList.scss +++ b/src/projects/detail/components/SpecQuestionList/SpecQuestionList.scss @@ -106,7 +106,7 @@ margin: 15px 30px 15px 0; } - .SliderInput { + .SliderRadioGroup { margin: 40px auto 15px auto; width: 80%; } diff --git a/src/projects/detail/components/SpecQuestions.jsx b/src/projects/detail/components/SpecQuestions.jsx index b9a56d0bf..b6406f1f7 100644 --- a/src/projects/detail/components/SpecQuestions.jsx +++ b/src/projects/detail/components/SpecQuestions.jsx @@ -8,7 +8,6 @@ import SpecQuestionIcons from './SpecQuestionList/SpecQuestionIcons' import SpecFeatureQuestion from './SpecFeatureQuestion' import ColorSelector from './../../../components/ColorSelector/ColorSelector' import SelectDropdown from './../../../components/SelectDropdown/SelectDropdown' -import SliderInput from './../../../components/SliderInput/SliderInput' // HOC for TextareaInput const SeeAttachedTextareaInput = seeAttachedWrapperField(TCFormFields.Textarea) @@ -142,7 +141,7 @@ const SpecQuestions = ({questions, project, dirtyProject, resetFeatures, showFea }) break case 'slide-radiogroup': - ChildElem = SliderInput + ChildElem = TCFormFields.SliderRadioGroup _.assign(elemProps, { options: q.options, min: 0, diff --git a/src/projects/list/components/Projects/ProjectCardBody.jsx b/src/projects/list/components/Projects/ProjectCardBody.jsx index 0e7fcca0d..2687e7b77 100644 --- a/src/projects/list/components/Projects/ProjectCardBody.jsx +++ b/src/projects/list/components/Projects/ProjectCardBody.jsx @@ -8,6 +8,8 @@ import { PROJECT_STATUS_ACTIVE, PROJECT_ROLE_COPILOT, PROJECT_ROLE_MANAGER } fro import './ProjectCardBody.scss' import _ from 'lodash' +const EnhancedProjectStatus = editableProjectStatus(ProjectStatus) + function ProjectCardBody({ project, duration, currentMemberRole, descLinesCount = 8, onChangeStatus, isSuperUser }) { if (!project) return null @@ -17,8 +19,6 @@ function ProjectCardBody({ project, duration, currentMemberRole, descLinesCount const progress = _.get(process, 'percent', 0) - const EnhancedProjectStatus = canEdit ? editableProjectStatus(ProjectStatus) : ProjectStatus - return (