11// @flow
22
33import Icon from '@conveyal/woonerf/components/icon'
4+ import { isEqual } from 'lodash'
45import React , { Component } from 'react'
56import {
67 Button ,
@@ -24,15 +25,44 @@ import type {
2425} from '../../../types'
2526
2627type State = {
27- activeEditingIndex : number
28+ activeEditingIndex : number ,
29+ // Holds the contents that should be shown in the SubstitutionRow component.
30+ activeSubstitution : ?Substitution ,
31+ // Last edited contents.
32+ previousEditingIndex : number ,
33+ previousSubstitution : ?Substitution
34+ }
35+
36+ /**
37+ * Determines whether two substitutions are equal (except for the valid field).
38+ */
39+ function areEqual ( sub1 : ?Substitution , sub2 : ?Substitution ) : boolean {
40+ if ( ! sub1 || ! sub2 ) return false
41+ return isEqual (
42+ {
43+ description : sub1 . description ,
44+ normalizeSpace : sub1 . normalizeSpace ,
45+ pattern : sub1 . pattern ,
46+ replacement : sub1 . replacement
47+ } ,
48+ {
49+ description : sub2 . description ,
50+ normalizeSpace : sub2 . normalizeSpace ,
51+ pattern : sub2 . pattern ,
52+ replacement : sub2 . replacement
53+ }
54+ )
2855}
2956
3057/**
3158 * Component that renders input fields for the NormalizeFieldTransformation.
3259 */
3360export default class NormalizeField extends Component < TransformProps < NormalizeFieldFields > , State > {
3461 state = {
35- activeEditingIndex : - 1
62+ activeEditingIndex : - 1 ,
63+ activeSubstitution : null ,
64+ previousEditingIndex : - 1 ,
65+ previousSubstitution : null
3666 }
3767
3868 componentDidMount ( ) {
@@ -42,6 +72,21 @@ export default class NormalizeField extends Component<TransformProps<NormalizeFi
4272 componentDidUpdate ( prevProps : TransformProps < NormalizeFieldFields > ) {
4373 if ( prevProps . transformation !== this . props . transformation ) {
4474 this . _updateErrors ( )
75+
76+ // If user entered an invalid substitution pattern and clicks save,
77+ // it will be rejected and the values entered will be lost.
78+ // To avoid that, keep the new row visible and in editing state.
79+ const { previousEditingIndex, previousSubstitution : editedSubstitution } = this . state
80+ const loadedSubstitution = this . props . transformation . substitutions [ previousEditingIndex ]
81+ if ( previousEditingIndex !== - 1 && ! areEqual ( editedSubstitution , loadedSubstitution ) ) {
82+ this . setState ( {
83+ activeEditingIndex : previousEditingIndex ,
84+ activeSubstitution : {
85+ ...editedSubstitution ,
86+ valid : false
87+ }
88+ } )
89+ }
4590 }
4691 }
4792
@@ -60,12 +105,17 @@ export default class NormalizeField extends Component<TransformProps<NormalizeFi
60105 _onChangeSubstitution = ( substitution : Substitution , index : number ) = > {
61106 const newSubstitutions = [ ] . concat ( this . props . transformation . substitutions )
62107 newSubstitutions [ index ] = substitution
108+ this . setState ( {
109+ activeSubstitution : null ,
110+ previousSubstitution : substitution
111+ } )
63112 this . _updateTransformation ( { substitutions : newSubstitutions } )
64113 }
65114
66115 _onRemoveSubstitution = ( index : number ) => {
67116 const newSubstitutions = [ ] . concat ( this . props . transformation . substitutions )
68117 newSubstitutions . splice ( index , 1 )
118+ this . setState ( { previousEditingIndex : - 1 } )
69119 this . _updateTransformation ( { substitutions : newSubstitutions } )
70120 }
71121
@@ -101,7 +151,11 @@ export default class NormalizeField extends Component<TransformProps<NormalizeFi
101151 }
102152
103153 _onEndEditSubstitution = ( ) => {
104- this . setState ( { activeEditingIndex : - 1 } )
154+ this . setState ( {
155+ activeEditingIndex : - 1 ,
156+ activeSubstitution : null ,
157+ previousEditingIndex : this . state . activeEditingIndex
158+ } )
105159 }
106160
107161 /**
@@ -129,7 +183,7 @@ export default class NormalizeField extends Component<TransformProps<NormalizeFi
129183
130184 render ( ) {
131185 const { index, transformation } = this . props
132- const { activeEditingIndex } = this . state
186+ const { activeEditingIndex, activeSubstitution } = this . state
133187 const {
134188 capitalizationExceptions,
135189 capitalize,
@@ -233,7 +287,7 @@ export default class NormalizeField extends Component<TransformProps<NormalizeFi
233287 onChange = { this . _onChangeSubstitution }
234288 onEndEdit = { this . _onEndEditSubstitution }
235289 onRemove = { this . _onRemoveSubstitution }
236- substitution = { s }
290+ substitution = { activeEditingIndex === i ? ( activeSubstitution || s ) : s }
237291 />
238292 ) ) }
239293 { activeEditingIndex === substitutions . length &&
@@ -244,10 +298,10 @@ export default class NormalizeField extends Component<TransformProps<NormalizeFi
244298 key = { activeEditingIndex }
245299 onChange = { this . _onChangeSubstitution }
246300 onEndEdit = { this . _onEndEditSubstitution }
247- substitution = { {
248- invalid : false ,
301+ substitution = { activeSubstitution || {
249302 pattern : '' ,
250- replacement : ''
303+ replacement : '' ,
304+ valid : true
251305 } }
252306 />
253307 }
0 commit comments