@ditojs/admin 2.93.0 → 2.95.0
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.css +1 -1
- package/dist/dito-admin.es.js +1672 -1522
- package/dist/dito-admin.umd.js +6 -6
- package/package.json +6 -6
- package/src/components/DitoAccount.vue +7 -1
- package/src/components/DitoCreateButton.vue +6 -2
- package/src/components/DitoDialog.vue +1 -1
- package/src/components/DitoErrors.vue +1 -0
- package/src/components/DitoForm.vue +1 -0
- package/src/components/DitoFormInner.vue +5 -0
- package/src/components/DitoHeader.vue +1 -1
- package/src/components/DitoLabel.vue +6 -1
- package/src/components/DitoMenu.vue +1 -0
- package/src/components/DitoNavigation.vue +1 -1
- package/src/components/DitoNotifications.vue +1 -1
- package/src/components/DitoPane.vue +3 -0
- package/src/components/DitoPanel.vue +2 -0
- package/src/components/DitoSchema.vue +1 -0
- package/src/components/DitoScopes.vue +1 -0
- package/src/components/DitoSpinner.vue +2 -0
- package/src/components/DitoTableHead.vue +10 -0
- package/src/components/DitoTabs.vue +18 -2
- package/src/components/DitoTrail.vue +2 -1
- package/src/components/DitoTreeItem.vue +22 -3
- package/src/mixins/DitoMixin.js +4 -2
- package/src/mixins/PulldownMixin.js +9 -0
- package/src/types/DitoTypeCheckboxes.vue +2 -0
- package/src/types/DitoTypeList.vue +2 -0
- package/src/types/DitoTypeMultiselect.vue +2 -0
- package/src/types/DitoTypeRadio.vue +2 -0
- package/src/types/DitoTypeUpload.vue +4 -4
- package/types/index.d.ts +74 -11
- package/types/tests/admin.test-d.ts +0 -27
- package/types/tests/component-buttons.test-d.ts +0 -44
- package/types/tests/component-list.test-d.ts +0 -159
- package/types/tests/component-misc.test-d.ts +0 -137
- package/types/tests/component-object.test-d.ts +0 -69
- package/types/tests/component-section.test-d.ts +0 -174
- package/types/tests/component-select.test-d.ts +0 -107
- package/types/tests/components.test-d.ts +0 -81
- package/types/tests/context.test-d.ts +0 -31
- package/types/tests/fixtures.ts +0 -24
- package/types/tests/form.test-d.ts +0 -109
- package/types/tests/instance.test-d.ts +0 -20
- package/types/tests/schema-features.test-d.ts +0 -402
- package/types/tests/variance.test-d.ts +0 -125
- package/types/tests/view.test-d.ts +0 -146
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { expectTypeOf, assertType, describe, it } from 'vitest'
|
|
2
|
-
import type { Component, Components, Form } from '../index.d.ts'
|
|
3
|
-
import type { Entry, Parent, ParentWithMarkers } from './fixtures.ts'
|
|
4
|
-
|
|
5
|
-
describe('Components', () => {
|
|
6
|
-
it('accepts data-only components', () => {
|
|
7
|
-
assertType<Components<Parent>>({
|
|
8
|
-
title: { type: 'text' }
|
|
9
|
-
})
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
it('infers array element type for list components', () => {
|
|
13
|
-
assertType<Components<Parent>>({
|
|
14
|
-
entries: {
|
|
15
|
-
type: 'list',
|
|
16
|
-
form: {
|
|
17
|
-
type: 'form',
|
|
18
|
-
components: {
|
|
19
|
-
title: { type: 'text' }
|
|
20
|
-
}
|
|
21
|
-
} satisfies Form<Entry>
|
|
22
|
-
}
|
|
23
|
-
})
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('accepts UI-only keys for non-data components', () => {
|
|
27
|
-
assertType<Components<ParentWithMarkers>>({
|
|
28
|
-
viewButton: {
|
|
29
|
-
type: 'button',
|
|
30
|
-
text: 'View',
|
|
31
|
-
events: {
|
|
32
|
-
click({ item }) {
|
|
33
|
-
expectTypeOf(item).not.toBeAny()
|
|
34
|
-
expectTypeOf(item).toHaveProperty('title')
|
|
35
|
-
expectTypeOf(item).toHaveProperty('entries')
|
|
36
|
-
expectTypeOf(item).not.toHaveProperty('viewButton')
|
|
37
|
-
expectTypeOf(item).not.toHaveProperty('spacer')
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
})
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
it('provides typed item in callbacks for data keys', () => {
|
|
45
|
-
assertType<Components<Parent>>({
|
|
46
|
-
title: {
|
|
47
|
-
type: 'text',
|
|
48
|
-
format({ item }) {
|
|
49
|
-
expectTypeOf(item).not.toBeAny()
|
|
50
|
-
expectTypeOf(item.title).toBeString()
|
|
51
|
-
expectTypeOf(item.entries).toEqualTypeOf<Entry[]>()
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
})
|
|
55
|
-
})
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
describe('Components negative tests', () => {
|
|
59
|
-
it('rejects unknown keys', () => {
|
|
60
|
-
assertType<Components<Parent>>({
|
|
61
|
-
title: { type: 'text' },
|
|
62
|
-
// @ts-expect-error 'nonExistent' is not a key of Parent
|
|
63
|
-
nonExistent: { type: 'text' }
|
|
64
|
-
})
|
|
65
|
-
})
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
describe('Components<any> compatibility', () => {
|
|
69
|
-
it('Components<Specific> is assignable to Components<any>', () => {
|
|
70
|
-
const specific: Components<Parent> = {
|
|
71
|
-
title: { type: 'text' }
|
|
72
|
-
}
|
|
73
|
-
assertType<Components<any>>(specific)
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
it('Component<Specific> is assignable to Component<any>', () => {
|
|
77
|
-
const specific: Component<Parent> = { type: 'text' }
|
|
78
|
-
assertType<Component<any>>(specific)
|
|
79
|
-
assertType<Component>(specific)
|
|
80
|
-
})
|
|
81
|
-
})
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { expectTypeOf, assertType, describe, it } from 'vitest'
|
|
2
|
-
import type { DitoContext, OrItemAccessor } from '../index.d.ts'
|
|
3
|
-
import type { Entry, ParentWithMarkers } from './fixtures.ts'
|
|
4
|
-
|
|
5
|
-
describe('DitoContext', () => {
|
|
6
|
-
it('item strips never keys', () => {
|
|
7
|
-
type Ctx = DitoContext<ParentWithMarkers>
|
|
8
|
-
expectTypeOf<Ctx['item']>().not.toBeAny()
|
|
9
|
-
expectTypeOf<Ctx['item']>().toHaveProperty('id')
|
|
10
|
-
expectTypeOf<Ctx['item']>().toHaveProperty('title')
|
|
11
|
-
expectTypeOf<Ctx['item']>().toHaveProperty('entries')
|
|
12
|
-
expectTypeOf<Ctx['item']>().not.toHaveProperty('viewButton')
|
|
13
|
-
expectTypeOf<Ctx['item']>().not.toHaveProperty('spacer')
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
it('item preserves data keys', () => {
|
|
17
|
-
type Ctx = DitoContext<Entry>
|
|
18
|
-
expectTypeOf<Ctx['item']>().not.toBeAny()
|
|
19
|
-
expectTypeOf<Ctx['item']>().toEqualTypeOf<{
|
|
20
|
-
id: number
|
|
21
|
-
title: string
|
|
22
|
-
}>()
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
it('OrItemAccessor accepts value or callback', () => {
|
|
26
|
-
assertType<OrItemAccessor<Entry, {}, string>>('hello')
|
|
27
|
-
assertType<OrItemAccessor<Entry, {}, string>>(
|
|
28
|
-
(ctx: DitoContext<Entry>) => ctx.item.title
|
|
29
|
-
)
|
|
30
|
-
})
|
|
31
|
-
})
|
package/types/tests/fixtures.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export type Entry = {
|
|
2
|
-
id: number
|
|
3
|
-
title: string
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export type Parent = {
|
|
7
|
-
id: number
|
|
8
|
-
title: string
|
|
9
|
-
entries: Entry[]
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export type ParentWithMarkers = Parent & {
|
|
13
|
-
viewButton: never
|
|
14
|
-
spacer: never
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export type Address = {
|
|
18
|
-
street: string
|
|
19
|
-
city: string
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export type ParentWithAddress = Parent & {
|
|
23
|
-
address: Address
|
|
24
|
-
}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { expectTypeOf, assertType, describe, it } from 'vitest'
|
|
2
|
-
import type { Form, ResolvableForm } from '../index.d.ts'
|
|
3
|
-
import type { Entry, Parent } from './fixtures.ts'
|
|
4
|
-
|
|
5
|
-
describe('Form assignability', () => {
|
|
6
|
-
it('Form<Specific> is assignable to Form<any>', () => {
|
|
7
|
-
const specific: Form<Entry> = {
|
|
8
|
-
type: 'form',
|
|
9
|
-
components: {
|
|
10
|
-
title: { type: 'text' }
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
assertType<Form<any>>(specific)
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
it('Form<Specific> is assignable to ResolvableForm', () => {
|
|
17
|
-
const specific: Form<Entry> = {
|
|
18
|
-
type: 'form',
|
|
19
|
-
components: {
|
|
20
|
-
title: { type: 'text' }
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
assertType<ResolvableForm>(specific)
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('Record<string, Form<$Item>> is assignable to ResolvableForm<$Item>', () => {
|
|
27
|
-
const module: Record<string, Form<Entry>> = {
|
|
28
|
-
entryForm: { type: 'form', components: {} }
|
|
29
|
-
}
|
|
30
|
-
assertType<ResolvableForm<Entry>>(module)
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('Record<string, Form<WrongType>> is not assignable to ResolvableForm<$Item>', () => {
|
|
34
|
-
const module: Record<string, Form<Parent>> = {
|
|
35
|
-
parentForm: { type: 'form', components: {} }
|
|
36
|
-
}
|
|
37
|
-
// @ts-expect-error Form<Parent> should not be assignable to ResolvableForm<Entry>
|
|
38
|
-
assertType<ResolvableForm<Entry>>(module)
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('() => import() pattern is assignable to ResolvableForm', () => {
|
|
42
|
-
const importForm = () =>
|
|
43
|
-
Promise.resolve({
|
|
44
|
-
default: { type: 'form', components: {} } as Form<Entry>
|
|
45
|
-
})
|
|
46
|
-
assertType<ResolvableForm<Entry>>(importForm)
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('accepts event callbacks with typed item', () => {
|
|
50
|
-
assertType<Form<Entry>>({
|
|
51
|
-
type: 'form',
|
|
52
|
-
events: {
|
|
53
|
-
create({ item }) {
|
|
54
|
-
expectTypeOf(item).not.toBeAny()
|
|
55
|
-
expectTypeOf(item.title).toBeString()
|
|
56
|
-
},
|
|
57
|
-
submit({ item }) {
|
|
58
|
-
expectTypeOf(item).not.toBeAny()
|
|
59
|
-
expectTypeOf(item.id).toBeNumber()
|
|
60
|
-
},
|
|
61
|
-
error({ item, error }) {
|
|
62
|
-
expectTypeOf(item).not.toBeAny()
|
|
63
|
-
expectTypeOf(item.title).toBeString()
|
|
64
|
-
expectTypeOf(error).not.toBeAny()
|
|
65
|
-
expectTypeOf(error).toEqualTypeOf<Error>()
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
})
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
it('accepts onCreate/onSubmit/onError top-level callbacks', () => {
|
|
72
|
-
assertType<Form<Entry>>({
|
|
73
|
-
type: 'form',
|
|
74
|
-
onCreate({ item }) {
|
|
75
|
-
expectTypeOf(item).not.toBeAny()
|
|
76
|
-
expectTypeOf(item.title).toBeString()
|
|
77
|
-
},
|
|
78
|
-
onSubmit({ item }) {
|
|
79
|
-
expectTypeOf(item).not.toBeAny()
|
|
80
|
-
expectTypeOf(item.id).toBeNumber()
|
|
81
|
-
},
|
|
82
|
-
onError({ item, error }) {
|
|
83
|
-
expectTypeOf(item).not.toBeAny()
|
|
84
|
-
expectTypeOf(error).not.toBeAny()
|
|
85
|
-
expectTypeOf(error).toEqualTypeOf<Error>()
|
|
86
|
-
}
|
|
87
|
-
})
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('typed Form variable used as form prop in list component', () => {
|
|
91
|
-
const childForm: Form<Entry> = {
|
|
92
|
-
type: 'form',
|
|
93
|
-
components: {
|
|
94
|
-
title: { type: 'text' }
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const parentForm: Form<Parent> = {
|
|
99
|
-
type: 'form',
|
|
100
|
-
components: {
|
|
101
|
-
title: { type: 'text' },
|
|
102
|
-
entries: {
|
|
103
|
-
type: 'list',
|
|
104
|
-
form: childForm
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
})
|
|
109
|
-
})
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { expectTypeOf, describe, it } from 'vitest'
|
|
2
|
-
import type { DitoFormInstance, DitoContext } from '../index.d.ts'
|
|
3
|
-
import type { Entry } from './fixtures.ts'
|
|
4
|
-
|
|
5
|
-
describe('DitoComponentInstanceBase and DitoFormInstance', () => {
|
|
6
|
-
it('DitoFormInstance extends base with item and form properties', () => {
|
|
7
|
-
type Instance = DitoFormInstance<Entry>
|
|
8
|
-
expectTypeOf<Instance['item']>().not.toBeAny()
|
|
9
|
-
expectTypeOf<Instance['item']['title']>().toBeString()
|
|
10
|
-
expectTypeOf<Instance['isCreating']>().toBeBoolean()
|
|
11
|
-
expectTypeOf<Instance['submit']>().returns.toEqualTypeOf<Promise<boolean>>()
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
it('context is typed with item type', () => {
|
|
15
|
-
type Instance = DitoFormInstance<Entry>
|
|
16
|
-
expectTypeOf<Instance['context']>().not.toBeAny()
|
|
17
|
-
expectTypeOf<Instance['context']>()
|
|
18
|
-
.toEqualTypeOf<DitoContext<Entry>>()
|
|
19
|
-
})
|
|
20
|
-
})
|
|
@@ -1,402 +0,0 @@
|
|
|
1
|
-
import { expectTypeOf, assertType, describe, it } from 'vitest'
|
|
2
|
-
import type {
|
|
3
|
-
Form,
|
|
4
|
-
Components,
|
|
5
|
-
PanelSchema,
|
|
6
|
-
UploadSchema,
|
|
7
|
-
DitoComponentInstance,
|
|
8
|
-
HiddenSchema
|
|
9
|
-
} from '../index.d.ts'
|
|
10
|
-
import type { Entry, Parent } from './fixtures.ts'
|
|
11
|
-
|
|
12
|
-
describe('SchemaFields: methods', () => {
|
|
13
|
-
it('methods have `this` typed as component instance', () => {
|
|
14
|
-
assertType<Form<Entry>>({
|
|
15
|
-
type: 'form',
|
|
16
|
-
components: {
|
|
17
|
-
title: { type: 'text' }
|
|
18
|
-
},
|
|
19
|
-
methods: {
|
|
20
|
-
greet() {
|
|
21
|
-
expectTypeOf(this).not.toBeAny()
|
|
22
|
-
expectTypeOf(this).toEqualTypeOf<DitoComponentInstance<Entry>>()
|
|
23
|
-
expectTypeOf(this.item).not.toBeAny()
|
|
24
|
-
expectTypeOf(this.item.title).toBeString()
|
|
25
|
-
expectTypeOf(this.item.id).toBeNumber()
|
|
26
|
-
},
|
|
27
|
-
getTitle() {
|
|
28
|
-
return this.item.title
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
})
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
describe('SchemaFields: computed', () => {
|
|
36
|
-
it('computed getter uses `this` typing', () => {
|
|
37
|
-
assertType<Form<Entry>>({
|
|
38
|
-
type: 'form',
|
|
39
|
-
components: {
|
|
40
|
-
title: { type: 'text' }
|
|
41
|
-
},
|
|
42
|
-
computed: {
|
|
43
|
-
upperTitle() {
|
|
44
|
-
expectTypeOf(this).not.toBeAny()
|
|
45
|
-
expectTypeOf(this.item).not.toBeAny()
|
|
46
|
-
expectTypeOf(this.item.title).toBeString()
|
|
47
|
-
return this.item.title.toUpperCase()
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
})
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
it('computed getter/setter object uses `this` typing', () => {
|
|
54
|
-
assertType<Form<Entry>>({
|
|
55
|
-
type: 'form',
|
|
56
|
-
components: {
|
|
57
|
-
title: { type: 'text' }
|
|
58
|
-
},
|
|
59
|
-
computed: {
|
|
60
|
-
upperTitle: {
|
|
61
|
-
get() {
|
|
62
|
-
expectTypeOf(this).not.toBeAny()
|
|
63
|
-
expectTypeOf(this.item).not.toBeAny()
|
|
64
|
-
expectTypeOf(this.item.title).toBeString()
|
|
65
|
-
return this.item.title.toUpperCase()
|
|
66
|
-
},
|
|
67
|
-
set(_value) {
|
|
68
|
-
expectTypeOf(this).not.toBeAny()
|
|
69
|
-
expectTypeOf(this.item.id).toBeNumber()
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
})
|
|
74
|
-
})
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
describe('SchemaFields: watch', () => {
|
|
78
|
-
it('watch with plain handler function', () => {
|
|
79
|
-
assertType<Form<Entry>>({
|
|
80
|
-
type: 'form',
|
|
81
|
-
components: {
|
|
82
|
-
title: { type: 'text' }
|
|
83
|
-
},
|
|
84
|
-
watch: {
|
|
85
|
-
title(value, oldValue) {
|
|
86
|
-
expectTypeOf(this).not.toBeAny()
|
|
87
|
-
expectTypeOf(this.item).not.toBeAny()
|
|
88
|
-
expectTypeOf(this.item.title).toBeString()
|
|
89
|
-
expectTypeOf(value).toBeAny()
|
|
90
|
-
expectTypeOf(oldValue).toBeAny()
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
})
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
it('watch with object containing deep and immediate', () => {
|
|
97
|
-
assertType<Form<Entry>>({
|
|
98
|
-
type: 'form',
|
|
99
|
-
components: {
|
|
100
|
-
title: { type: 'text' }
|
|
101
|
-
},
|
|
102
|
-
watch: {
|
|
103
|
-
title: {
|
|
104
|
-
handler(value, oldValue) {
|
|
105
|
-
expectTypeOf(this).not.toBeAny()
|
|
106
|
-
expectTypeOf(this.item).not.toBeAny()
|
|
107
|
-
expectTypeOf(this.item.title).toBeString()
|
|
108
|
-
expectTypeOf(value).toBeAny()
|
|
109
|
-
expectTypeOf(oldValue).toBeAny()
|
|
110
|
-
},
|
|
111
|
-
deep: true,
|
|
112
|
-
immediate: true
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
})
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
it('watch as factory function returning handlers', () => {
|
|
119
|
-
assertType<Form<Entry>>({
|
|
120
|
-
type: 'form',
|
|
121
|
-
components: {
|
|
122
|
-
title: { type: 'text' }
|
|
123
|
-
},
|
|
124
|
-
watch() {
|
|
125
|
-
expectTypeOf(this).not.toBeAny()
|
|
126
|
-
expectTypeOf(this.item).not.toBeAny()
|
|
127
|
-
expectTypeOf(this.item.title).toBeString()
|
|
128
|
-
return {
|
|
129
|
-
title(value) {
|
|
130
|
-
expectTypeOf(this).not.toBeAny()
|
|
131
|
-
expectTypeOf(this.item).not.toBeAny()
|
|
132
|
-
expectTypeOf(this.item.id).toBeNumber()
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
})
|
|
137
|
-
})
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
describe('SchemaFields: panels', () => {
|
|
141
|
-
it('form with panels containing components and buttons', () => {
|
|
142
|
-
assertType<Form<Entry>>({
|
|
143
|
-
type: 'form',
|
|
144
|
-
components: {
|
|
145
|
-
title: { type: 'text' }
|
|
146
|
-
},
|
|
147
|
-
panels: {
|
|
148
|
-
sidebar: {
|
|
149
|
-
type: 'panel',
|
|
150
|
-
label: 'Sidebar',
|
|
151
|
-
components: {
|
|
152
|
-
id: { type: 'number' }
|
|
153
|
-
},
|
|
154
|
-
buttons: {
|
|
155
|
-
save: { text: 'Save' }
|
|
156
|
-
},
|
|
157
|
-
panelButtons: {
|
|
158
|
-
refresh: { text: 'Refresh' }
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
})
|
|
163
|
-
})
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
describe('SchemaFields: lifecycle callbacks', () => {
|
|
167
|
-
it('onInitialize and onChange receive typed context', () => {
|
|
168
|
-
assertType<Form<Entry>>({
|
|
169
|
-
type: 'form',
|
|
170
|
-
components: {
|
|
171
|
-
title: { type: 'text' }
|
|
172
|
-
},
|
|
173
|
-
onInitialize({ item }) {
|
|
174
|
-
expectTypeOf(item).not.toBeAny()
|
|
175
|
-
expectTypeOf(item.title).toBeString()
|
|
176
|
-
expectTypeOf(item.id).toBeNumber()
|
|
177
|
-
},
|
|
178
|
-
onChange({ item }) {
|
|
179
|
-
expectTypeOf(item).not.toBeAny()
|
|
180
|
-
expectTypeOf(item.title).toBeString()
|
|
181
|
-
}
|
|
182
|
-
})
|
|
183
|
-
})
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
describe('SchemaTypeMixin: parse and process callbacks', () => {
|
|
187
|
-
it('parse callback receives typed item', () => {
|
|
188
|
-
assertType<Components<Parent>>({
|
|
189
|
-
title: {
|
|
190
|
-
type: 'text',
|
|
191
|
-
parse({ item }) {
|
|
192
|
-
expectTypeOf(item).not.toBeAny()
|
|
193
|
-
expectTypeOf(item.title).toBeString()
|
|
194
|
-
expectTypeOf(item.entries).toEqualTypeOf<Entry[]>()
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
})
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
it('process callback receives typed item', () => {
|
|
201
|
-
assertType<Components<Parent>>({
|
|
202
|
-
title: {
|
|
203
|
-
type: 'text',
|
|
204
|
-
process({ item }) {
|
|
205
|
-
expectTypeOf(item).not.toBeAny()
|
|
206
|
-
expectTypeOf(item.title).toBeString()
|
|
207
|
-
return item.title.trim()
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
})
|
|
211
|
-
})
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
describe('SchemaDataMixin: data callback', () => {
|
|
215
|
-
it('data callback receives typed item on HiddenSchema', () => {
|
|
216
|
-
assertType<HiddenSchema<Entry>>({
|
|
217
|
-
type: 'hidden',
|
|
218
|
-
data({ item }) {
|
|
219
|
-
expectTypeOf(item).not.toBeAny()
|
|
220
|
-
expectTypeOf(item.title).toBeString()
|
|
221
|
-
expectTypeOf(item.id).toBeNumber()
|
|
222
|
-
return { computed: item.title }
|
|
223
|
-
}
|
|
224
|
-
})
|
|
225
|
-
})
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
describe('UploadSchema', () => {
|
|
229
|
-
it('accepts multiple, extensions, accept, and maxSize', () => {
|
|
230
|
-
assertType<Components<Parent>>({
|
|
231
|
-
entries: {
|
|
232
|
-
type: 'upload',
|
|
233
|
-
multiple: true,
|
|
234
|
-
extensions: ['jpg', 'png', 'gif'],
|
|
235
|
-
accept: ['image/png', 'image/jpeg'],
|
|
236
|
-
maxSize: '5mb'
|
|
237
|
-
}
|
|
238
|
-
})
|
|
239
|
-
})
|
|
240
|
-
|
|
241
|
-
it('accepts extensions as regex', () => {
|
|
242
|
-
assertType<Components<Parent>>({
|
|
243
|
-
entries: {
|
|
244
|
-
type: 'upload',
|
|
245
|
-
extensions: /\.(gif|jpe?g|png)$/i
|
|
246
|
-
}
|
|
247
|
-
})
|
|
248
|
-
})
|
|
249
|
-
|
|
250
|
-
it('accepts thumbnails and downloadUrl as callbacks', () => {
|
|
251
|
-
assertType<UploadSchema<Entry>>({
|
|
252
|
-
type: 'upload',
|
|
253
|
-
thumbnails({ item }) {
|
|
254
|
-
expectTypeOf(item).not.toBeAny()
|
|
255
|
-
expectTypeOf(item.title).toBeString()
|
|
256
|
-
return true
|
|
257
|
-
},
|
|
258
|
-
downloadUrl({ item }) {
|
|
259
|
-
expectTypeOf(item).not.toBeAny()
|
|
260
|
-
expectTypeOf(item.id).toBeNumber()
|
|
261
|
-
return `/files/${item.id}`
|
|
262
|
-
}
|
|
263
|
-
})
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
it('accepts thumbnailUrl and render as callbacks', () => {
|
|
267
|
-
assertType<UploadSchema<Entry>>({
|
|
268
|
-
type: 'upload',
|
|
269
|
-
thumbnailUrl({ item }) {
|
|
270
|
-
expectTypeOf(item).not.toBeAny()
|
|
271
|
-
expectTypeOf(item.title).toBeString()
|
|
272
|
-
return `/thumbs/${item.id}`
|
|
273
|
-
},
|
|
274
|
-
render({ item }) {
|
|
275
|
-
expectTypeOf(item).not.toBeAny()
|
|
276
|
-
return item.title
|
|
277
|
-
}
|
|
278
|
-
})
|
|
279
|
-
})
|
|
280
|
-
|
|
281
|
-
it('accepts draggable and deletable as callbacks', () => {
|
|
282
|
-
assertType<UploadSchema<Entry>>({
|
|
283
|
-
type: 'upload',
|
|
284
|
-
draggable({ item }) {
|
|
285
|
-
expectTypeOf(item).not.toBeAny()
|
|
286
|
-
expectTypeOf(item.id).toBeNumber()
|
|
287
|
-
return true
|
|
288
|
-
},
|
|
289
|
-
deletable({ item }) {
|
|
290
|
-
expectTypeOf(item).not.toBeAny()
|
|
291
|
-
expectTypeOf(item.title).toBeString()
|
|
292
|
-
return false
|
|
293
|
-
}
|
|
294
|
-
})
|
|
295
|
-
})
|
|
296
|
-
})
|
|
297
|
-
|
|
298
|
-
describe('PanelSchema', () => {
|
|
299
|
-
it('constrains component keys to item properties', () => {
|
|
300
|
-
assertType<PanelSchema<Entry>>({
|
|
301
|
-
type: 'panel',
|
|
302
|
-
components: {
|
|
303
|
-
id: { type: 'number' },
|
|
304
|
-
title: { type: 'text' }
|
|
305
|
-
}
|
|
306
|
-
})
|
|
307
|
-
})
|
|
308
|
-
|
|
309
|
-
it('rejects unknown component keys', () => {
|
|
310
|
-
assertType<PanelSchema<Entry>>({
|
|
311
|
-
type: 'panel',
|
|
312
|
-
components: {
|
|
313
|
-
// @ts-expect-error — 'extra' is not a key of Entry
|
|
314
|
-
extra: { type: 'text' }
|
|
315
|
-
}
|
|
316
|
-
})
|
|
317
|
-
})
|
|
318
|
-
|
|
319
|
-
it('accepts buttons and panelButtons', () => {
|
|
320
|
-
assertType<PanelSchema<Entry>>({
|
|
321
|
-
type: 'panel',
|
|
322
|
-
buttons: {
|
|
323
|
-
save: {
|
|
324
|
-
text: 'Save',
|
|
325
|
-
events: {
|
|
326
|
-
click({ item }) {
|
|
327
|
-
expectTypeOf(item).not.toBeAny()
|
|
328
|
-
expectTypeOf(item.title).toBeString()
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
},
|
|
333
|
-
panelButtons: {
|
|
334
|
-
refresh: { text: 'Refresh' }
|
|
335
|
-
}
|
|
336
|
-
})
|
|
337
|
-
})
|
|
338
|
-
|
|
339
|
-
it('accepts sticky as callback', () => {
|
|
340
|
-
assertType<PanelSchema<Entry>>({
|
|
341
|
-
type: 'panel',
|
|
342
|
-
sticky({ item }) {
|
|
343
|
-
expectTypeOf(item).not.toBeAny()
|
|
344
|
-
expectTypeOf(item.id).toBeNumber()
|
|
345
|
-
return true
|
|
346
|
-
}
|
|
347
|
-
})
|
|
348
|
-
})
|
|
349
|
-
|
|
350
|
-
it('component callbacks in panels receive typed item', () => {
|
|
351
|
-
assertType<PanelSchema<Parent>>({
|
|
352
|
-
type: 'panel',
|
|
353
|
-
components: {
|
|
354
|
-
title: {
|
|
355
|
-
type: 'text',
|
|
356
|
-
format({ item }) {
|
|
357
|
-
expectTypeOf(item).not.toBeAny()
|
|
358
|
-
expectTypeOf(item.title).toBeString()
|
|
359
|
-
expectTypeOf(item.entries)
|
|
360
|
-
.toEqualTypeOf<Entry[]>()
|
|
361
|
-
return ''
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
})
|
|
366
|
-
})
|
|
367
|
-
|
|
368
|
-
it('panels on Form inherit item type', () => {
|
|
369
|
-
assertType<Form<Parent>>({
|
|
370
|
-
type: 'form',
|
|
371
|
-
components: {
|
|
372
|
-
title: { type: 'text' }
|
|
373
|
-
},
|
|
374
|
-
panels: {
|
|
375
|
-
info: {
|
|
376
|
-
type: 'panel',
|
|
377
|
-
components: {
|
|
378
|
-
title: {
|
|
379
|
-
type: 'text',
|
|
380
|
-
format({ item }) {
|
|
381
|
-
expectTypeOf(item).not.toBeAny()
|
|
382
|
-
expectTypeOf(item.title).toBeString()
|
|
383
|
-
return ''
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
},
|
|
387
|
-
buttons: {
|
|
388
|
-
save: {
|
|
389
|
-
text: 'Save',
|
|
390
|
-
events: {
|
|
391
|
-
click({ item }) {
|
|
392
|
-
expectTypeOf(item).not.toBeAny()
|
|
393
|
-
expectTypeOf(item.id).toBeNumber()
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
})
|
|
401
|
-
})
|
|
402
|
-
})
|