Skip to content

Commit 93c1062

Browse files
fix(SubstitutionRow): Add feedback for invalid substitution patterns.
1 parent 64c070d commit 93c1062

File tree

3 files changed

+45
-11
lines changed

3 files changed

+45
-11
lines changed

lib/manager/components/transform/NormalizeField.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
import Select from 'react-select'
1515

1616
import GtfsFieldSelector from '../GtfsFieldSelector'
17-
import SubstitutionRow, { isSubstitutionInvalid } from './SubstitutionRow'
17+
import SubstitutionRow, { isSubstitutionBlank, isSubstitutionInvalid } from './SubstitutionRow'
1818

1919
import type {
2020
NormalizeFieldFields,
@@ -39,6 +39,12 @@ export default class NormalizeField extends Component<TransformProps<NormalizeFi
3939
this._updateErrors()
4040
}
4141

42+
componentDidUpdate (prevProps: TransformProps<NormalizeFieldFields>) {
43+
if (prevProps.transformation !== this.props.transformation) {
44+
this._updateErrors()
45+
}
46+
}
47+
4248
_onChangeFieldToNormalize = (fieldOption: ReactSelectOption) => {
4349
this._updateTransformation({fieldName: fieldOption.value})
4450
}
@@ -70,17 +76,23 @@ export default class NormalizeField extends Component<TransformProps<NormalizeFi
7076
_getValidationErrors (fields: NormalizeFieldFields): Array<string> {
7177
const issues = []
7278
const { fieldName } = fields
79+
const { substitutions } = this.props.transformation
7380

7481
// fieldName must be defined.
7582
if (!fieldName || fieldName.length === 0) {
7683
issues.push('Field to normalize must be defined.')
7784
}
7885

7986
// The pattern for substitutions must not be null or empty.
80-
if (this.props.transformation.substitutions.filter(isSubstitutionInvalid).length > 0) {
87+
if (substitutions.filter(isSubstitutionBlank).length > 0) {
8188
issues.push('Substitution patterns must be defined.')
8289
}
8390

91+
// The pattern for substitutions must be valid.
92+
if (substitutions.filter(isSubstitutionInvalid).length > 0) {
93+
issues.push('Some substitution patterns are invalid.')
94+
}
95+
8496
return issues
8597
}
8698

@@ -233,6 +245,7 @@ export default class NormalizeField extends Component<TransformProps<NormalizeFi
233245
onChange={this._onChangeSubstitution}
234246
onEndEdit={this._onEndEditSubstitution}
235247
substitution={{
248+
invalid: false,
236249
pattern: '',
237250
replacement: ''
238251
}}

lib/manager/components/transform/SubstitutionRow.js

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ const buttonGroupStyle = { width: '60px' }
2929
* Extract just the subset of fields to build the state variable.
3030
*/
3131
function extractStateFields (props: Props): Substitution {
32-
const { description, normalizeSpace, pattern, replacement } = props.substitution
32+
const { description, invalid, normalizeSpace, pattern, replacement } = props.substitution
3333
return {
3434
description: description || '',
35+
invalid,
3536
normalizeSpace,
3637
pattern,
3738
replacement
@@ -41,10 +42,17 @@ function extractStateFields (props: Props): Substitution {
4142
/**
4243
* @returns true if the given substitution pattern is null or empty, false otherwise.
4344
*/
44-
export function isSubstitutionInvalid (substitution: Substitution) {
45+
export function isSubstitutionBlank (substitution: Substitution) {
4546
return !substitution.pattern || substitution.pattern === ''
4647
}
4748

49+
/**
50+
* @returns true if the given substitution is marked invalid by the backend, false otherwise.
51+
*/
52+
export function isSubstitutionInvalid (substitution: Substitution) {
53+
return substitution.invalid
54+
}
55+
4856
/**
4957
* Renders and lets user edit a substitution.
5058
*/
@@ -137,18 +145,29 @@ export default class SubstitutionRow extends Component<Props, Substitution> {
137145
}
138146

139147
render () {
140-
const { activeEditingIndex } = this.props
141-
const { description, normalizeSpace, pattern, replacement } = this.state
148+
const { activeEditingIndex, substitution: originalSubstitution } = this.props
149+
const { description, invalid, normalizeSpace, pattern, replacement } = this.state
142150
const regexTestLink = `https://regexr.com/?expression=${encodeURIComponent(pattern)}&text=${STRING_FOR_REGEXR_TEST}`
143151
const isEditing = this._isEditing()
144152
const allowEdit = activeEditingIndex === -1
145-
const isEditingInvalid = isSubstitutionInvalid(this.state)
153+
const isPatternBlank = isSubstitutionBlank(this.state)
154+
const isPatternInvalid = invalid && pattern === originalSubstitution.pattern
155+
const isEditingInvalid = isPatternBlank || isPatternInvalid
156+
let validationMessage = null
157+
if (isPatternBlank) {
158+
validationMessage = 'The substitution search pattern cannot be empty'
159+
} else if (isPatternInvalid) {
160+
validationMessage = `The substitution search pattern '${originalSubstitution.pattern}' is invalid`
161+
}
146162

147163
// Construct CSS class for row
148164
const editingClassName = isEditing ? '' : 'inactive'
149165
const allowEditClassName = allowEdit ? 'allow-edit' : ''
150166
const rowClassName = `substitution-row ${editingClassName} ${allowEditClassName}`
151167

168+
// Override style for inputs with invalid search patterns.
169+
const patternStyleOverride = isPatternInvalid ? { borderColor: 'inherit' } : null
170+
152171
return (
153172
<tr className={rowClassName}>
154173
<td>
@@ -163,10 +182,11 @@ export default class SubstitutionRow extends Component<Props, Substitution> {
163182
onKeyDown={this._onKeyDown}
164183
placeholder='Text (regex) to find'
165184
readOnly={!isEditing}
166-
title={isEditingInvalid ? 'Substitution search pattern cannot be empty' : null}
185+
style={patternStyleOverride}
186+
title={validationMessage}
167187
value={pattern}
168188
/>
169-
<InputGroup.Addon>
189+
<InputGroup.Addon style={patternStyleOverride}>
170190
<a
171191
href={regexTestLink}
172192
target='_blank'
@@ -217,9 +237,9 @@ export default class SubstitutionRow extends Component<Props, Substitution> {
217237
<ButtonGroup style={buttonGroupStyle}>
218238
<Button
219239
bsSize='xsmall'
220-
disabled={isEditingInvalid}
240+
disabled={isPatternInvalid}
221241
onClick={this._onClickSave}
222-
title={isEditingInvalid ? 'Unable to save, there are errors in this row.' : 'Save changes in this row'}
242+
title={isPatternInvalid ? 'Unable to save, there are errors in this row.' : 'Save changes in this row'}
223243
>
224244
<Icon type='check' />
225245
</Button>

lib/types/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ export type FetchFrequency = $Values<typeof FETCH_FREQUENCIES>
301301

302302
export type Substitution = {
303303
description?: string,
304+
invalid: boolean,
304305
normalizeSpace?: boolean,
305306
pattern: string,
306307
replacement: string

0 commit comments

Comments
 (0)