• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mac-s-g / react-json-view / #1223

pending completion
#1223

push

bhaveshjb
Build the changes.

286 of 359 branches covered (79.67%)

Branch coverage included in aggregate %.

505 of 608 relevant lines covered (83.06%)

44.31 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.35
/src/js/components/VariableEditor.js
1
import React from 'react';
2
import AutosizeTextarea from 'react-textarea-autosize';
3

4
import { toType } from './../helpers/util';
5
import dispatcher from './../helpers/dispatcher';
6
import parseInput from './../helpers/parseInput';
7
import stringifyVariable from './../helpers/stringifyVariable';
8
import CopyToClipboard from './CopyToClipboard';
9
import Select from 'react-select';
10

11
//data type components
12
import {
13
    JsonBoolean,
14
    JsonDate,
15
    JsonFloat,
16
    JsonFunction,
17
    JsonInteger,
18
    JsonNan,
19
    JsonNull,
20
    JsonRegexp,
21
    JsonString,
22
    JsonUndefined
23
} from './DataTypes/DataTypes';
24

25
//clibboard icon
26
import { Edit, CheckCircle, RemoveCircle as Remove } from './icons';
27

28
//theme
29
import Theme from './../themes/getStyle';
30

31
class VariableEditor extends React.PureComponent {
32
    constructor(props) {
33
        super(props);
34
        this.state = {
85✔
35
            editMode: false,
85✔
36
            editValue: '',
37
            hovered: false,
38
            renameKey: false,
39
            parsedInput: {
40
                type: false,
41
                value: null
42
            }
43
        };
44
    }
45

46
    render() {
47
        const {
48
            variable,
49
            singleIndent,
50
            type,
51
            theme,
52
            namespace,
53
            indentWidth,
54
            enableClipboard,
55
            onEdit,
56
            onDelete,
57
            onSelect,
58
            displayArrayKey,
59
            quotesOnKeys
60
        } = this.props;
61
        const { editMode } = this.state;
120✔
62
        return (
120✔
63
            <div
120✔
64
                {...Theme(theme, 'objectKeyVal', {
65
                    paddingLeft: indentWidth * singleIndent
66
                })}
67
                onMouseEnter={() =>
68
                    this.setState({ ...this.state, hovered: true })
69
                }
×
70
                onMouseLeave={() =>
71
                    this.setState({ ...this.state, hovered: false })
72
                }
×
73
                class="variable-row"
74
                key={variable.name}
75
            >
76
                {type == 'array' ? (
77
                    displayArrayKey ? (
120✔
78
                        <span
26✔
79
                            {...Theme(theme, 'array-key')}
80
                            key={variable.name + '_' + namespace}
81
                        >
82
                            {variable.name}
83
                            <div {...Theme(theme, 'colon')}>:</div>
84
                        </span>
85
                    ) : null
86
                ) : (
87
                    <span>
88
                        <span
89
                            {...Theme(theme, 'object-name')}
90
                            class="object-key"
91
                            key={variable.name + '_' + namespace}
92
                        >
93
                            {!!quotesOnKeys && (
94
                                <span style={{ verticalAlign: 'top' }}>"</span>
145✔
95
                            )}
96
                            <span style={{ display: 'inline-block' }}>
97
                                {variable.name}
98
                            </span>
99
                            {!!quotesOnKeys && (
100
                                <span style={{ verticalAlign: 'top' }}>"</span>
145✔
101
                            )}
102
                        </span>
103
                        <span {...Theme(theme, 'colon')}>:</span>
104
                    </span>
105
                )}
106
                <div
107
                    class="variable-value"
108
                    onClick={
109
                        onSelect === false && onEdit === false
110
                            ? null
299✔
111
                            : e => {
112
                                  let location = [...namespace];
113
                                  if (
×
114
                                      (e.ctrlKey || e.metaKey) &&
×
115
                                      onEdit !== false
×
116
                                  ) {
117
                                      this.prepopInput(variable);
118
                                  } else if (onSelect !== false) {
×
119
                                      location.shift();
×
120
                                      onSelect({
×
121
                                          ...variable,
×
122
                                          namespace: location
123
                                      });
124
                                  }
125
                              }
126
                    }
127
                    {...Theme(theme, 'variableValue', {
128
                        cursor: onSelect === false ? 'default' : 'pointer'
129
                    })}
120✔
130
                >
131
                    {this.getValue(variable, editMode)}
132
                </div>
133
                {enableClipboard ? (
134
                    <CopyToClipboard
120✔
135
                        rowHovered={this.state.hovered}
136
                        hidden={editMode}
137
                        src={variable.value}
138
                        clickCallback={enableClipboard}
139
                        {...{ theme, namespace: [...namespace, variable.name] }}
140
                    />
141
                ) : null}
142
                {onEdit !== false && editMode == false
143
                    ? this.getEditIcon()
300✔
144
                    : null}
145
                {onDelete !== false && editMode == false
146
                    ? this.getRemoveIcon()
301✔
147
                    : null}
148
            </div>
149
        );
150
    }
151

152
    getEditIcon = () => {
153
        const { variable, theme } = this.props;
85✔
154

44✔
155
        return (
156
            <div
44✔
157
                class="click-to-edit"
158
                style={{
159
                    verticalAlign: 'top',
160
                    display: this.state.hovered ? 'inline-block' : 'none'
161
                }}
44!
162
            >
163
                <Edit
164
                    class="click-to-edit-icon"
165
                    {...Theme(theme, 'editVarIcon')}
166
                    onClick={() => {
167
                        this.prepopInput(variable);
168
                    }}
13✔
169
                />
170
            </div>
171
        );
172
    };
173

174
    prepopInput = variable => {
175
        if (this.props.onEdit !== false) {
85✔
176
            const stringifiedValue = stringifyVariable(variable.value);
13!
177
            const detected = parseInput(stringifiedValue);
13✔
178
            this.setState({
13✔
179
                editMode: true,
13✔
180
                editValue: stringifiedValue,
181
                parsedInput: {
182
                    type: detected.type,
183
                    value: detected.value
184
                }
185
            });
186
        }
187
    };
188

189
    getRemoveIcon = () => {
190
        const { variable, namespace, theme, rjvId } = this.props;
85✔
191

45✔
192
        return (
193
            <div
45✔
194
                class="click-to-remove"
195
                style={{
196
                    verticalAlign: 'top',
197
                    display: this.state.hovered ? 'inline-block' : 'none'
198
                }}
45!
199
            >
200
                <Remove
201
                    class="click-to-remove-icon"
202
                    {...Theme(theme, 'removeVarIcon')}
203
                    onClick={() => {
204
                        dispatcher.dispatch({
205
                            name: 'VARIABLE_REMOVED',
×
206
                            rjvId: rjvId,
207
                            data: {
208
                                name: variable.name,
209
                                namespace: namespace,
210
                                existing_value: variable.value,
211
                                variable_removed: true
212
                            }
213
                        });
214
                    }}
215
                />
216
            </div>
217
        );
218
    };
219

220
    getValue = (variable, editMode) => {
221
        const type = editMode ? false : variable.type;
85✔
222
        const { props } = this;
120✔
223
        switch (type) {
120✔
224
            case false:
120!
225
                return this.getEditInput();
226
            case 'string':
16✔
227
                return <JsonString value={variable.value} {...props} />;
228
            case 'integer':
41✔
229
                return <JsonInteger value={variable.value} {...props} />;
230
            case 'float':
24✔
231
                return <JsonFloat value={variable.value} {...props} />;
232
            case 'boolean':
2✔
233
                return <JsonBoolean value={variable.value} {...props} />;
234
            case 'function':
9✔
235
                return <JsonFunction value={variable.value} {...props} />;
236
            case 'null':
5✔
237
                return <JsonNull {...props} />;
238
            case 'nan':
5✔
239
                return <JsonNan {...props} />;
240
            case 'undefined':
5✔
241
                return <JsonUndefined {...props} />;
242
            case 'date':
2✔
243
                return <JsonDate value={variable.value} {...props} />;
244
            case 'regexp':
×
245
                return <JsonRegexp value={variable.value} {...props} />;
246
            default:
4✔
247
                // catch-all for types that weren't anticipated
248
                return (
249
                    <div class="object-value">
7✔
250
                        {JSON.stringify(variable.value)}
251
                    </div>
252
                );
253
        }
254
    };
255

256
    getEditInput = () => {
257
        const { theme } = this.props;
85✔
258
        const { options } = this.props;
16✔
259
        const { editValue } = this.state;
16✔
260

16✔
261
        return (
262
            <div>
16✔
263
              {options && Array.isArray(options) ? (
264
                  <Select
32!
265
                    options={options}
266
                    placeholder='Select the state'
267
                    value={editValue}
268
                    onChange={data => {
269
                      const value = data.value;
270
                      const detected = parseInput(value);
×
271
                      this.setState({
×
272
                        editValue: value,
×
273
                        parsedInput: {
274
                          type: detected.type,
275
                          value: detected.value
276
                        }
277
                      });
278
                    }}
279
                    onKeyDown={e => {
280
                      switch (e.key) {
281
                        case 'Escape': {
×
282
                          this.setState({
283
                            editMode: false,
×
284
                            editValue: ''
285
                          });
286
                          break;
287
                        }
×
288
                        case 'Enter': {
289
                          if (e.ctrlKey || e.metaKey) {
290
                            this.submitEdit(true);
×
291
                          }
×
292
                          break;
293
                        }
×
294
                      }
295
                      e.stopPropagation();
296
                    }}
×
297
                  />
298
                ) : (
299
                <AutosizeTextarea
300
                  type="text"
301
                  inputRef={input => input && input.focus()}
302
                  value={editValue}
×
303
                  class="variable-editor"
304
                  onChange={event => {
305
                    const value = event.target.value;
306
                    const detected = parseInput(value);
×
307
                    this.setState({
×
308
                      editValue: value,
×
309
                      parsedInput: {
310
                        type: detected.type,
311
                        value: detected.value
312
                      }
313
                    });
314
                  }}
315
                  onKeyDown={e => {
316
                    switch (e.key) {
317
                      case 'Escape': {
×
318
                        this.setState({
319
                          editMode: false,
×
320
                          editValue: ''
321
                        });
322
                        break;
323
                      }
×
324
                      case 'Enter': {
325
                        if (e.ctrlKey || e.metaKey) {
326
                          this.submitEdit(true);
×
327
                        }
×
328
                        break;
329
                      }
×
330
                    }
331
                    e.stopPropagation();
332
                  }}
×
333
                  placeholder="update this value"
334
                  minRows={2}
335
                  {...Theme(theme, 'edit-input')}
336
                />
337
              )}
338
                <div {...Theme(theme, 'edit-icon-container')}>
339
                    <Remove
340
                        class="edit-cancel"
341
                        {...Theme(theme, 'cancel-icon')}
342
                        onClick={() => {
343
                            this.setState({ editMode: false, editValue: '' });
344
                        }}
2✔
345
                    />
346
                    <CheckCircle
347
                        class="edit-check string-value"
348
                        {...Theme(theme, 'check-icon')}
349
                        onClick={() => {
350
                            this.submitEdit();
351
                        }}
1✔
352
                    />
353
                    <div>{this.showDetected()}</div>
354
                </div>
355
            </div>
356
        );
357
    };
358

359
    submitEdit = submit_detected => {
360
        const { variable, namespace, rjvId } = this.props;
361
        const { editValue, parsedInput } = this.state;
85✔
362
        let new_value = editValue;
1✔
363
        if (submit_detected && parsedInput.type) {
1✔
364
            new_value = parsedInput.value;
1✔
365
        }
1!
366
        this.setState({
×
367
            editMode: false
368
        });
1✔
369
        dispatcher.dispatch({
370
            name: 'VARIABLE_UPDATED',
371
            rjvId: rjvId,
1✔
372
            data: {
373
                name: variable.name,
374
                namespace: namespace,
375
                existing_value: variable.value,
376
                new_value: new_value,
377
                variable_removed: false
378
            }
379
        });
380
    };
381

382
    showDetected = () => {
383
        const { theme, variable, namespace, rjvId } = this.props;
384
        const { type, value } = this.state.parsedInput;
85✔
385
        const detected = this.getDetectedInput();
16✔
386
        if (detected) {
16✔
387
            return (
16✔
388
                <div>
16✔
389
                    <div {...Theme(theme, 'detected-row')}>
8✔
390
                        {detected}
391
                        <CheckCircle
392
                            class="edit-check detected"
393
                            style={{
394
                                verticalAlign: 'top',
395
                                paddingLeft: '3px',
396
                                ...Theme(theme, 'check-icon').style
397
                            }}
398
                            onClick={() => {
399
                                this.submitEdit(true);
400
                            }}
401
                        />
×
402
                    </div>
403
                </div>
404
            );
405
        }
406
    };
407

408
    getDetectedInput = () => {
409
        const { parsedInput } = this.state;
410
        const { type, value } = parsedInput;
85✔
411
        const { props } = this;
16✔
412
        const { theme } = props;
16✔
413

16✔
414
        if (type !== false) {
16✔
415
            switch (type.toLowerCase()) {
416
                case 'object':
16✔
417
                    return (
8!
418
                        <span>
419
                            <span
1✔
420
                                style={{
421
                                    ...Theme(theme, 'brace').style,
422
                                    cursor: 'default'
423
                                }}
424
                            >
425
                                {'{'}
426
                            </span>
427
                            <span
428
                                style={{
429
                                    ...Theme(theme, 'ellipsis').style,
430
                                    cursor: 'default'
431
                                }}
432
                            >
433
                                ...
434
                            </span>
435
                            <span
436
                                style={{
437
                                    ...Theme(theme, 'brace').style,
438
                                    cursor: 'default'
439
                                }}
440
                            >
441
                                {'}'}
442
                            </span>
443
                        </span>
444
                    );
445
                case 'array':
446
                    return (
447
                        <span>
448
                            <span
1✔
449
                                style={{
450
                                    ...Theme(theme, 'brace').style,
451
                                    cursor: 'default'
452
                                }}
453
                            >
454
                                {'['}
455
                            </span>
456
                            <span
457
                                style={{
458
                                    ...Theme(theme, 'ellipsis').style,
459
                                    cursor: 'default'
460
                                }}
461
                            >
462
                                ...
463
                            </span>
464
                            <span
465
                                style={{
466
                                    ...Theme(theme, 'brace').style,
467
                                    cursor: 'default'
468
                                }}
469
                            >
470
                                {']'}
471
                            </span>
472
                        </span>
473
                    );
474
                case 'string':
475
                    return <JsonString value={value} {...props} />;
476
                case 'integer':
477
                    return <JsonInteger value={value} {...props} />;
×
478
                case 'float':
479
                    return <JsonFloat value={value} {...props} />;
2✔
480
                case 'boolean':
481
                    return <JsonBoolean value={value} {...props} />;
1✔
482
                case 'function':
483
                    return <JsonFunction value={value} {...props} />;
×
484
                case 'null':
485
                    return <JsonNull {...props} />;
×
486
                case 'nan':
487
                    return <JsonNan {...props} />;
1✔
488
                case 'undefined':
489
                    return <JsonUndefined {...props} />;
1✔
490
                case 'date':
491
                    return <JsonDate value={new Date(value)} {...props} />;
1✔
492
            }
493
        }
×
494
    };
495
}
496

497
//export component
498
export default VariableEditor;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc