Skip to content

Commit 8340e2e

Browse files
author
Parth Shah
committed
Merge branch 'dev' of https://github.com/appirio-tech/connect-app into dev
2 parents a00fa32 + 540c6b7 commit 8340e2e

File tree

6 files changed

+119
-50
lines changed

6 files changed

+119
-50
lines changed

src/projects/detail/components/FeatureSelector/CustomFeatureForm.jsx

Lines changed: 67 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import _ from 'lodash'
33
import cn from 'classnames'
44
import Panel from '../../../../components/Panel/Panel'
55
import DeleteFeatureModal from './DeleteFeatureModal'
6-
import { Formsy, TCFormFields, SwitchButton } from 'appirio-tech-react-components'
6+
import { Icons, Formsy, TCFormFields, SwitchButton } from 'appirio-tech-react-components'
77

88
require('./FeatureForm.scss')
99

@@ -12,38 +12,41 @@ class CustomFeatureForm extends Component {
1212
constructor(props) {
1313
super(props)
1414
this.toggleFeature = this.toggleFeature.bind(this)
15-
this.state = { showDeleteModal : false }
15+
this.state = { showDeleteModal : false, editMode : false }
1616
this.onSave = this.onSave.bind(this)
17+
this.editFeature = this.editFeature.bind(this)
1718
this.onDelete = this.onDelete.bind(this)
1819
this.onDeleteIntent = this.onDeleteIntent.bind(this)
1920
this.onCancelDelete = this.onCancelDelete.bind(this)
21+
this.onChange = this.onChange.bind(this)
2022
}
2123

2224
componentWillMount() {
2325
this.componentWillReceiveProps(this.props)
2426
}
2527

2628
componentWillReceiveProps(nextProps) {
27-
const previousData = this.state.data || {}
2829
this.setState({
29-
data: nextProps.featureData || previousData,
30-
isActive: !!nextProps.featureData
30+
data: nextProps.featureData,
31+
isAdded: !!nextProps.featureData,
32+
isActive: !!nextProps.featureData && !nextProps.featureData.disabled,
33+
showDeleteModal: false
3134
})
3235
}
3336

3437
toggleFeature() {
35-
const { removeFeature, addFeature, featureData, isEdittable } = this.props
38+
const { addFeature, isEdittable } = this.props
3639
if (isEdittable) {
37-
if (this.state.isActive) {
38-
// remove feature
39-
removeFeature(featureData.id)
40-
} else {
41-
// add feature
42-
addFeature(this.state.data)
43-
}
40+
addFeature({ ...this.state.data, disabled: !!this.state.isActive })
4441
}
4542
}
4643

44+
editFeature() {
45+
this.setState({
46+
editMode : true
47+
})
48+
}
49+
4750
onDelete(data) {
4851
this.props.removeFeature(data.id)
4952
}
@@ -59,59 +62,82 @@ class CustomFeatureForm extends Component {
5962
onSave(data) {
6063
const { featureData } = this.props
6164
this.props.addFeature(_.merge({
62-
id: data.title.toLowerCase().replace(' ', '_'),
65+
id: data.title.toLowerCase().replace(/\s/g, '_'),
6366
categoryId: 'custom',
6467
notes: ''
6568
}, featureData, data))
69+
// assumes addFeature to be a synchronous call, otherwise it could lead to inconsistent UI state
70+
// e.g. if async addFeature fails, it would close the edit mode
71+
// this call is needed here because debounce call (for notes change) is closing the edit mode if
72+
// we do set the editMode in componentWillReceiveProps method
73+
this.setState({ editMode : false })
74+
}
75+
76+
onChange(fieldName, value) {
77+
const { featureData } = this.props
78+
// following check is needed to prevent adding the feature again after removing
79+
// because forms' onChange event gets fire with only form data when we lose focus from the form
80+
// alternative to this check is to put the change handler on textarea instead of form
81+
if (featureData) {// feature is already added
82+
const data = {}
83+
data[fieldName] = value
84+
this.props.updateFeature(_.merge({}, featureData, data))
85+
}
6686
}
6787

6888
render() {
69-
const { isEdittable, onCancel, featureData } = this.props
70-
const { data, isActive, showDeleteModal } = this.state
71-
const submitButton = !isActive
72-
? <button type="submit" className="tc-btn tc-btn-primary tc-btn-md" disabled={!isEdittable}>Save Feature</button>
73-
: <button type="submit" className="tc-btn tc-btn-default tc-btn-md" disabled={!isEdittable}>Delete Custom Feature</button>
74-
const formAction = isActive ? this.onDeleteIntent : this.onSave
89+
const { isEdittable, onCancel } = this.props
90+
const { data, isAdded, editMode, isActive, showDeleteModal } = this.state
91+
// const _debouncedOnChange = _.debounce(this.onChange, 2000, { trailing: true, maxWait: 10000 })
7592
const formClasses = cn('feature-form', {
7693
'modal-active': showDeleteModal
7794
})
7895
return (
7996
<Panel className={ formClasses }>
80-
<div className="feature-title-row">
81-
<span className="title">{ _.get(data, 'title', 'Define a new feature')}</span>
82-
<SwitchButton
83-
disabled={!isEdittable}
84-
onChange={ this.toggleFeature }
85-
name="featue-active"
86-
checked={isActive ? 'checked' : null }
87-
/>
88-
</div>
97+
{ (isAdded && !editMode) &&
98+
<div className="feature-title-row">
99+
<span className="title">{ _.get(data, 'title', 'Define a new feature')}</span>
100+
<div className="feature-actions">
101+
{ isAdded &&
102+
<SwitchButton
103+
disabled={!isEdittable}
104+
onChange={ this.toggleFeature }
105+
name="featue-active"
106+
checked={isActive ? 'checked' : null }
107+
label="Enable Feature"
108+
/>
109+
}
110+
<div className="separator"/>
111+
<button className="clean feature-edit-action" onClick={ this.editFeature }><Icons.IconUIPencil /></button>
112+
<button className="clean feature-delete-action" onClick={ this.onDeleteIntent }><Icons.IconUITrashSimple /></button>
113+
</div>
114+
</div>
115+
}
89116
<div className="feature-form-content">
90-
<Formsy.Form className="custom-feature-form" disabled={!isEdittable} onValidSubmit={ formAction }>
91-
{ !isActive &&
117+
<Formsy.Form className="custom-feature-form" disabled={!isEdittable} onValidSubmit={ this.onSave }>
118+
{ (!isAdded || editMode) &&
92119
<TCFormFields.TextInput
93120
name="title"
94121
label="Feature name"
95122
validations="minLength:1" required
96123
validationError="Feature name is required"
97124
wrapperClass="row"
98-
// placeholder="My awesome feature"
99125
value={ _.get(data, 'title', '') }
100126
/>
101127
}
102-
{ !isActive ?
128+
{ (isActive && !editMode) ?
103129
<TCFormFields.Textarea
104-
name="description"
105-
label="Feature description"
106-
wrapperClass="feature-description"
107-
// placeholder="Briefly describe the feature, including how it will be used, and provide examples that will help designers and developers understand it."
108-
value={ _.get(data, 'description', '') }
130+
name="notes"
131+
label="Feature Notes"
132+
wrapperClass="feature-notes"
133+
value={ _.get(data, 'notes', '') }
134+
onChange={ this.onChange }
109135
/>
110-
: <p className="feature-description">{ featureData.description }</p>
136+
: null
111137
}
112138
<div className="feature-form-actions">
113-
{ !isActive && <button type="button" className="tc-btn tc-btn-default tc-btn-md" onClick={ onCancel }>Cancel</button> }
114-
{ submitButton }
139+
{ (!isAdded || editMode) && <button type="button" className="tc-btn tc-btn-default tc-btn-md" onClick={ onCancel }>Cancel</button> }
140+
{ (!isAdded || editMode) && <button type="submit" className="tc-btn tc-btn-primary tc-btn-md" disabled={!isEdittable}>Save Feature</button> }
115141
</div>
116142
</Formsy.Form>
117143
</div>

src/projects/detail/components/FeatureSelector/DeleteFeatureModal.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import React, {PropTypes} from 'react'
22

33
const DeleteFeatureModal = ({ feature, onCancel, onConfirm }) => {
4+
const deleteFeature = () => {
5+
onConfirm(feature)
6+
}
47
return (
58
<div className="modal">
69
<div className="modal-title danger flex center">
@@ -13,7 +16,7 @@ const DeleteFeatureModal = ({ feature, onCancel, onConfirm }) => {
1316

1417
<div className="button-area flex center">
1518
<button className="tc-btn tc-btn-default tc-btn-sm btn-cancel" onClick={onCancel}>Cancel</button>
16-
<button className="tc-btn tc-btn-warning tc-btn-sm" onClick={onConfirm}>Delete Custom Feature</button>
19+
<button className="tc-btn tc-btn-warning tc-btn-sm" onClick={ deleteFeature }>Delete Custom Feature</button>
1720
</div>
1821
</div>
1922
</div>

src/projects/detail/components/FeatureSelector/FeatureForm.scss

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
display: flex;
1818
justify-content: space-between;
1919
margin-bottom: 4 * $base-unit;
20+
padding-bottom: 2 * $base-unit;
21+
border-bottom: 1px solid $tc-gray-10;
2022

2123
.title {
2224
@include tc-heading;
@@ -26,9 +28,33 @@
2628
color: $tc-black;
2729
white-space: nowrap;
2830
}
31+
32+
.separator {
33+
width: 1px;
34+
height: 25px;
35+
background-color: $tc-gray-10;
36+
margin: 0px 2 * $base-unit;
37+
}
38+
39+
.feature-actions {
40+
display: flex;
41+
align-items: center;
42+
43+
.feature-edit-action {
44+
margin-right: 2 * $base-unit;
45+
fill: $tc-gray-40;
46+
height: 16px;
47+
}
48+
49+
.feature-delete-action {
50+
fill: $tc-gray-40;
51+
height: 16px;
52+
}
53+
}
2954
}
3055

3156
.feature-form-content {
57+
3258
.feature-description {
3359
@include roboto;
3460
font-size: $tc-body-md;

src/projects/detail/components/FeatureSelector/FeaturePicker.jsx

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -263,11 +263,22 @@ class FeaturePicker extends Component {
263263
}
264264

265265
addFeature(feature) {
266-
const newState = update(this.state, {
267-
activeFeatureCount: {$set: this.state.activeFeatureCount + 1},
268-
activeFeatureList: { $push : [feature] },
269-
selectedFeatureId: { $set : feature.id }
270-
})
266+
let newState
267+
const featureIndex = _.findIndex(this.state.activeFeatureList, (f) => f.id === feature.id )
268+
// if feature is already added and is custom feature, update feature
269+
if (feature.categoryId === 'custom' && featureIndex >= 0) {
270+
newState = update(this.state, {
271+
activeFeatureCount: { $set: this.state.activeFeatureCount - 1 },
272+
activeFeatureList: { $splice : [[featureIndex, 1, feature]] },
273+
selectedFeatureId: { $set : feature.id }
274+
})
275+
} else {// add standard feature
276+
newState = update(this.state, {
277+
activeFeatureCount: {$set: this.state.activeFeatureCount + 1},
278+
activeFeatureList: { $push : [feature] },
279+
selectedFeatureId: { $set : feature.id }
280+
})
281+
}
271282
this.setState(newState)
272283
this.props.onSave(newState.activeFeatureList)
273284
}
@@ -277,7 +288,9 @@ class FeaturePicker extends Component {
277288
const idx = _.findIndex(this.state.activeFeatureList, f => f.id === featureId )
278289
const newState = update(this.state, {
279290
activeFeatureCount: {$set: this.state.activeFeatureCount - 1},
280-
activeFeatureList: { $splice: [[idx, 1]] }
291+
activeFeatureList: { $splice: [[idx, 1]] },
292+
showCutsomFeatureForm: { $set : false },
293+
selectedFeatureId: { $set : null }
281294
})
282295
this.setState(newState)
283296
this.props.onSave(newState.activeFeatureList)

src/projects/detail/components/FeatureSelector/FlattenedFeatureList.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const FlattenedFeatureList = ({ addedFeatures }) => {
1010
if (addedFeatures) {
1111
childrenDom = []
1212
addedFeatures.map((f, idx) => {
13+
if (f.disabled) return
1314
const feature = f.categoryId === 'custom' ? f : allFeaturesMap[f.id]
1415
if (feature.categoryId === 'custom') {
1516
feature.icon = require('./images/custom-features.svg')
@@ -19,7 +20,7 @@ const FlattenedFeatureList = ({ addedFeatures }) => {
1920
{feature.icon && <div className="icon-col">{ <img src={feature.icon} /> }</div>}
2021
<div className="content-col">
2122
<h4>{ feature.title }</h4>
22-
<p>{ feature.description }</p>
23+
<p>{ feature.categoryId === 'custom' ? feature.notes : feature.description }</p>
2324
</div>
2425
</div>
2526
)

src/projects/detail/components/FeatureSelector/PickerFeatureList.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class PickerFeatureList extends Component {
2020
allFeatures = _.filter(activeFeatureList, f => f.categoryId === id)
2121
}
2222
const renderFeature = (feature, idx) => {
23-
const isActive = _.findIndex(activeFeatureList, f => f.id === feature.id) > -1
23+
const isActive = _.findIndex(activeFeatureList, f => f.id === feature.id) > -1 && !feature.disabled
2424
const featureClasses = classNames('feature-list-feature', {
2525
'active-feature' : feature.id === selectedFeatureId,
2626
'selected-feature' : isActive

0 commit comments

Comments
 (0)