@ditojs/admin 3.0.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dito-admin.es.js +2043 -1956
- package/dist/dito-admin.umd.js +5 -6
- package/package.json +8 -6
- package/src/DitoAdmin.js +1 -65
- package/src/DitoTypeComponent.js +17 -0
- package/src/components/DitoButtons.vue +14 -1
- package/src/components/DitoDialog.vue +1 -1
- package/src/components/DitoForm.vue +3 -8
- package/src/components/DitoPanel.vue +3 -2
- package/src/components/DitoRoot.vue +1 -8
- package/src/components/DitoRouterView.vue +27 -0
- package/src/components/DitoView.vue +2 -7
- package/src/components/index.js +1 -0
- package/src/mixins/DitoMixin.js +28 -17
- package/src/mixins/OptionsMixin.js +14 -4
- package/src/mixins/ResourceMixin.js +4 -2
- package/src/mixins/RouteMixin.js +1 -1
- package/src/mixins/SourceMixin.js +13 -1
- package/src/types/DitoTypeMultiselect.vue +23 -12
- package/src/types/DitoTypePanel.vue +2 -1
- package/src/types/DitoTypeTreeList.vue +5 -11
- package/src/utils/DataSchemaResolver.js +13 -0
- package/src/utils/FormModel.js +91 -32
- package/src/utils/request.js +70 -0
- package/src/utils/schema/data.js +102 -37
- package/src/utils/schema/lookup.js +9 -25
- package/src/utils/schema/validation.js +2 -4
- package/types/index.d.ts +25 -0
- package/src/utils/DataSchemaResolver.test.js +0 -132
- package/src/utils/FormModel.test.js +0 -808
- package/src/utils/data.test.js +0 -28
- package/src/utils/schema/data.test.js +0 -471
- package/src/utils/schema/lookup.test.js +0 -61
- package/src/utils/schema/structure.test.js +0 -57
- package/src/utils/schema/validation.test.js +0 -395
- package/src/utils/units.test.js +0 -71
package/src/utils/data.test.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { getRelativeDataPath } from './data.js'
|
|
2
|
-
|
|
3
|
-
describe('getRelativeDataPath()', () => {
|
|
4
|
-
it('returns the part of the data path inside the parent data path', () => {
|
|
5
|
-
expect(getRelativeDataPath('chapters/0/title', 'chapters')).toBe(
|
|
6
|
-
'0/title'
|
|
7
|
-
)
|
|
8
|
-
expect(getRelativeDataPath('chapters/0', '')).toBe('chapters/0')
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
it('returns an empty string for the same data paths', () => {
|
|
12
|
-
expect(getRelativeDataPath('chapters', 'chapters')).toBe('')
|
|
13
|
-
expect(getRelativeDataPath('', '')).toBe('')
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
it('returns `null` for data paths outside the parent data path', () => {
|
|
17
|
-
expect(getRelativeDataPath('chapters', 'chapters/0')).toBe(null)
|
|
18
|
-
expect(getRelativeDataPath('chaptersList/0', 'chapters')).toBe(null)
|
|
19
|
-
expect(getRelativeDataPath('chapters', undefined)).toBe(null)
|
|
20
|
-
expect(getRelativeDataPath(undefined, 'chapters')).toBe(null)
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it('normalizes the data paths', () => {
|
|
24
|
-
expect(getRelativeDataPath('chapters[0].title', 'chapters/')).toBe(
|
|
25
|
-
'0/title'
|
|
26
|
-
)
|
|
27
|
-
})
|
|
28
|
-
})
|
|
@@ -1,471 +0,0 @@
|
|
|
1
|
-
import { vi } from 'vitest'
|
|
2
|
-
import DitoContext from '../../DitoContext.js'
|
|
3
|
-
import { registerTypeComponent } from './types.js'
|
|
4
|
-
import {
|
|
5
|
-
processSchemaData,
|
|
6
|
-
getComponentPathByDataPath,
|
|
7
|
-
computeValue,
|
|
8
|
-
processData,
|
|
9
|
-
initializeData,
|
|
10
|
-
shouldRenderSchema
|
|
11
|
-
} from './data.js'
|
|
12
|
-
|
|
13
|
-
// Register minimal type options, as the actual type components can't be
|
|
14
|
-
// imported without a Vue SFC compiler:
|
|
15
|
-
registerTypeComponent('text', { defaultNested: true })
|
|
16
|
-
registerTypeComponent('section', {
|
|
17
|
-
defaultNested: false,
|
|
18
|
-
defaultValue: ({ schema }) => (schema.nested ? {} : undefined),
|
|
19
|
-
treatNullAsMissing: ({ schema }) => !!schema.nested
|
|
20
|
-
})
|
|
21
|
-
registerTypeComponent('panel', { defaultNested: false })
|
|
22
|
-
registerTypeComponent('reference', {
|
|
23
|
-
defaultNested: true,
|
|
24
|
-
processValue: ({ value }) => value && { id: value.id }
|
|
25
|
-
})
|
|
26
|
-
registerTypeComponent('list', {
|
|
27
|
-
defaultNested: true,
|
|
28
|
-
getSourceType: () => 'list'
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
const chapters = {
|
|
32
|
-
type: 'list',
|
|
33
|
-
form: {
|
|
34
|
-
type: 'form',
|
|
35
|
-
components: { title: { type: 'text' } }
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const schema = {
|
|
40
|
-
type: 'form',
|
|
41
|
-
components: {
|
|
42
|
-
title: { type: 'text' },
|
|
43
|
-
publishing: {
|
|
44
|
-
type: 'section',
|
|
45
|
-
components: { publisher: { type: 'text' } }
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
tabs: {
|
|
49
|
-
structure: { type: 'tab', components: { chapters } },
|
|
50
|
-
layout: { type: 'tab', components: { chapters } }
|
|
51
|
-
},
|
|
52
|
-
panels: {
|
|
53
|
-
info: { type: 'panel', components: { note: { type: 'text' } } }
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const data = {
|
|
58
|
-
title: 'Book',
|
|
59
|
-
publisher: 'Publisher',
|
|
60
|
-
note: 'Note',
|
|
61
|
-
chapters: [{ title: 'One' }, { title: 'Two' }]
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
describe('processSchemaData()', () => {
|
|
65
|
-
it('passes the component paths of components, tabs and panels', () => {
|
|
66
|
-
const componentPaths = {}
|
|
67
|
-
processSchemaData(schema, data, {
|
|
68
|
-
dataPath: '',
|
|
69
|
-
componentPath: 'book',
|
|
70
|
-
shouldProcess: ({ componentPath, dataPath }) => {
|
|
71
|
-
componentPaths[componentPath] = dataPath
|
|
72
|
-
return true
|
|
73
|
-
},
|
|
74
|
-
options: { component: null }
|
|
75
|
-
})
|
|
76
|
-
expect(componentPaths).toEqual({
|
|
77
|
-
'book/title': 'title',
|
|
78
|
-
'book/publishing': '',
|
|
79
|
-
'book/publishing/publisher': 'publisher',
|
|
80
|
-
'book/structure': '',
|
|
81
|
-
'book/structure/chapters': 'chapters',
|
|
82
|
-
'book/structure/chapters/0/title': 'chapters/0/title',
|
|
83
|
-
'book/structure/chapters/1/title': 'chapters/1/title',
|
|
84
|
-
'book/layout': '',
|
|
85
|
-
'book/layout/chapters': 'chapters',
|
|
86
|
-
'book/layout/chapters/0/title': 'chapters/0/title',
|
|
87
|
-
'book/layout/chapters/1/title': 'chapters/1/title',
|
|
88
|
-
'book/info': '',
|
|
89
|
-
'book/info/note': 'note'
|
|
90
|
-
})
|
|
91
|
-
})
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
describe('processSchemaData() with panel components', () => {
|
|
95
|
-
it('walks the components of panel components with their data', () => {
|
|
96
|
-
const componentPaths = {}
|
|
97
|
-
processSchemaData(
|
|
98
|
-
{
|
|
99
|
-
type: 'form',
|
|
100
|
-
components: {
|
|
101
|
-
links: {
|
|
102
|
-
type: 'panel',
|
|
103
|
-
components: { website: { type: 'text' } }
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
{ website: 'https://example.com' },
|
|
108
|
-
{
|
|
109
|
-
dataPath: '',
|
|
110
|
-
componentPath: 'book',
|
|
111
|
-
shouldProcess: ({ componentPath, dataPath }) => {
|
|
112
|
-
componentPaths[componentPath] = dataPath
|
|
113
|
-
return true
|
|
114
|
-
},
|
|
115
|
-
options: { component: null }
|
|
116
|
-
}
|
|
117
|
-
)
|
|
118
|
-
expect(componentPaths).toEqual({
|
|
119
|
-
'book/links': '',
|
|
120
|
-
'book/links/website': 'website'
|
|
121
|
-
})
|
|
122
|
-
})
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
describe('processSchemaData() with panels that have their own data', () => {
|
|
126
|
-
it("doesn't walk their components with the data of the schema", () => {
|
|
127
|
-
const componentPaths = []
|
|
128
|
-
processSchemaData(
|
|
129
|
-
{
|
|
130
|
-
type: 'form',
|
|
131
|
-
components: {
|
|
132
|
-
search: {
|
|
133
|
-
type: 'panel',
|
|
134
|
-
data: () => ({ term: null }),
|
|
135
|
-
components: { term: { type: 'text' } }
|
|
136
|
-
}
|
|
137
|
-
},
|
|
138
|
-
panels: {
|
|
139
|
-
filters: {
|
|
140
|
-
type: 'panel',
|
|
141
|
-
data: () => ({ query: null }),
|
|
142
|
-
components: { query: { type: 'text' } }
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
{},
|
|
147
|
-
{
|
|
148
|
-
dataPath: '',
|
|
149
|
-
componentPath: '',
|
|
150
|
-
shouldProcess: ({ componentPath }) => {
|
|
151
|
-
componentPaths.push(componentPath)
|
|
152
|
-
return true
|
|
153
|
-
},
|
|
154
|
-
options: { component: null }
|
|
155
|
-
}
|
|
156
|
-
)
|
|
157
|
-
expect(componentPaths).toEqual(['search', 'filters'])
|
|
158
|
-
})
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
describe('getComponentPathByDataPath()', () => {
|
|
162
|
-
const getComponentPath = (dataPath, options) =>
|
|
163
|
-
getComponentPathByDataPath(schema, data, dataPath, options)
|
|
164
|
-
|
|
165
|
-
it('adds the names of tabs and unnested components', () => {
|
|
166
|
-
expect(getComponentPath('title')).toBe('title')
|
|
167
|
-
expect(getComponentPath('publisher')).toBe('publishing/publisher')
|
|
168
|
-
expect(getComponentPath('note')).toBe('info/note')
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
it('appends the item indices of list items', () => {
|
|
172
|
-
expect(getComponentPath('chapters/1')).toBe('structure/chapters/1')
|
|
173
|
-
expect(getComponentPath('chapters/1/title')).toBe(
|
|
174
|
-
'structure/chapters/1/title'
|
|
175
|
-
)
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
it('uses the first component in schema order', () => {
|
|
179
|
-
expect(getComponentPath('chapters')).toBe('structure/chapters')
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
it('appends the data path of values without component', () => {
|
|
183
|
-
expect(getComponentPath('unknown/0')).toBe('unknown/0')
|
|
184
|
-
expect(getComponentPath('chapters/0/unknown')).toBe(
|
|
185
|
-
'structure/chapters/0/unknown'
|
|
186
|
-
)
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
it('continues `componentPath`', () => {
|
|
190
|
-
expect(getComponentPath('chapters/0', { componentPath: 'main' })).toBe(
|
|
191
|
-
'main/structure/chapters/0'
|
|
192
|
-
)
|
|
193
|
-
expect(getComponentPathByDataPath(schema, null, 'chapters/0')).toBe(
|
|
194
|
-
'chapters/0'
|
|
195
|
-
)
|
|
196
|
-
})
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
describe('computeValue()', () => {
|
|
200
|
-
const schema = {
|
|
201
|
-
type: 'text',
|
|
202
|
-
compute: ({ item }) => item.title?.toUpperCase()
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
it('returns the computed value without writing it into the data', () => {
|
|
206
|
-
const data = { title: 'Book' }
|
|
207
|
-
expect(computeValue(schema, data, 'upper', 'upper')).toBe('BOOK')
|
|
208
|
-
expect(data).toEqual({ title: 'Book' })
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
it('returns the value if `compute()` returns `undefined`', () => {
|
|
212
|
-
const data = { upper: 'Kept' }
|
|
213
|
-
expect(computeValue(schema, data, 'upper', 'upper')).toBe('Kept')
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
it('returns the default of missing values without writing it', () => {
|
|
217
|
-
const data = {}
|
|
218
|
-
const schema = { type: 'text', default: 'Untitled' }
|
|
219
|
-
expect(computeValue(schema, data, 'title', 'title')).toBe('Untitled')
|
|
220
|
-
expect(data).toEqual({})
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
it('returns the defaults of nested sections that are `null`', () => {
|
|
224
|
-
const chooser = { type: 'section', nested: true, components: {} }
|
|
225
|
-
expect(
|
|
226
|
-
computeValue(chooser, { chooser: null }, 'chooser', 'chooser')
|
|
227
|
-
).toEqual(
|
|
228
|
-
{}
|
|
229
|
-
)
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
it('provides `context.options` through `getOptions()` when read', () => {
|
|
233
|
-
const options = ['Small', 'Large']
|
|
234
|
-
const getOptions = vi.fn(() => options)
|
|
235
|
-
const schema = {
|
|
236
|
-
type: 'text',
|
|
237
|
-
compute: context => context.value ?? context.options[0]
|
|
238
|
-
}
|
|
239
|
-
expect(
|
|
240
|
-
computeValue(schema, { size: 'Large' }, 'size', 'size', { getOptions })
|
|
241
|
-
).toBe('Large')
|
|
242
|
-
expect(getOptions).not.toHaveBeenCalled()
|
|
243
|
-
expect(computeValue(schema, {}, 'size', 'size', { getOptions })).toBe(
|
|
244
|
-
'Small'
|
|
245
|
-
)
|
|
246
|
-
expect(getOptions).toHaveBeenCalledTimes(1)
|
|
247
|
-
})
|
|
248
|
-
})
|
|
249
|
-
|
|
250
|
-
describe('processData()', () => {
|
|
251
|
-
it('processes computed values without writing into the data', () => {
|
|
252
|
-
const schema = {
|
|
253
|
-
type: 'form',
|
|
254
|
-
components: {
|
|
255
|
-
title: { type: 'text' },
|
|
256
|
-
upper: {
|
|
257
|
-
type: 'text',
|
|
258
|
-
compute: ({ item }) => item.title.toUpperCase()
|
|
259
|
-
},
|
|
260
|
-
subtitle: { type: 'text', default: 'None' }
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
const data = { title: 'Book' }
|
|
264
|
-
expect(
|
|
265
|
-
processData(schema, { type: 'list' }, data, '', { target: 'server' })
|
|
266
|
-
).toEqual({ title: 'Book', upper: 'BOOK', subtitle: 'None' })
|
|
267
|
-
expect(data).toEqual({ title: 'Book' })
|
|
268
|
-
})
|
|
269
|
-
|
|
270
|
-
describe('with `shouldCallComputeAndProcess: false`', () => {
|
|
271
|
-
// `DitoContext` needs a component to inherit from:
|
|
272
|
-
const component = {}
|
|
273
|
-
const options = {
|
|
274
|
-
component,
|
|
275
|
-
target: 'clipboard',
|
|
276
|
-
shouldCallComputeAndProcess: false
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
it('falls back to defaults without calling `compute()`', () => {
|
|
280
|
-
const compute = vi.fn(() => 'Computed')
|
|
281
|
-
const process = vi.fn(({ value }) => value)
|
|
282
|
-
const schema = {
|
|
283
|
-
type: 'form',
|
|
284
|
-
components: {
|
|
285
|
-
title: { type: 'text', process },
|
|
286
|
-
upper: { type: 'text', compute, default: 'None' }
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
expect(
|
|
290
|
-
processData(schema, { type: 'list' }, { title: 'Book' }, '', options)
|
|
291
|
-
).toEqual({ title: 'Book', upper: 'None' })
|
|
292
|
-
expect(compute).not.toHaveBeenCalled()
|
|
293
|
-
expect(process).not.toHaveBeenCalled()
|
|
294
|
-
})
|
|
295
|
-
|
|
296
|
-
it("processes values through their types' `processValue()`", () => {
|
|
297
|
-
const schema = {
|
|
298
|
-
type: 'form',
|
|
299
|
-
components: { author: { type: 'reference' } }
|
|
300
|
-
}
|
|
301
|
-
const data = { author: { id: 1, name: 'Author' } }
|
|
302
|
-
expect(processData(schema, { type: 'list' }, data, '', options)).toEqual({
|
|
303
|
-
author: { id: 1 }
|
|
304
|
-
})
|
|
305
|
-
})
|
|
306
|
-
|
|
307
|
-
it('keeps excluded values of components with `process()`', () => {
|
|
308
|
-
const schema = {
|
|
309
|
-
type: 'form',
|
|
310
|
-
components: {
|
|
311
|
-
tagsText: {
|
|
312
|
-
type: 'text',
|
|
313
|
-
exclude: true,
|
|
314
|
-
process: ({ value, processedItem }) => {
|
|
315
|
-
processedItem.tags = value.split(',')
|
|
316
|
-
}
|
|
317
|
-
},
|
|
318
|
-
search: { type: 'text', exclude: true }
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
const data = { tagsText: 'a,b', search: 'Search' }
|
|
322
|
-
expect(processData(schema, { type: 'list' }, data, '', options)).toEqual({
|
|
323
|
-
tagsText: 'a,b'
|
|
324
|
-
})
|
|
325
|
-
expect(
|
|
326
|
-
processData(schema, { type: 'list' }, data, '', {
|
|
327
|
-
component,
|
|
328
|
-
target: 'server'
|
|
329
|
-
})
|
|
330
|
-
).toEqual({ tags: ['a', 'b'] })
|
|
331
|
-
})
|
|
332
|
-
})
|
|
333
|
-
})
|
|
334
|
-
|
|
335
|
-
describe('shouldRenderSchema()', () => {
|
|
336
|
-
it("doesn't evaluate the components of sources for their items", () => {
|
|
337
|
-
// `DitoContext` needs a component to inherit from:
|
|
338
|
-
const context = new DitoContext({}, { data: { links: null }, dataPath: '' })
|
|
339
|
-
const condition = vi.fn(({ item }) => !!item.url)
|
|
340
|
-
const links = {
|
|
341
|
-
type: 'list',
|
|
342
|
-
components: { url: { type: 'text', if: condition } }
|
|
343
|
-
}
|
|
344
|
-
expect(shouldRenderSchema(links, context)).toBe(true)
|
|
345
|
-
expect(condition).not.toHaveBeenCalled()
|
|
346
|
-
})
|
|
347
|
-
|
|
348
|
-
it('hides sections whose components are all hidden', () => {
|
|
349
|
-
const context = new DitoContext({}, { data: {}, dataPath: '' })
|
|
350
|
-
const section = {
|
|
351
|
-
type: 'section',
|
|
352
|
-
components: { url: { type: 'text', if: () => false } }
|
|
353
|
-
}
|
|
354
|
-
expect(shouldRenderSchema(section, context)).toBe(false)
|
|
355
|
-
})
|
|
356
|
-
})
|
|
357
|
-
|
|
358
|
-
describe('computeValue() with if', () => {
|
|
359
|
-
it("doesn't compute values of components whose if is false", () => {
|
|
360
|
-
const schema = {
|
|
361
|
-
type: 'text',
|
|
362
|
-
if: ({ item }) => !!item.preview,
|
|
363
|
-
compute: ({ item }) => item.preview.name
|
|
364
|
-
}
|
|
365
|
-
expect(computeValue(schema, { name: 'Stored' }, 'name', 'name')).toBe(
|
|
366
|
-
'Stored'
|
|
367
|
-
)
|
|
368
|
-
expect(
|
|
369
|
-
computeValue(schema, { preview: { name: 'Preview' } }, 'name', 'name')
|
|
370
|
-
).toBe('Preview')
|
|
371
|
-
})
|
|
372
|
-
})
|
|
373
|
-
|
|
374
|
-
describe('initializeData()', () => {
|
|
375
|
-
it('passes through primitive values of lists with wrapPrimitives', () => {
|
|
376
|
-
const prices = {
|
|
377
|
-
type: 'list',
|
|
378
|
-
wrapPrimitives: 'price',
|
|
379
|
-
form: {
|
|
380
|
-
type: 'form',
|
|
381
|
-
components: { price: { type: 'text', default: 0 } }
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
const data = { prices: [100, 200] }
|
|
385
|
-
initializeData({ type: 'form', components: { prices } }, data)
|
|
386
|
-
expect(data).toEqual({ prices: [100, 200] })
|
|
387
|
-
})
|
|
388
|
-
|
|
389
|
-
it('sets the objects of nested sections that are `null`', () => {
|
|
390
|
-
const chooser = {
|
|
391
|
-
type: 'section',
|
|
392
|
-
nested: true,
|
|
393
|
-
components: { topic: { type: 'text' } }
|
|
394
|
-
}
|
|
395
|
-
const data = { chooser: null }
|
|
396
|
-
initializeData({ type: 'form', components: { chooser } }, data, {})
|
|
397
|
-
// The section's components are initialized in its object too:
|
|
398
|
-
expect(data.chooser).toHaveProperty('topic')
|
|
399
|
-
})
|
|
400
|
-
|
|
401
|
-
it('can leave the defaults of computed values to `computeValue()`', () => {
|
|
402
|
-
const pricing = {
|
|
403
|
-
type: 'text',
|
|
404
|
-
default: 'auto',
|
|
405
|
-
compute: ({ value, item }) => (
|
|
406
|
-
value ?? (item.customFactor != null ? 'factor' : undefined)
|
|
407
|
-
)
|
|
408
|
-
}
|
|
409
|
-
const schema = { type: 'form', components: { pricing } }
|
|
410
|
-
const data = { customFactor: 10 }
|
|
411
|
-
initializeData(schema, data, null, {
|
|
412
|
-
shouldSetDefaultsOfComponentsWithCompute: false
|
|
413
|
-
})
|
|
414
|
-
expect(data).toEqual({ customFactor: 10 })
|
|
415
|
-
// New data starts with all defaults:
|
|
416
|
-
expect(initializeData(schema, {})).toEqual({ pricing: 'auto' })
|
|
417
|
-
expect(computeValue(pricing, data, 'pricing', 'pricing')).toBe('factor')
|
|
418
|
-
expect(computeValue(pricing, {}, 'pricing', 'pricing')).toBe('auto')
|
|
419
|
-
})
|
|
420
|
-
|
|
421
|
-
it('numbers the items of lists with `orderKey`, also nested ones', () => {
|
|
422
|
-
const parts = {
|
|
423
|
-
type: 'list',
|
|
424
|
-
orderKey: 'order',
|
|
425
|
-
form: { type: 'form', components: { name: { type: 'text' } } }
|
|
426
|
-
}
|
|
427
|
-
const items = {
|
|
428
|
-
type: 'list',
|
|
429
|
-
orderKey: 'order',
|
|
430
|
-
// Lists with `compute()` are numbered too.
|
|
431
|
-
compute: ({ value }) => value,
|
|
432
|
-
form: { type: 'form', components: { parts } }
|
|
433
|
-
}
|
|
434
|
-
const data = {
|
|
435
|
-
items: [
|
|
436
|
-
{ order: 5, parts: [{ name: 'A', order: 2 }, { name: 'B', order: 7 }] },
|
|
437
|
-
{ order: 9, parts: [] }
|
|
438
|
-
]
|
|
439
|
-
}
|
|
440
|
-
initializeData(
|
|
441
|
-
{ type: 'form', tabs: { main: { type: 'tab', components: { items } } } },
|
|
442
|
-
data,
|
|
443
|
-
// `DitoContext` needs a component to inherit from:
|
|
444
|
-
{}
|
|
445
|
-
)
|
|
446
|
-
expect(data.items.map(item => item.order)).toEqual([0, 1])
|
|
447
|
-
expect(data.items[0].parts.map(part => part.order)).toEqual([0, 1])
|
|
448
|
-
})
|
|
449
|
-
|
|
450
|
-
it('leaves lists without `orderKey` and of primitives untouched', () => {
|
|
451
|
-
const form = { type: 'form', components: { name: { type: 'text' } } }
|
|
452
|
-
const schema = {
|
|
453
|
-
type: 'form',
|
|
454
|
-
components: {
|
|
455
|
-
items: { type: 'list', form },
|
|
456
|
-
prices: {
|
|
457
|
-
type: 'list',
|
|
458
|
-
orderKey: 'order',
|
|
459
|
-
wrapPrimitives: 'price',
|
|
460
|
-
form: { type: 'form', components: { price: { type: 'text' } } }
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
const data = { items: [{ name: 'A', order: 5 }], prices: [100, 200] }
|
|
465
|
-
initializeData(schema, data, {})
|
|
466
|
-
expect(data).toEqual({
|
|
467
|
-
items: [{ name: 'A', order: 5 }],
|
|
468
|
-
prices: [100, 200]
|
|
469
|
-
})
|
|
470
|
-
})
|
|
471
|
-
})
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { registerTypeComponent } from './types.js'
|
|
2
|
-
import { getAllPanelEntries } from './lookup.js'
|
|
3
|
-
|
|
4
|
-
// Register minimal type options, as the actual type components can't be
|
|
5
|
-
// imported without a Vue SFC compiler:
|
|
6
|
-
registerTypeComponent('panel', {
|
|
7
|
-
defaultNested: false,
|
|
8
|
-
getPanelSchema: (api, schema) => schema
|
|
9
|
-
})
|
|
10
|
-
registerTypeComponent('list', {
|
|
11
|
-
defaultNested: true,
|
|
12
|
-
getPanelSchema: () => ({ type: 'panel', name: '$filters' })
|
|
13
|
-
})
|
|
14
|
-
registerTypeComponent('section', { defaultNested: false })
|
|
15
|
-
|
|
16
|
-
describe('getAllPanelEntries()', () => {
|
|
17
|
-
const getPaths = entries =>
|
|
18
|
-
entries.map(({ dataPath, componentPath }) => ({ dataPath, componentPath }))
|
|
19
|
-
|
|
20
|
-
it('adds the names of panel components once to their paths', () => {
|
|
21
|
-
// Unnested components add their name to their component path, but not to
|
|
22
|
-
// their data path, so the panel is addressed relative to the data path.
|
|
23
|
-
const links = { type: 'panel', name: 'links' }
|
|
24
|
-
expect(
|
|
25
|
-
getPaths(getAllPanelEntries(null, links, 'book', 'main', 'main/links'))
|
|
26
|
-
).toEqual([{ dataPath: 'book/links', componentPath: 'main/links' }])
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('continues the paths of nested components for type panels', () => {
|
|
30
|
-
const books = { type: 'list', name: 'books' }
|
|
31
|
-
expect(
|
|
32
|
-
getPaths(
|
|
33
|
-
getAllPanelEntries(
|
|
34
|
-
null,
|
|
35
|
-
books,
|
|
36
|
-
'shelf/books',
|
|
37
|
-
'main/books',
|
|
38
|
-
'main/books'
|
|
39
|
-
)
|
|
40
|
-
)
|
|
41
|
-
).toEqual([
|
|
42
|
-
{
|
|
43
|
-
dataPath: 'shelf/books/$filters',
|
|
44
|
-
componentPath: 'main/books/$filters'
|
|
45
|
-
}
|
|
46
|
-
])
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('continues the component path of components for their panels', () => {
|
|
50
|
-
const section = {
|
|
51
|
-
type: 'section',
|
|
52
|
-
name: 'section',
|
|
53
|
-
panels: { info: { type: 'panel' } }
|
|
54
|
-
}
|
|
55
|
-
expect(
|
|
56
|
-
getPaths(
|
|
57
|
-
getAllPanelEntries(null, section, 'book', 'main', 'main/section')
|
|
58
|
-
)
|
|
59
|
-
).toEqual([{ dataPath: 'book/info', componentPath: 'main/section/info' }])
|
|
60
|
-
})
|
|
61
|
-
})
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { registerTypeComponent } from './types.js'
|
|
2
|
-
import { hasComponentNamed } from './structure.js'
|
|
3
|
-
|
|
4
|
-
// Register minimal type options, as the actual type components can't be
|
|
5
|
-
// imported without a Vue SFC compiler:
|
|
6
|
-
registerTypeComponent('text', { defaultNested: true })
|
|
7
|
-
registerTypeComponent('section', { defaultNested: false })
|
|
8
|
-
|
|
9
|
-
describe('hasComponentNamed()', () => {
|
|
10
|
-
const schema = {
|
|
11
|
-
type: 'form',
|
|
12
|
-
components: {
|
|
13
|
-
title: { type: 'text' },
|
|
14
|
-
unnested: {
|
|
15
|
-
type: 'section',
|
|
16
|
-
components: {
|
|
17
|
-
note: { type: 'text' },
|
|
18
|
-
inner: {
|
|
19
|
-
type: 'section',
|
|
20
|
-
components: { remark: { type: 'text' } }
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
nested: {
|
|
25
|
-
type: 'section',
|
|
26
|
-
nested: true,
|
|
27
|
-
components: { subtitle: { type: 'text' } }
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
tabs: {
|
|
31
|
-
tab: {
|
|
32
|
-
type: 'tab',
|
|
33
|
-
components: { edition: { type: 'text' } }
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
it('finds components and components in tabs', () => {
|
|
39
|
-
expect(hasComponentNamed(schema, 'title')).toBe(true)
|
|
40
|
-
expect(hasComponentNamed(schema, 'edition')).toBe(true)
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('finds components of unnested components', () => {
|
|
44
|
-
expect(hasComponentNamed(schema, 'note')).toBe(true)
|
|
45
|
-
expect(hasComponentNamed(schema, 'remark')).toBe(true)
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
it('finds nested components, but not their components', () => {
|
|
49
|
-
expect(hasComponentNamed(schema, 'nested')).toBe(true)
|
|
50
|
-
expect(hasComponentNamed(schema, 'subtitle')).toBe(false)
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it('ignores the names of unnested components', () => {
|
|
54
|
-
expect(hasComponentNamed(schema, 'unnested')).toBe(false)
|
|
55
|
-
expect(hasComponentNamed(schema, 'unknown')).toBe(false)
|
|
56
|
-
})
|
|
57
|
-
})
|