@ditojs/admin 2.92.0 → 2.94.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 +1668 -1518
- package/dist/dito-admin.umd.js +6 -6
- package/package.json +14 -15
- 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 +10 -0
- package/src/mixins/DitoMixin.js +4 -2
- package/src/mixins/PulldownMixin.js +9 -0
- package/src/mixins/SortableMixin.js +3 -1
- 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/src/utils/ui/dist/dito-ui.es.js +2729 -0
- package/src/utils/ui/dist/dito-ui.umd.js +2744 -0
- package/src/utils/ui/dist/style.css +1139 -0
- 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
package/types/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
TimeFormat
|
|
9
9
|
} from '@ditojs/utils'
|
|
10
10
|
import { RequireAtLeastOne, SetOptional } from 'type-fest'
|
|
11
|
-
import { Component as VueComponent } from 'vue'
|
|
11
|
+
import { Component as VueComponent, ComponentPublicInstance } from 'vue'
|
|
12
12
|
import { Router as VueRouter } from 'vue-router'
|
|
13
13
|
|
|
14
14
|
declare global {
|
|
@@ -695,7 +695,19 @@ export interface SchemaSourceMixin<$Item> {
|
|
|
695
695
|
*
|
|
696
696
|
* @defaultValue `false`
|
|
697
697
|
*/
|
|
698
|
-
creatable?: OrItemAccessor
|
|
698
|
+
creatable?: OrItemAccessor<
|
|
699
|
+
$Item,
|
|
700
|
+
{},
|
|
701
|
+
| boolean
|
|
702
|
+
| {
|
|
703
|
+
label?: string
|
|
704
|
+
/**
|
|
705
|
+
* Returns additional query parameters to include in the
|
|
706
|
+
* URL when navigating to the create form.
|
|
707
|
+
*/
|
|
708
|
+
query?: (context: DitoContext) => Record<string, any>
|
|
709
|
+
}
|
|
710
|
+
>
|
|
699
711
|
/**
|
|
700
712
|
* Whether to add edit buttons next to the list items.
|
|
701
713
|
*
|
|
@@ -788,11 +800,14 @@ export type SchemaOptions<$Item, $Option = any> =
|
|
|
788
800
|
*/
|
|
789
801
|
groupBy?: keyof $Option
|
|
790
802
|
/**
|
|
791
|
-
* Custom equality function for
|
|
803
|
+
* Custom equality function for matching a value to an
|
|
804
|
+
* option. Receives a context with `value` and `option`.
|
|
792
805
|
*/
|
|
793
806
|
equals?: (
|
|
794
|
-
|
|
795
|
-
|
|
807
|
+
context: DitoContext & {
|
|
808
|
+
value: any
|
|
809
|
+
option: $Option
|
|
810
|
+
}
|
|
796
811
|
) => boolean
|
|
797
812
|
/**
|
|
798
813
|
* Relative path to resolve options from data
|
|
@@ -1640,6 +1655,12 @@ export interface CheckboxesSchema<$Item = any>
|
|
|
1640
1655
|
* The type of the component.
|
|
1641
1656
|
*/
|
|
1642
1657
|
type: 'checkboxes'
|
|
1658
|
+
/**
|
|
1659
|
+
* Whether multiple values can be selected.
|
|
1660
|
+
*
|
|
1661
|
+
* @defaultValue `true`
|
|
1662
|
+
*/
|
|
1663
|
+
multiple?: boolean
|
|
1643
1664
|
/**
|
|
1644
1665
|
* @defaultValue `'vertical'`
|
|
1645
1666
|
*/
|
|
@@ -1918,7 +1939,7 @@ export type DitoContext<$Item = any> = {
|
|
|
1918
1939
|
/**
|
|
1919
1940
|
* The current search term in select components.
|
|
1920
1941
|
*/
|
|
1921
|
-
searchTerm: string
|
|
1942
|
+
searchTerm: string
|
|
1922
1943
|
/**
|
|
1923
1944
|
* Whether a button request is currently running.
|
|
1924
1945
|
*/
|
|
@@ -1976,9 +1997,22 @@ export type DitoContext<$Item = any> = {
|
|
|
1976
1997
|
getResourceUrl(resource: Resource): string
|
|
1977
1998
|
/** Displays a notification to the user. */
|
|
1978
1999
|
notify(options: {
|
|
2000
|
+
/** Notification style. @defaultValue `'info'` */
|
|
1979
2001
|
type?: LiteralUnion<'warning' | 'error' | 'info' | 'success'>
|
|
2002
|
+
/** Heading text. Defaults to a label matching `type`. */
|
|
1980
2003
|
title?: string
|
|
2004
|
+
/** Body text, rendered as HTML paragraphs. */
|
|
1981
2005
|
text: OrArrayOf<string>
|
|
2006
|
+
/**
|
|
2007
|
+
* The original error object. When `type` is `'error'`,
|
|
2008
|
+
* this is logged to the console alongside the message.
|
|
2009
|
+
*/
|
|
2010
|
+
error?: unknown
|
|
2011
|
+
/**
|
|
2012
|
+
* Display duration in milliseconds. When omitted, calculated
|
|
2013
|
+
* automatically from content length. Use `0` for sticky.
|
|
2014
|
+
*/
|
|
2015
|
+
duration?: number
|
|
1982
2016
|
}): void
|
|
1983
2017
|
}
|
|
1984
2018
|
|
|
@@ -1997,7 +2031,9 @@ export type DitoComponentInstance<
|
|
|
1997
2031
|
$Members extends Record<string, any> = {}
|
|
1998
2032
|
> = DitoComponentInstanceBase<$Item> & $Members
|
|
1999
2033
|
|
|
2000
|
-
export interface DitoComponentInstanceBase<$Item = any>
|
|
2034
|
+
export interface DitoComponentInstanceBase<$Item = any>
|
|
2035
|
+
extends ComponentPublicInstance,
|
|
2036
|
+
EmitterMixin {
|
|
2001
2037
|
// -- Data access (ValueMixin, ContextMixin, TypeMixin) --
|
|
2002
2038
|
|
|
2003
2039
|
/** The current value of the component (getter/setter). */
|
|
@@ -2968,6 +3004,31 @@ export type SourceComponent<$Item = any> =
|
|
|
2968
3004
|
| TreeListSchema<$Item>
|
|
2969
3005
|
| TreeObjectSchema<$Item>
|
|
2970
3006
|
|
|
3007
|
+
/**
|
|
3008
|
+
* Field components that don't have an options mixin (excludes
|
|
3009
|
+
* select, multiselect, and source components like list/object/tree).
|
|
3010
|
+
*/
|
|
3011
|
+
type NonOptionFieldComponent<$Item = any> = Exclude<
|
|
3012
|
+
NonSectionComponent<$Item>,
|
|
3013
|
+
SelectSchema<$Item> | MultiselectSchema<$Item> | SourceComponent<$Item>
|
|
3014
|
+
>
|
|
3015
|
+
|
|
3016
|
+
/**
|
|
3017
|
+
* Select/multiselect with a typed `$Option` for option callbacks.
|
|
3018
|
+
*
|
|
3019
|
+
* Note: This is intentionally an intersection type rather than
|
|
3020
|
+
* `SelectSchema<$Item, $Option> | MultiselectSchema<$Item, $Option>`,
|
|
3021
|
+
* because using the full interfaces in a union breaks TypeScript's
|
|
3022
|
+
* contextual typing for callbacks in the `Components` mapped type.
|
|
3023
|
+
*/
|
|
3024
|
+
type OptionComponent<$Item, $Option> = BaseSchema<$Item> &
|
|
3025
|
+
SchemaOptionsMixin<$Item, $Option> &
|
|
3026
|
+
SchemaAffixMixin<$Item> &
|
|
3027
|
+
Pick<
|
|
3028
|
+
MultiselectSchema,
|
|
3029
|
+
'multiple' | 'searchable' | 'stayOpen' | 'taggable'
|
|
3030
|
+
> & { type: 'select' | 'multiselect' }
|
|
3031
|
+
|
|
2971
3032
|
/**
|
|
2972
3033
|
* Strips properties with `never` values from a type.
|
|
2973
3034
|
*/
|
|
@@ -3047,14 +3108,16 @@ export type Components<$Item = any> = 0 extends 1 & $Item
|
|
|
3047
3108
|
: {
|
|
3048
3109
|
[K in keyof $Item]?: [$Item[K]] extends [never]
|
|
3049
3110
|
? NonSectionComponent<$Item> | SectionSchema<$Item>
|
|
3050
|
-
:
|
|
3111
|
+
: NonNullable<$Item[K]> extends (infer E)[]
|
|
3051
3112
|
? E extends Record<string, any>
|
|
3052
3113
|
? NonSectionComponent<E>
|
|
3053
3114
|
: NonSectionComponent<$Item>
|
|
3054
|
-
:
|
|
3115
|
+
: NonNullable<$Item[K]> extends Record<string, any>
|
|
3055
3116
|
?
|
|
3056
|
-
|
|
|
3057
|
-
|
|
|
3117
|
+
| NonOptionFieldComponent<$Item>
|
|
3118
|
+
| OptionComponent<$Item, NonNullable<$Item[K]>>
|
|
3119
|
+
| SourceComponent<NonNullable<$Item[K]>>
|
|
3120
|
+
| SectionSchema<$Item, NonNullable<$Item[K]>>
|
|
3058
3121
|
:
|
|
3059
3122
|
| NonSectionComponent<$Item>
|
|
3060
3123
|
| SectionSchema<$Item>
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { assertType, describe, it } from 'vitest'
|
|
2
|
-
import type { default as DitoAdmin, View } from '../index.d.ts'
|
|
3
|
-
import type { Entry } from './fixtures.ts'
|
|
4
|
-
|
|
5
|
-
describe('DitoAdmin', () => {
|
|
6
|
-
it('constructor accepts element and views option', () => {
|
|
7
|
-
assertType<ConstructorParameters<typeof DitoAdmin>>([
|
|
8
|
-
document.body,
|
|
9
|
-
{
|
|
10
|
-
views: {
|
|
11
|
-
entries: {
|
|
12
|
-
type: 'view',
|
|
13
|
-
component: {
|
|
14
|
-
type: 'list',
|
|
15
|
-
form: {
|
|
16
|
-
type: 'form',
|
|
17
|
-
components: {
|
|
18
|
-
title: { type: 'text' }
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
} satisfies View<Entry>
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
])
|
|
26
|
-
})
|
|
27
|
-
})
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { expectTypeOf, assertType, describe, it } from 'vitest'
|
|
2
|
-
import type { Buttons } from '../index.d.ts'
|
|
3
|
-
import type { Entry } from './fixtures.ts'
|
|
4
|
-
|
|
5
|
-
describe('Buttons', () => {
|
|
6
|
-
it('accepts button without type', () => {
|
|
7
|
-
assertType<Buttons<Entry>>({
|
|
8
|
-
save: { text: 'Save' }
|
|
9
|
-
})
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
it('accepts button with type', () => {
|
|
13
|
-
assertType<Buttons<Entry>>({
|
|
14
|
-
save: { type: 'button', text: 'Save' }
|
|
15
|
-
})
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
it('click callback receives typed item', () => {
|
|
19
|
-
assertType<Buttons<Entry>>({
|
|
20
|
-
save: {
|
|
21
|
-
text: 'Save',
|
|
22
|
-
events: {
|
|
23
|
-
click({ item }) {
|
|
24
|
-
expectTypeOf(item).not.toBeAny()
|
|
25
|
-
expectTypeOf(item).toHaveProperty('id')
|
|
26
|
-
expectTypeOf(item).toHaveProperty('title')
|
|
27
|
-
expectTypeOf(item.id).toBeNumber()
|
|
28
|
-
expectTypeOf(item.title).toBeString()
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
})
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
it('rejects invalid button type', () => {
|
|
36
|
-
assertType<Buttons<Entry>>({
|
|
37
|
-
save: {
|
|
38
|
-
// @ts-expect-error 'text' is not a valid button type
|
|
39
|
-
type: 'text',
|
|
40
|
-
text: 'Save'
|
|
41
|
-
}
|
|
42
|
-
})
|
|
43
|
-
})
|
|
44
|
-
})
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import { expectTypeOf, assertType, describe, it } from 'vitest'
|
|
2
|
-
import type {
|
|
3
|
-
Components,
|
|
4
|
-
Form,
|
|
5
|
-
ListSchema,
|
|
6
|
-
ColumnSchema,
|
|
7
|
-
DitoContext
|
|
8
|
-
} from '../index.d.ts'
|
|
9
|
-
import type { Entry, Parent } from './fixtures.ts'
|
|
10
|
-
|
|
11
|
-
describe('ListSchema', () => {
|
|
12
|
-
it('accepts form components typed against $Item', () => {
|
|
13
|
-
assertType<ListSchema<Entry>>({
|
|
14
|
-
type: 'list',
|
|
15
|
-
form: {
|
|
16
|
-
type: 'form',
|
|
17
|
-
components: {
|
|
18
|
-
title: { type: 'text' }
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
})
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
it('accepts columns as a record of ColumnSchema', () => {
|
|
25
|
-
assertType<ListSchema<Entry>>({
|
|
26
|
-
type: 'list',
|
|
27
|
-
columns: {
|
|
28
|
-
title: {
|
|
29
|
-
label: 'Title',
|
|
30
|
-
sortable: true
|
|
31
|
-
} satisfies ColumnSchema<Entry>
|
|
32
|
-
}
|
|
33
|
-
})
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('column render callback receives typed value', () => {
|
|
37
|
-
assertType<ListSchema<Entry>>({
|
|
38
|
-
type: 'list',
|
|
39
|
-
columns: {
|
|
40
|
-
title: {
|
|
41
|
-
render({ value }) {
|
|
42
|
-
expectTypeOf(value).not.toBeAny()
|
|
43
|
-
expectTypeOf(value).toBeString()
|
|
44
|
-
return value
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
})
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
it('accepts columns as an array of item keys', () => {
|
|
52
|
-
assertType<ListSchema<Entry>>({
|
|
53
|
-
type: 'list',
|
|
54
|
-
columns: ['title', 'id']
|
|
55
|
-
})
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
it('rejects unknown keys in columns array', () => {
|
|
59
|
-
assertType<ListSchema<Entry>>({
|
|
60
|
-
type: 'list',
|
|
61
|
-
// @ts-expect-error 'missing' is not a key of Entry
|
|
62
|
-
columns: ['title', 'missing']
|
|
63
|
-
})
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
it('accepts creatable as boolean or callback', () => {
|
|
67
|
-
assertType<ListSchema<Entry>>({
|
|
68
|
-
type: 'list',
|
|
69
|
-
creatable: true
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
assertType<ListSchema<Entry>>({
|
|
73
|
-
type: 'list',
|
|
74
|
-
creatable(ctx) {
|
|
75
|
-
expectTypeOf(ctx).not.toBeAny()
|
|
76
|
-
expectTypeOf(ctx).toMatchTypeOf<DitoContext<Entry>>()
|
|
77
|
-
expectTypeOf(ctx.item.title).toBeString()
|
|
78
|
-
return true
|
|
79
|
-
}
|
|
80
|
-
})
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
it('types list correctly inside Components', () => {
|
|
84
|
-
assertType<Components<Parent>>({
|
|
85
|
-
entries: {
|
|
86
|
-
type: 'list',
|
|
87
|
-
form: {
|
|
88
|
-
type: 'form',
|
|
89
|
-
components: {
|
|
90
|
-
title: { type: 'text' }
|
|
91
|
-
}
|
|
92
|
-
} satisfies Form<Entry>
|
|
93
|
-
}
|
|
94
|
-
})
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
it('rejects wrong form type via typed variable', () => {
|
|
98
|
-
const parentForm: Form<Parent> = {
|
|
99
|
-
type: 'form',
|
|
100
|
-
components: {
|
|
101
|
-
title: { type: 'text' },
|
|
102
|
-
entries: { type: 'list' }
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
assertType<ListSchema<Entry>>({
|
|
107
|
-
type: 'list',
|
|
108
|
-
// @ts-expect-error Form<Parent> is not assignable to ResolvableForm<Entry>
|
|
109
|
-
form: parentForm
|
|
110
|
-
})
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
it('accepts draggable as OrItemAccessor', () => {
|
|
114
|
-
assertType<ListSchema<Entry>>({
|
|
115
|
-
type: 'list',
|
|
116
|
-
draggable(ctx) {
|
|
117
|
-
expectTypeOf(ctx).not.toBeAny()
|
|
118
|
-
expectTypeOf(ctx).toMatchTypeOf<DitoContext<Entry>>()
|
|
119
|
-
return true
|
|
120
|
-
}
|
|
121
|
-
})
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
it('accepts scopes as string array or object', () => {
|
|
125
|
-
assertType<ListSchema<Entry>>({
|
|
126
|
-
type: 'list',
|
|
127
|
-
scopes: ['active', 'archived']
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
assertType<ListSchema<Entry>>({
|
|
131
|
-
type: 'list',
|
|
132
|
-
scopes: {
|
|
133
|
-
active: { label: 'Active', defaultScope: true },
|
|
134
|
-
archived: 'Archived'
|
|
135
|
-
}
|
|
136
|
-
})
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
it('accepts itemLabel as string, callback, or false', () => {
|
|
140
|
-
assertType<ListSchema<Entry>>({
|
|
141
|
-
type: 'list',
|
|
142
|
-
itemLabel: 'title'
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
assertType<ListSchema<Entry>>({
|
|
146
|
-
type: 'list',
|
|
147
|
-
itemLabel: false
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
assertType<ListSchema<Entry>>({
|
|
151
|
-
type: 'list',
|
|
152
|
-
itemLabel({ item }) {
|
|
153
|
-
expectTypeOf(item).not.toBeAny()
|
|
154
|
-
expectTypeOf(item.title).toBeString()
|
|
155
|
-
return item.title
|
|
156
|
-
}
|
|
157
|
-
})
|
|
158
|
-
})
|
|
159
|
-
})
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { expectTypeOf, assertType, describe, it } from 'vitest'
|
|
2
|
-
import type { MenuSchema, View, NumberSchema, Form } from '../index.d.ts'
|
|
3
|
-
import type { Entry } from './fixtures.ts'
|
|
4
|
-
|
|
5
|
-
describe('MenuSchema', () => {
|
|
6
|
-
it('accepts menu with items containing views', () => {
|
|
7
|
-
assertType<MenuSchema<Entry>>({
|
|
8
|
-
type: 'menu',
|
|
9
|
-
label: 'Main Menu',
|
|
10
|
-
name: 'mainMenu',
|
|
11
|
-
items: {
|
|
12
|
-
entries: {
|
|
13
|
-
type: 'view',
|
|
14
|
-
component: {
|
|
15
|
-
type: 'list',
|
|
16
|
-
form: {
|
|
17
|
-
type: 'form',
|
|
18
|
-
components: {
|
|
19
|
-
title: { type: 'text' }
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
})
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
it('View union accepts both ViewSchema and MenuSchema', () => {
|
|
29
|
-
const viewSchema: View<Entry> = {
|
|
30
|
-
type: 'view',
|
|
31
|
-
components: {
|
|
32
|
-
title: { type: 'text' }
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const menuSchema: View<Entry> = {
|
|
37
|
-
type: 'menu',
|
|
38
|
-
label: 'Group',
|
|
39
|
-
items: {
|
|
40
|
-
sub: {
|
|
41
|
-
type: 'view',
|
|
42
|
-
components: {
|
|
43
|
-
title: { type: 'text' }
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
assertType<View<Entry>>(viewSchema)
|
|
50
|
-
assertType<View<Entry>>(menuSchema)
|
|
51
|
-
})
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
describe('NumberSchema', () => {
|
|
55
|
-
it('accepts min, max, step, decimals', () => {
|
|
56
|
-
assertType<NumberSchema<Entry>>({
|
|
57
|
-
type: 'number',
|
|
58
|
-
min: 0,
|
|
59
|
-
max: 100,
|
|
60
|
-
step: 0.5,
|
|
61
|
-
decimals: 2
|
|
62
|
-
})
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
it('accepts range as tuple', () => {
|
|
66
|
-
assertType<NumberSchema<Entry>>({
|
|
67
|
-
type: 'integer',
|
|
68
|
-
range: [0, 100]
|
|
69
|
-
})
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
it('accepts rules including integer', () => {
|
|
73
|
-
assertType<NumberSchema<Entry>>({
|
|
74
|
-
type: 'number',
|
|
75
|
-
rules: {
|
|
76
|
-
min: 0,
|
|
77
|
-
max: 999,
|
|
78
|
-
integer: true
|
|
79
|
-
}
|
|
80
|
-
})
|
|
81
|
-
})
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
describe('SchemaAffixMixin (prefix/suffix)', () => {
|
|
85
|
-
it('accepts prefix as string', () => {
|
|
86
|
-
assertType<NumberSchema<Entry>>({
|
|
87
|
-
type: 'number',
|
|
88
|
-
prefix: '$'
|
|
89
|
-
})
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
it('accepts prefix as array of SchemaAffix objects', () => {
|
|
93
|
-
assertType<NumberSchema<Entry>>({
|
|
94
|
-
type: 'number',
|
|
95
|
-
prefix: [
|
|
96
|
-
{ type: 'icon', text: 'currency' },
|
|
97
|
-
{ html: '<b>$</b>' }
|
|
98
|
-
]
|
|
99
|
-
})
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
it('accepts suffix with conditional if callback', () => {
|
|
103
|
-
assertType<NumberSchema<Entry>>({
|
|
104
|
-
type: 'number',
|
|
105
|
-
suffix: {
|
|
106
|
-
text: '%',
|
|
107
|
-
if({ item }) {
|
|
108
|
-
expectTypeOf(item).not.toBeAny()
|
|
109
|
-
return item.id > 0
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
})
|
|
113
|
-
})
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
describe('ClipboardConfig', () => {
|
|
117
|
-
it('accepts copy/paste callbacks on a form', () => {
|
|
118
|
-
assertType<Form<Entry>>({
|
|
119
|
-
type: 'form',
|
|
120
|
-
clipboard: {
|
|
121
|
-
copy(context) {
|
|
122
|
-
expectTypeOf(context).not.toBeAny()
|
|
123
|
-
expectTypeOf(context.item).not.toBeAny()
|
|
124
|
-
return context.item
|
|
125
|
-
},
|
|
126
|
-
paste(context) {
|
|
127
|
-
expectTypeOf(context).not.toBeAny()
|
|
128
|
-
expectTypeOf(context.item).not.toBeAny()
|
|
129
|
-
return context.item
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
components: {
|
|
133
|
-
title: { type: 'text' }
|
|
134
|
-
}
|
|
135
|
-
})
|
|
136
|
-
})
|
|
137
|
-
})
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { expectTypeOf, assertType, describe, it } from 'vitest'
|
|
2
|
-
import type {
|
|
3
|
-
ObjectSchema,
|
|
4
|
-
TreeListSchema,
|
|
5
|
-
Form,
|
|
6
|
-
Components
|
|
7
|
-
} from '../index.d.ts'
|
|
8
|
-
import type { Address, Entry, ParentWithAddress } from './fixtures.ts'
|
|
9
|
-
|
|
10
|
-
describe('ObjectSchema', () => {
|
|
11
|
-
it('accepts typed form', () => {
|
|
12
|
-
assertType<ObjectSchema<Address>>({
|
|
13
|
-
type: 'object',
|
|
14
|
-
form: {
|
|
15
|
-
type: 'form',
|
|
16
|
-
components: {
|
|
17
|
-
street: { type: 'text' },
|
|
18
|
-
city: { type: 'text' }
|
|
19
|
-
}
|
|
20
|
-
} satisfies Form<Address>
|
|
21
|
-
})
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
it('accepts typed columns', () => {
|
|
25
|
-
assertType<ObjectSchema<Address>>({
|
|
26
|
-
type: 'object',
|
|
27
|
-
columns: {
|
|
28
|
-
street: { label: 'Street' },
|
|
29
|
-
city: { label: 'City' }
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it('rejects unknown key in columns array', () => {
|
|
35
|
-
assertType<ObjectSchema<Address>>({
|
|
36
|
-
type: 'object',
|
|
37
|
-
// @ts-expect-error 'zipCode' is not a key of Address
|
|
38
|
-
columns: ['street', 'zipCode']
|
|
39
|
-
})
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
it('works inside Components for a data key', () => {
|
|
43
|
-
assertType<Components<ParentWithAddress>>({
|
|
44
|
-
address: {
|
|
45
|
-
type: 'object',
|
|
46
|
-
form: {
|
|
47
|
-
type: 'form',
|
|
48
|
-
components: {
|
|
49
|
-
title: { type: 'text' }
|
|
50
|
-
}
|
|
51
|
-
} satisfies Form<ParentWithAddress>
|
|
52
|
-
}
|
|
53
|
-
})
|
|
54
|
-
})
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
describe('TreeListSchema', () => {
|
|
58
|
-
it('accepts typed form', () => {
|
|
59
|
-
assertType<TreeListSchema<Entry>>({
|
|
60
|
-
type: 'tree-list',
|
|
61
|
-
form: {
|
|
62
|
-
type: 'form',
|
|
63
|
-
components: {
|
|
64
|
-
title: { type: 'text' }
|
|
65
|
-
}
|
|
66
|
-
} satisfies Form<Entry>
|
|
67
|
-
})
|
|
68
|
-
})
|
|
69
|
-
})
|