@morscherlab/mld-sdk 0.6.5 → 0.7.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/__tests__/composables/formBuilderRegistry.test.d.ts +1 -0
- package/dist/__tests__/composables/useFormBuilder.test.d.ts +1 -0
- package/dist/components/BaseButton.vue.d.ts +1 -1
- package/dist/components/BasePill.vue.d.ts +1 -1
- package/dist/components/DropdownButton.vue.d.ts +1 -1
- package/dist/components/FormActions.vue.d.ts +33 -0
- package/dist/components/FormActions.vue.js +76 -0
- package/dist/components/FormActions.vue.js.map +1 -0
- package/dist/components/FormActions.vue3.js +6 -0
- package/dist/components/FormActions.vue3.js.map +1 -0
- package/dist/components/FormBuilder.vue.js +205 -0
- package/dist/components/FormBuilder.vue.js.map +1 -0
- package/dist/components/FormBuilder.vue3.js +6 -0
- package/dist/components/FormBuilder.vue3.js.map +1 -0
- package/dist/components/FormFieldRenderer.vue.d.ts +31 -0
- package/dist/components/FormFieldRenderer.vue.js +48 -0
- package/dist/components/FormFieldRenderer.vue.js.map +1 -0
- package/dist/components/FormFieldRenderer.vue2.js +5 -0
- package/dist/components/FormFieldRenderer.vue2.js.map +1 -0
- package/dist/components/FormSection.vue.d.ts +43 -0
- package/dist/components/FormSection.vue.js +117 -0
- package/dist/components/FormSection.vue.js.map +1 -0
- package/dist/components/FormSection.vue3.js +6 -0
- package/dist/components/FormSection.vue3.js.map +1 -0
- package/dist/components/IconButton.vue.d.ts +1 -1
- package/dist/components/LoadingSpinner.vue.d.ts +1 -1
- package/dist/components/ProgressBar.vue.d.ts +1 -1
- package/dist/components/ReagentList.vue.d.ts +2 -2
- package/dist/components/ResourceCard.vue.d.ts +1 -1
- package/dist/components/SegmentedControl.vue.d.ts +1 -1
- package/dist/components/WellEditPopup.vue.d.ts +2 -2
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.js +19 -8
- package/dist/components/index.js.map +1 -1
- package/dist/composables/formBuilderRegistry.d.ts +13 -0
- package/dist/composables/formBuilderRegistry.js +87 -0
- package/dist/composables/formBuilderRegistry.js.map +1 -0
- package/dist/composables/index.d.ts +2 -0
- package/dist/composables/index.js +6 -0
- package/dist/composables/index.js.map +1 -1
- package/dist/composables/useFormBuilder.d.ts +23 -0
- package/dist/composables/useFormBuilder.js +264 -0
- package/dist/composables/useFormBuilder.js.map +1 -0
- package/dist/styles.css +239 -4
- package/dist/types/form-builder.d.ts +167 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/__tests__/composables/formBuilderRegistry.test.ts +187 -0
- package/src/__tests__/composables/useFormBuilder.test.ts +917 -0
- package/src/components/FormActions.vue +92 -0
- package/src/components/FormBuilder.vue +214 -0
- package/src/components/FormFieldRenderer.vue +58 -0
- package/src/components/FormSection.vue +90 -0
- package/src/components/index.ts +6 -0
- package/src/composables/formBuilderRegistry.ts +79 -0
- package/src/composables/index.ts +6 -0
- package/src/composables/useFormBuilder.ts +382 -0
- package/src/styles/components/app-container.css +1 -0
- package/src/styles/components/app-layout.css +1 -2
- package/src/styles/components/form-builder.css +69 -0
- package/src/styles/index.css +1 -0
- package/src/types/form-builder.ts +197 -0
- package/src/types/index.ts +14 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { FieldRules } from '../composables/useForm';
|
|
2
|
+
/** All supported field types that map to SDK components. */
|
|
3
|
+
export type FormFieldType = 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'search' | 'textarea' | 'select' | 'multiselect' | 'checkbox' | 'toggle' | 'radio' | 'slider' | 'tags' | 'date' | 'time' | 'datetime' | 'file' | 'formula' | 'sequence' | 'molecule' | 'concentration' | 'unit';
|
|
4
|
+
export type FieldCondition = {
|
|
5
|
+
field: string;
|
|
6
|
+
eq: unknown;
|
|
7
|
+
} | {
|
|
8
|
+
field: string;
|
|
9
|
+
neq: unknown;
|
|
10
|
+
} | {
|
|
11
|
+
field: string;
|
|
12
|
+
gt: number;
|
|
13
|
+
} | {
|
|
14
|
+
field: string;
|
|
15
|
+
lt: number;
|
|
16
|
+
} | {
|
|
17
|
+
field: string;
|
|
18
|
+
gte: number;
|
|
19
|
+
} | {
|
|
20
|
+
field: string;
|
|
21
|
+
lte: number;
|
|
22
|
+
} | {
|
|
23
|
+
field: string;
|
|
24
|
+
in: unknown[];
|
|
25
|
+
} | {
|
|
26
|
+
field: string;
|
|
27
|
+
notIn: unknown[];
|
|
28
|
+
} | {
|
|
29
|
+
field: string;
|
|
30
|
+
truthy: true;
|
|
31
|
+
} | {
|
|
32
|
+
field: string;
|
|
33
|
+
falsy: true;
|
|
34
|
+
} | {
|
|
35
|
+
field: string;
|
|
36
|
+
contains: string;
|
|
37
|
+
} | {
|
|
38
|
+
and: FieldCondition[];
|
|
39
|
+
} | {
|
|
40
|
+
or: FieldCondition[];
|
|
41
|
+
} | {
|
|
42
|
+
not: FieldCondition;
|
|
43
|
+
};
|
|
44
|
+
export interface FieldValidation {
|
|
45
|
+
required?: boolean | string;
|
|
46
|
+
minLength?: number | {
|
|
47
|
+
value: number;
|
|
48
|
+
message: string;
|
|
49
|
+
};
|
|
50
|
+
maxLength?: number | {
|
|
51
|
+
value: number;
|
|
52
|
+
message: string;
|
|
53
|
+
};
|
|
54
|
+
min?: number | {
|
|
55
|
+
value: number;
|
|
56
|
+
message: string;
|
|
57
|
+
};
|
|
58
|
+
max?: number | {
|
|
59
|
+
value: number;
|
|
60
|
+
message: string;
|
|
61
|
+
};
|
|
62
|
+
/** Pattern as a string (converted to RegExp at runtime). */
|
|
63
|
+
pattern?: string | {
|
|
64
|
+
value: string;
|
|
65
|
+
message: string;
|
|
66
|
+
};
|
|
67
|
+
email?: boolean | string;
|
|
68
|
+
}
|
|
69
|
+
export interface FormFieldSchema {
|
|
70
|
+
name: string;
|
|
71
|
+
label: string;
|
|
72
|
+
type: FormFieldType;
|
|
73
|
+
defaultValue?: unknown;
|
|
74
|
+
placeholder?: string;
|
|
75
|
+
hint?: string;
|
|
76
|
+
size?: 'sm' | 'md' | 'lg';
|
|
77
|
+
disabled?: boolean;
|
|
78
|
+
readonly?: boolean;
|
|
79
|
+
validation?: FieldValidation;
|
|
80
|
+
condition?: FieldCondition;
|
|
81
|
+
/** Grid column span (1-3). Defaults to 1. */
|
|
82
|
+
colSpan?: number;
|
|
83
|
+
/** Extra props passed directly to the underlying component. */
|
|
84
|
+
props?: Record<string, unknown>;
|
|
85
|
+
}
|
|
86
|
+
export interface FormSectionSchema {
|
|
87
|
+
id: string;
|
|
88
|
+
title: string;
|
|
89
|
+
description?: string;
|
|
90
|
+
collapsible?: boolean;
|
|
91
|
+
defaultOpen?: boolean;
|
|
92
|
+
/** Number of grid columns (1-3). Defaults to 1. */
|
|
93
|
+
columns?: 1 | 2 | 3;
|
|
94
|
+
fields: FormFieldSchema[];
|
|
95
|
+
condition?: FieldCondition;
|
|
96
|
+
}
|
|
97
|
+
export interface FormStepSchema {
|
|
98
|
+
id: string;
|
|
99
|
+
label: string;
|
|
100
|
+
description?: string;
|
|
101
|
+
icon?: string;
|
|
102
|
+
optional?: boolean;
|
|
103
|
+
sections: FormSectionSchema[];
|
|
104
|
+
}
|
|
105
|
+
export type FormSchema = {
|
|
106
|
+
sections: FormSectionSchema[];
|
|
107
|
+
steps?: never;
|
|
108
|
+
submitLabel?: string;
|
|
109
|
+
cancelLabel?: string;
|
|
110
|
+
showCancel?: boolean;
|
|
111
|
+
} | {
|
|
112
|
+
sections?: never;
|
|
113
|
+
steps: FormStepSchema[];
|
|
114
|
+
submitLabel?: string;
|
|
115
|
+
cancelLabel?: string;
|
|
116
|
+
showCancel?: boolean;
|
|
117
|
+
};
|
|
118
|
+
export interface FieldEnhancement<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
119
|
+
/** Custom validator appended to the field's rules. */
|
|
120
|
+
validate?: (value: unknown, data: T) => string | undefined | null;
|
|
121
|
+
/** Dynamic visibility (overrides schema condition if both present). */
|
|
122
|
+
visible?: (data: T) => boolean;
|
|
123
|
+
/** Dynamic options for select/multiselect/radio fields. */
|
|
124
|
+
options?: (data: T) => {
|
|
125
|
+
label: string;
|
|
126
|
+
value: unknown;
|
|
127
|
+
}[];
|
|
128
|
+
/** Dynamic extra props merged into the component. */
|
|
129
|
+
props?: (data: T) => Record<string, unknown>;
|
|
130
|
+
}
|
|
131
|
+
export interface FormEnhancements<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
132
|
+
/** Per-field enhancements keyed by field name. */
|
|
133
|
+
fields?: Partial<Record<keyof T, FieldEnhancement<T>>>;
|
|
134
|
+
/** Called on successful submit. */
|
|
135
|
+
onSubmit?: (data: T) => Promise<void> | void;
|
|
136
|
+
/** Transform data before submit (return transformed copy). */
|
|
137
|
+
transform?: (data: T) => T | Record<string, unknown>;
|
|
138
|
+
/** Called when any field value changes. */
|
|
139
|
+
onFieldChange?: (field: keyof T, value: unknown, data: T) => void;
|
|
140
|
+
}
|
|
141
|
+
export interface UseFormBuilderReturn<T extends Record<string, unknown>> {
|
|
142
|
+
/** Underlying useForm return. */
|
|
143
|
+
form: import('../composables/useForm').UseFormReturn<T>;
|
|
144
|
+
/** Validation rules derived from schema + enhancements. */
|
|
145
|
+
rules: Partial<Record<keyof T, FieldRules>>;
|
|
146
|
+
/** Whether a field is currently visible. */
|
|
147
|
+
isFieldVisible: (name: string) => boolean;
|
|
148
|
+
/** Whether a section is currently visible. */
|
|
149
|
+
isSectionVisible: (id: string) => boolean;
|
|
150
|
+
/** All flattened field schemas. */
|
|
151
|
+
fields: FormFieldSchema[];
|
|
152
|
+
/** Resolve final component props for a field. */
|
|
153
|
+
getResolvedFieldProps: (field: FormFieldSchema) => Record<string, unknown>;
|
|
154
|
+
/** Get options for a select/radio/multiselect field. */
|
|
155
|
+
getFieldOptions: (name: string) => {
|
|
156
|
+
label: string;
|
|
157
|
+
value: unknown;
|
|
158
|
+
}[] | undefined;
|
|
159
|
+
currentStep: import('vue').Ref<number>;
|
|
160
|
+
isCurrentStepValid: import('vue').ComputedRef<boolean>;
|
|
161
|
+
goNext: () => boolean;
|
|
162
|
+
goBack: () => void;
|
|
163
|
+
goToStep: (index: number) => void;
|
|
164
|
+
validate: () => boolean;
|
|
165
|
+
reset: (values?: Partial<T>) => void;
|
|
166
|
+
submit: () => Promise<void>;
|
|
167
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export type { ContainerDirection, ButtonVariant, ButtonSize, InputType, ModalSize, AlertType, Toast, TabItem, SelectOption, RadioOption, FormFieldProps, SidebarToolSection, CollapsibleState, TopBarVariant, TopBarPage, TopBarTab, TopBarTabOption, TopBarSettingsConfig, WellPlateFormat, WellState, WellPlateSelectionMode, WellPlateSize, WellShape, Well, HeatmapColorScale, HeatmapConfig, SlotPosition, WellExtendedData, WellEditData, WellEditField, WellLegendItem, PlateCondition, ColumnCondition, RowCondition, Rack, SampleType, PlateMap, PlateMapEditorState, ProtocolStepType, ProtocolStepStatus, ProtocolStep, SampleGroup, GroupItem, FileUploaderMode, SegmentedOption, SegmentedControlVariant, SegmentedControlSize, MultiSelectOption, MultiSelectSize, PillVariant, PillSize, CalendarSelectionMode, CalendarMarker, CalendarDayContext, SortDirection, SortState, DataFrameColumn, PaginationState, SpinnerSize, SpinnerVariant, DividerSpacing, StatusType, ProgressVariant, ProgressSize, AvatarSize, EmptyStateColor, EmptyStateSize, BreadcrumbItem, TooltipPosition, ConfirmVariant, SettingsTab, NumberNotation, UnitOption, WizardStep, WizardStepState, AuditEntryType, AuditEntry, BatchItemStatus, BatchItem, BatchSummary, TimePickerFormat, TimeRange, ScheduleView, ScheduleEventStatus, ScheduleEvent, ScheduleBlockedSlot, ScheduleSlotContext, ScheduleEventCreateContext, ScheduleEventUpdateContext, ResourceStatus, ResourceSpec, MoleculeData, StorageCondition, ReagentColumn, Reagent, TreeNodeType, BadgeVariant, TreeNode, } from './components';
|
|
2
|
+
export type { FormFieldType, FieldCondition, FieldValidation, FormFieldSchema, FormSectionSchema, FormStepSchema, FormSchema, FieldEnhancement, FormEnhancements, UseFormBuilderReturn, } from './form-builder';
|
|
2
3
|
export type { AuthConfig, UserInfo, LoginResponse, TokenVerifyResponse, RegisterRequest, UpdateProfileRequest, CredentialInfo, } from './auth';
|
|
3
4
|
export type { PluginInfo, PluginNavItem, PluginSettings, PluginSettingField, PlatformContext, PlatformContextOptions, PlatformEventType, PlatformEvent, ThemeMode, ColorPalette, TableDensity, } from './platform';
|
package/package.json
CHANGED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { getFieldRegistryEntry, getTypeDefault } from '../../composables/formBuilderRegistry'
|
|
3
|
+
import type { FormFieldType } from '../../types/form-builder'
|
|
4
|
+
|
|
5
|
+
describe('formBuilderRegistry', () => {
|
|
6
|
+
describe('getFieldRegistryEntry', () => {
|
|
7
|
+
it('should return an entry for every supported field type', () => {
|
|
8
|
+
const types: FormFieldType[] = [
|
|
9
|
+
'text', 'email', 'password', 'tel', 'url', 'search',
|
|
10
|
+
'number', 'textarea', 'select', 'multiselect',
|
|
11
|
+
'checkbox', 'toggle', 'radio', 'slider', 'tags',
|
|
12
|
+
'date', 'time', 'datetime', 'file',
|
|
13
|
+
'formula', 'sequence', 'molecule', 'concentration', 'unit',
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
for (const type of types) {
|
|
17
|
+
const entry = getFieldRegistryEntry(type)
|
|
18
|
+
expect(entry, `missing registry entry for type "${type}"`).toBeDefined()
|
|
19
|
+
expect(entry.component).toBeDefined()
|
|
20
|
+
expect(typeof entry.vModel).toBe('boolean')
|
|
21
|
+
expect(typeof entry.defaults).toBe('object')
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('should return correct component for text type', () => {
|
|
26
|
+
const entry = getFieldRegistryEntry('text')
|
|
27
|
+
expect(entry.vModel).toBe(true)
|
|
28
|
+
expect(entry.defaults).toEqual({ type: 'text' })
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('should return correct defaults for email type', () => {
|
|
32
|
+
const entry = getFieldRegistryEntry('email')
|
|
33
|
+
expect(entry.defaults).toEqual({ type: 'email' })
|
|
34
|
+
expect(entry.vModel).toBe(true)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('should return correct defaults for password type', () => {
|
|
38
|
+
const entry = getFieldRegistryEntry('password')
|
|
39
|
+
expect(entry.defaults).toEqual({ type: 'password' })
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('should return correct defaults for tel type', () => {
|
|
43
|
+
const entry = getFieldRegistryEntry('tel')
|
|
44
|
+
expect(entry.defaults).toEqual({ type: 'tel' })
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('should return correct defaults for url type', () => {
|
|
48
|
+
const entry = getFieldRegistryEntry('url')
|
|
49
|
+
expect(entry.defaults).toEqual({ type: 'url' })
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('should return correct defaults for search type', () => {
|
|
53
|
+
const entry = getFieldRegistryEntry('search')
|
|
54
|
+
expect(entry.defaults).toEqual({ type: 'search' })
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should have vModel false for file type', () => {
|
|
58
|
+
const entry = getFieldRegistryEntry('file')
|
|
59
|
+
expect(entry.vModel).toBe(false)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('should have vModel true for all non-file types', () => {
|
|
63
|
+
const vModelTypes: FormFieldType[] = [
|
|
64
|
+
'text', 'email', 'password', 'tel', 'url', 'search',
|
|
65
|
+
'number', 'textarea', 'select', 'multiselect',
|
|
66
|
+
'checkbox', 'toggle', 'radio', 'slider', 'tags',
|
|
67
|
+
'date', 'time', 'datetime',
|
|
68
|
+
'formula', 'sequence', 'molecule', 'concentration', 'unit',
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
for (const type of vModelTypes) {
|
|
72
|
+
const entry = getFieldRegistryEntry(type)
|
|
73
|
+
expect(entry.vModel, `vModel should be true for "${type}"`).toBe(true)
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('should return empty defaults object for number type', () => {
|
|
78
|
+
const entry = getFieldRegistryEntry('number')
|
|
79
|
+
expect(entry.defaults).toEqual({})
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('should return empty defaults object for checkbox type', () => {
|
|
83
|
+
const entry = getFieldRegistryEntry('checkbox')
|
|
84
|
+
expect(entry.defaults).toEqual({})
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
describe('getTypeDefault', () => {
|
|
89
|
+
it('should return false for checkbox', () => {
|
|
90
|
+
expect(getTypeDefault('checkbox')).toBe(false)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('should return false for toggle', () => {
|
|
94
|
+
expect(getTypeDefault('toggle')).toBe(false)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('should return undefined for number', () => {
|
|
98
|
+
expect(getTypeDefault('number')).toBeUndefined()
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('should return undefined for slider', () => {
|
|
102
|
+
expect(getTypeDefault('slider')).toBeUndefined()
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('should return empty array for multiselect', () => {
|
|
106
|
+
expect(getTypeDefault('multiselect')).toEqual([])
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('should return empty array for tags', () => {
|
|
110
|
+
expect(getTypeDefault('tags')).toEqual([])
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('should return empty string for text', () => {
|
|
114
|
+
expect(getTypeDefault('text')).toBe('')
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('should return empty string for email', () => {
|
|
118
|
+
expect(getTypeDefault('email')).toBe('')
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('should return empty string for password', () => {
|
|
122
|
+
expect(getTypeDefault('password')).toBe('')
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('should return empty string for textarea', () => {
|
|
126
|
+
expect(getTypeDefault('textarea')).toBe('')
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('should return empty string for select', () => {
|
|
130
|
+
expect(getTypeDefault('select')).toBe('')
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('should return empty string for date', () => {
|
|
134
|
+
expect(getTypeDefault('date')).toBe('')
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('should return empty string for time', () => {
|
|
138
|
+
expect(getTypeDefault('time')).toBe('')
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('should return empty string for datetime', () => {
|
|
142
|
+
expect(getTypeDefault('datetime')).toBe('')
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('should return empty string for file', () => {
|
|
146
|
+
expect(getTypeDefault('file')).toBe('')
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('should return empty string for radio', () => {
|
|
150
|
+
expect(getTypeDefault('radio')).toBe('')
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('should return empty string for formula', () => {
|
|
154
|
+
expect(getTypeDefault('formula')).toBe('')
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('should return empty string for sequence', () => {
|
|
158
|
+
expect(getTypeDefault('sequence')).toBe('')
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
it('should return empty string for molecule', () => {
|
|
162
|
+
expect(getTypeDefault('molecule')).toBe('')
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
it('should return empty string for concentration', () => {
|
|
166
|
+
expect(getTypeDefault('concentration')).toBe('')
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('should return empty string for unit', () => {
|
|
170
|
+
expect(getTypeDefault('unit')).toBe('')
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('should return a new array instance each call for multiselect', () => {
|
|
174
|
+
const a = getTypeDefault('multiselect')
|
|
175
|
+
const b = getTypeDefault('multiselect')
|
|
176
|
+
expect(a).toEqual(b)
|
|
177
|
+
// Each call should return a distinct array
|
|
178
|
+
expect(a).not.toBe(b)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('should return a new array instance each call for tags', () => {
|
|
182
|
+
const a = getTypeDefault('tags')
|
|
183
|
+
const b = getTypeDefault('tags')
|
|
184
|
+
expect(a).not.toBe(b)
|
|
185
|
+
})
|
|
186
|
+
})
|
|
187
|
+
})
|