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

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

20 Mar 2021 08:46AM UTC coverage: 83.246% (-0.09%) from 83.333%
#1381

push

taylordowns2000
remove extra quotations from clipboardValue

285 of 351 branches covered (81.2%)

Branch coverage included in aggregate %.

6 of 7 new or added lines in 1 file covered. (85.71%)

36 existing lines in 3 files now uncovered.

505 of 598 relevant lines covered (84.45%)

45.02 hits per line

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

69.41
/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

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

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

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

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

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

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

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

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

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

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

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

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

261
        return (
16✔
262
            <div>
263
                <AutosizeTextarea
264
                    type="text"
UNCOV
265
                    inputRef={input => input && input.focus()}
×
266
                    value={editValue}
267
                    class="variable-editor"
268
                    onChange={event => {
269
                        const value = event.target.value;
×
UNCOV
270
                        const detected = parseInput(value);
×
UNCOV
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': {
UNCOV
282
                                this.setState({
×
283
                                    editMode: false,
284
                                    editValue: ''
285
                                });
UNCOV
286
                                break;
×
287
                            }
288
                            case 'Enter': {
UNCOV
289
                                if (e.ctrlKey || e.metaKey) {
×
290
                                    this.submitEdit(true);
×
291
                                }
UNCOV
292
                                break;
×
293
                            }
294
                        }
UNCOV
295
                        e.stopPropagation();
×
296
                    }}
297
                    placeholder="update this value"
298
                    minRows={2}
299
                    {...Theme(theme, 'edit-input')}
300
                />
301
                <div {...Theme(theme, 'edit-icon-container')}>
302
                    <Remove
303
                        class="edit-cancel"
304
                        {...Theme(theme, 'cancel-icon')}
305
                        onClick={() => {
306
                            this.setState({ editMode: false, editValue: '' });
2✔
307
                        }}
308
                    />
309
                    <CheckCircle
310
                        class="edit-check string-value"
311
                        {...Theme(theme, 'check-icon')}
312
                        onClick={() => {
313
                            this.submitEdit();
1✔
314
                        }}
315
                    />
316
                    <div>{this.showDetected()}</div>
317
                </div>
318
            </div>
319
        );
320
    };
321

322
    submitEdit = submit_detected => {
85✔
323
        const { variable, namespace, rjvId } = this.props;
1✔
324
        const { editValue, parsedInput } = this.state;
1✔
325
        let new_value = editValue;
1✔
326
        if (submit_detected && parsedInput.type) {
1!
UNCOV
327
            new_value = parsedInput.value;
×
328
        }
329
        this.setState({
1✔
330
            editMode: false
331
        });
332
        dispatcher.dispatch({
1✔
333
            name: 'VARIABLE_UPDATED',
334
            rjvId: rjvId,
335
            data: {
336
                name: variable.name,
337
                namespace: namespace,
338
                existing_value: variable.value,
339
                new_value: new_value,
340
                variable_removed: false
341
            }
342
        });
343
    };
344

345
    showDetected = () => {
85✔
346
        const { theme, variable, namespace, rjvId } = this.props;
16✔
347
        const { type, value } = this.state.parsedInput;
16✔
348
        const detected = this.getDetectedInput();
16✔
349
        if (detected) {
16✔
350
            return (
8✔
351
                <div>
352
                    <div {...Theme(theme, 'detected-row')}>
353
                        {detected}
354
                        <CheckCircle
355
                            class="edit-check detected"
356
                            style={{
357
                                verticalAlign: 'top',
358
                                paddingLeft: '3px',
359
                                ...Theme(theme, 'check-icon').style
360
                            }}
361
                            onClick={() => {
UNCOV
362
                                this.submitEdit(true);
×
363
                            }}
364
                        />
365
                    </div>
366
                </div>
367
            );
368
        }
369
    };
370

371
    getDetectedInput = () => {
85✔
372
        const { parsedInput } = this.state;
16✔
373
        const { type, value } = parsedInput;
16✔
374
        const { props } = this;
16✔
375
        const { theme } = props;
16✔
376

377
        if (type !== false) {
16✔
378
            switch (type.toLowerCase()) {
8!
379
                case 'object':
380
                    return (
1✔
381
                        <span>
382
                            <span
383
                                style={{
384
                                    ...Theme(theme, 'brace').style,
385
                                    cursor: 'default'
386
                                }}
387
                            >
388
                                {'{'}
389
                            </span>
390
                            <span
391
                                style={{
392
                                    ...Theme(theme, 'ellipsis').style,
393
                                    cursor: 'default'
394
                                }}
395
                            >
396
                                ...
397
                            </span>
398
                            <span
399
                                style={{
400
                                    ...Theme(theme, 'brace').style,
401
                                    cursor: 'default'
402
                                }}
403
                            >
404
                                {'}'}
405
                            </span>
406
                        </span>
407
                    );
408
                case 'array':
409
                    return (
1✔
410
                        <span>
411
                            <span
412
                                style={{
413
                                    ...Theme(theme, 'brace').style,
414
                                    cursor: 'default'
415
                                }}
416
                            >
417
                                {'['}
418
                            </span>
419
                            <span
420
                                style={{
421
                                    ...Theme(theme, 'ellipsis').style,
422
                                    cursor: 'default'
423
                                }}
424
                            >
425
                                ...
426
                            </span>
427
                            <span
428
                                style={{
429
                                    ...Theme(theme, 'brace').style,
430
                                    cursor: 'default'
431
                                }}
432
                            >
433
                                {']'}
434
                            </span>
435
                        </span>
436
                    );
437
                case 'string':
UNCOV
438
                    return <JsonString value={value} {...props} />;
×
439
                case 'integer':
440
                    return <JsonInteger value={value} {...props} />;
2✔
441
                case 'float':
442
                    return <JsonFloat value={value} {...props} />;
1✔
443
                case 'boolean':
444
                    return <JsonBoolean value={value} {...props} />;
×
445
                case 'function':
UNCOV
446
                    return <JsonFunction value={value} {...props} />;
×
447
                case 'null':
448
                    return <JsonNull {...props} />;
1✔
449
                case 'nan':
450
                    return <JsonNan {...props} />;
1✔
451
                case 'undefined':
452
                    return <JsonUndefined {...props} />;
1✔
453
                case 'date':
UNCOV
454
                    return <JsonDate value={new Date(value)} {...props} />;
×
455
            }
456
        }
457
    };
458
}
459

460
//export component
461
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