Skip to content

Commit 3a1f552

Browse files
aaroncannoncvKikobeats
authored andcommitted
Escape special characters in strings to render as expected for json
1 parent d5bc8af commit 3a1f552

File tree

8 files changed

+116
-23
lines changed

8 files changed

+116
-23
lines changed

dev-server/src/index.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,19 @@ ReactDom.render(
180180
}
181181
src={getExampleJson2()}
182182
/>
183+
184+
{/* String with special escape sequences */}
185+
<JsonViewer
186+
collapsed
187+
name='String with special escape sequences'
188+
src={getExampleWithStringEscapeSequences()}
189+
onEdit={e => {
190+
console.log('edit callback', e)
191+
if (e.new_value == 'error') {
192+
return false
193+
}
194+
}}
195+
/>
183196
</div>,
184197
document.getElementById('app-container')
185198
)
@@ -189,7 +202,7 @@ ReactDom.render(
189202
/*-------------------------------------------------------------------------*/
190203

191204
//just a function to get an example JSON object
192-
function getExampleJson1 () {
205+
function getExampleJson1() {
193206
return {
194207
string: 'this is a test string',
195208
integer: 42,
@@ -218,7 +231,7 @@ function getExampleJson1 () {
218231
}
219232

220233
//and another a function to get an example JSON object
221-
function getExampleJson2 () {
234+
function getExampleJson2() {
222235
return {
223236
normalized: {
224237
'1-grams': {
@@ -256,7 +269,7 @@ function getExampleJson2 () {
256269
}
257270
}
258271

259-
function getExampleJson3 () {
272+
function getExampleJson3() {
260273
return {
261274
example_information:
262275
'this example has the collapsed prop set to true and the indentWidth prop is set to 8',
@@ -272,7 +285,7 @@ function getExampleJson3 () {
272285
}
273286
}
274287

275-
function getExampleJson4 () {
288+
function getExampleJson4() {
276289
const large_array = new Array(225).fill('this is a large array full of items')
277290

278291
large_array.push(getExampleArray())
@@ -282,7 +295,7 @@ function getExampleJson4 () {
282295
return large_array
283296
}
284297

285-
function getExampleArray () {
298+
function getExampleArray() {
286299
return [
287300
'you can also display arrays!',
288301
new Date(),
@@ -294,3 +307,7 @@ function getExampleArray () {
294307
}
295308
]
296309
}
310+
311+
function getExampleWithStringEscapeSequences() {
312+
return { '\\\n\t\r\f\\n': '\\\n\t\r\f\\n' }
313+
}

docs/src/js/entry.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ require('./../style/scss/global.scss')
55

66
const app = document.getElementById('mac-react-container')
77

8-
//app entrypoint
8+
// app entrypoint
99
ReactDom.render(
1010
<div class='app-entry'>
1111
<Index />

src/js/components/DataTypes/String.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react'
22
import DataTypeLabel from './DataTypeLabel'
3-
import { toType } from './../../helpers/util'
3+
import { toType, escapeString } from './../../helpers/util'
44

55
// theme
66
import Theme from './../../themes/getStyle'
@@ -46,6 +46,8 @@ export default class extends React.PureComponent {
4646
const collapsible = toType(collapseStringsAfterLength) === 'integer'
4747
const style = { style: { cursor: 'default' } }
4848

49+
value = escapeString(value)
50+
4951
if (collapsible && value.length > collapseStringsAfterLength) {
5052
style.style.cursor = 'pointer'
5153
if (this.state.collapsed) {

src/js/components/VariableEditor.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react'
22
import AutosizeTextarea from 'react-textarea-autosize'
33

4-
import { toType } from './../helpers/util'
4+
import { escapeString } from './../helpers/util'
55
import dispatcher from './../helpers/dispatcher'
66
import parseInput from './../helpers/parseInput'
77
import stringifyVariable from './../helpers/stringifyVariable'
@@ -93,7 +93,7 @@ class VariableEditor extends React.PureComponent {
9393
{!!quotesOnKeys && (
9494
<span style={{ verticalAlign: 'top' }}>"</span>
9595
)}
96-
<span style={{ display: 'inline-block' }}>{variable.name}</span>
96+
<span style={{ display: 'inline-block' }}>{escapeString(variable.name)}</span>
9797
{!!quotesOnKeys && (
9898
<span style={{ verticalAlign: 'top' }}>"</span>
9999
)}

src/js/helpers/util.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ function getType (obj) {
2323
.toLowerCase()
2424
}
2525

26+
export function escapeString (value) {
27+
return value
28+
.replace(/\\/g, '\\\\')
29+
.replace(/\n/g, '\\n')
30+
.replace(/\t/g, '\\t')
31+
.replace(/\r/g, '\\r')
32+
.replace(/\f/g, '\\f')
33+
}
34+
2635
// validation for base-16 themes
2736
export function isTheme (theme) {
2837
const theme_keys = [

test/tests/js/components/DataTypes/String-test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,18 @@ describe('<JsonString />', function () {
6161
'"123456789"'
6262
)
6363
})
64+
65+
it('string with special escape sequences', function () {
66+
const rjvId = 1
67+
const props = {
68+
value: '\\\n\t\r\f\\n',
69+
rjvId: 1,
70+
displayDataTypes: false,
71+
theme: 'rjv-default'
72+
}
73+
const component = mount(<JsonString {...props} />).render()
74+
expect(component.find('.string-value').text()).to.equal(
75+
'"\\\\\\n\\t\\r\\f\\\\n"'
76+
)
77+
})
6478
})

test/tests/js/components/VariableEditor-test.js

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ describe('<VariableEditor />', function () {
1313
<VariableEditor
1414
src={{ test: true }}
1515
theme='rjv-default'
16-
onEdit={edit => {}}
16+
onEdit={edit => { }}
1717
rjvId={rjvId}
1818
singleIndent={1}
1919
variable={{
@@ -48,7 +48,7 @@ describe('<VariableEditor />', function () {
4848
<VariableEditor
4949
src={{ test: true }}
5050
theme='rjv-default'
51-
onEdit={edit => {}}
51+
onEdit={edit => { }}
5252
rjvId={rjvId}
5353
variable={{
5454
name: 'test',
@@ -66,7 +66,7 @@ describe('<VariableEditor />', function () {
6666
<VariableEditor
6767
src={{ test: true }}
6868
theme='rjv-default'
69-
onEdit={edit => {}}
69+
onEdit={edit => { }}
7070
rjvId={rjvId}
7171
variable={{
7272
name: 'test',
@@ -172,7 +172,7 @@ describe('<VariableEditor />', function () {
172172
<VariableEditor
173173
src={{ test: true }}
174174
theme='rjv-default'
175-
onEdit={edit => {}}
175+
onEdit={edit => { }}
176176
rjvId={rjvId}
177177
variable={{
178178
name: 'test',
@@ -192,7 +192,7 @@ describe('<VariableEditor />', function () {
192192
<VariableEditor
193193
src={{ test: true }}
194194
theme='rjv-default'
195-
onEdit={edit => {}}
195+
onEdit={edit => { }}
196196
rjvId={rjvId}
197197
variable={{
198198
name: 'test',
@@ -212,7 +212,7 @@ describe('<VariableEditor />', function () {
212212
<VariableEditor
213213
src={{ test: true }}
214214
theme='rjv-default'
215-
onEdit={edit => {}}
215+
onEdit={edit => { }}
216216
rjvId={rjvId}
217217
variable={{
218218
name: 'test',
@@ -232,7 +232,7 @@ describe('<VariableEditor />', function () {
232232
<VariableEditor
233233
src={{ test: true }}
234234
theme='rjv-default'
235-
onEdit={edit => {}}
235+
onEdit={edit => { }}
236236
rjvId={rjvId}
237237
variable={{
238238
name: 'test',
@@ -252,7 +252,7 @@ describe('<VariableEditor />', function () {
252252
<VariableEditor
253253
src={{ test: true }}
254254
theme='rjv-default'
255-
onEdit={edit => {}}
255+
onEdit={edit => { }}
256256
rjvId={rjvId}
257257
variable={{
258258
name: 'test',
@@ -274,7 +274,7 @@ describe('<VariableEditor />', function () {
274274
<VariableEditor
275275
src={{ test: true }}
276276
theme='rjv-default'
277-
onEdit={edit => {}}
277+
onEdit={edit => { }}
278278
rjvId={rjvId}
279279
variable={{
280280
name: 'test',
@@ -294,7 +294,7 @@ describe('<VariableEditor />', function () {
294294
<VariableEditor
295295
src={{ test: true }}
296296
theme='rjv-default'
297-
onEdit={edit => {}}
297+
onEdit={edit => { }}
298298
rjvId={rjvId}
299299
variable={{
300300
name: 'test',
@@ -314,7 +314,7 @@ describe('<VariableEditor />', function () {
314314
<VariableEditor
315315
src={{ test: true }}
316316
theme='rjv-default'
317-
onEdit={edit => {}}
317+
onEdit={edit => { }}
318318
rjvId={rjvId}
319319
variable={{
320320
name: 'test',
@@ -334,7 +334,7 @@ describe('<VariableEditor />', function () {
334334
<VariableEditor
335335
src={{ test: true }}
336336
theme='rjv-default'
337-
onEdit={edit => {}}
337+
onEdit={edit => { }}
338338
rjvId={rjvId}
339339
variable={{
340340
name: 'test',
@@ -348,4 +348,28 @@ describe('<VariableEditor />', function () {
348348
expect(wrapper.state('editMode')).to.equal(true)
349349
expect(wrapper.find('.variable-editor').props().value).to.equal('5')
350350
})
351+
352+
it('VariableEditor renders escaped characters', function () {
353+
const wrapper = shallow(
354+
<VariableEditor
355+
src={{ test: true }}
356+
theme='rjv-default'
357+
onEdit={edit => { }}
358+
rjvId={rjvId}
359+
variable={{
360+
name: '\\\n\t\r\f\\n',
361+
value: '\\\n\t\r\f\\n',
362+
type: 'string'
363+
}}
364+
/>
365+
)
366+
console.log(wrapper.debug())
367+
expect(wrapper.find('.object-key').text()).to.equal('\\\\\\n\\t\\r\\f\\\\n')
368+
expect(wrapper.find('.click-to-edit-icon').length).to.equal(1)
369+
wrapper.find('.click-to-edit-icon').simulate('click')
370+
expect(wrapper.state('editMode')).to.equal(true)
371+
expect(wrapper.find('.variable-editor').props().value).to.equal(
372+
'\\\n\t\r\f\\n'
373+
)
374+
})
351375
})

test/tests/js/helpers/Util-test.js

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react'
22
import { expect } from 'chai'
33

4-
import { toType, isTheme } from './../../../../src/js/helpers/util'
4+
import { toType, isTheme, escapeString } from './../../../../src/js/helpers/util'
55

66
describe('toType', function () {
77
it('toType object', function () {
@@ -30,7 +30,7 @@ describe('toType', function () {
3030
})
3131

3232
it('toType function', function () {
33-
const test = () => {}
33+
const test = () => { }
3434
expect(toType(test)).to.equal('function')
3535
})
3636

@@ -60,6 +60,33 @@ describe('toType', function () {
6060
})
6161
})
6262

63+
describe('escapeString', function () {
64+
it('escape \\\\', function () {
65+
const test = '\\'
66+
expect(escapeString(test)).to.equal('\\\\')
67+
})
68+
it('escape \\n', function () {
69+
const test = '\n'
70+
expect(escapeString(test)).to.equal('\\n')
71+
})
72+
it('escape \\t', function () {
73+
const test = '\t'
74+
expect(escapeString(test)).to.equal('\\t')
75+
})
76+
it('escape \\r', function () {
77+
const test = '\r'
78+
expect(escapeString(test)).to.equal('\\r')
79+
})
80+
it('escape \\f', function () {
81+
const test = '\f'
82+
expect(escapeString(test)).to.equal('\\f')
83+
})
84+
it('escape \\\\n', function () {
85+
const test = '\\n'
86+
expect(escapeString(test)).to.equal('\\\\n')
87+
})
88+
})
89+
6390
describe('isTheme', function () {
6491
it('isTheme valid theme', function () {
6592
const test = {

0 commit comments

Comments
 (0)