@zeno-lib/forms 0.2.1 → 0.2.2
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/create-zeno-form.d.mts +2 -3
- package/dist/form-element.d.mts +2 -2
- package/dist/lib/aria.d.mts +2 -3
- package/dist/lib/contexts.d.mts +2 -2
- package/dist/lib/formatted-number.mjs +5 -5
- package/dist/lib/schema-defaults.mjs +1 -1
- package/dist/lib/schema-required.mjs +2 -2
- package/package.json +24 -23
- package/src/fields/combobox-field.tsx +2 -2
- package/src/fields/date-picker-field.tsx +1 -1
- package/src/fields/number-field.tsx +1 -1
- package/src/fields/slider-field.tsx +7 -5
- package/src/form-dialog.tsx +2 -2
- package/src/lib/formatted-number.ts +5 -5
- package/src/lib/schema-defaults.ts +1 -1
- package/src/lib/schema-required.test.ts +1 -1
- package/src/lib/schema-required.ts +3 -5
- package/src/lib/validation-logic.test.ts +0 -1
- package/src/slider-field.test.tsx +30 -0
|
@@ -99,7 +99,7 @@ type CreateZenoFormConfig<FC extends Record<string, AnyComponent>, FMC extends R
|
|
|
99
99
|
* the `createFormHook` primitives plus the schema-aware `useForm` and the typed
|
|
100
100
|
* `useAppFields` wrappers (prop types are inferred from the injected components).
|
|
101
101
|
*/
|
|
102
|
-
declare function createZenoForm<FC extends Record<string, AnyComponent>, FMC extends Record<string, AnyComponent>>(config: CreateZenoFormConfig<FC, FMC>): {
|
|
102
|
+
export declare function createZenoForm<FC extends Record<string, AnyComponent>, FMC extends Record<string, AnyComponent>>(config: CreateZenoFormConfig<FC, FMC>): {
|
|
103
103
|
useAppFields: <TForm extends AnyAppForm>(form: TForm) => FieldWrappers<FC, FormDataOf<TForm>>;
|
|
104
104
|
useAppForm: <TFormData, TOnMount extends undefined | FormValidateOrFn<TFormData>, TOnChange extends undefined | FormValidateOrFn<TFormData>, TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData>, TOnBlur extends undefined | FormValidateOrFn<TFormData>, TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData>, TOnSubmit extends undefined | FormValidateOrFn<TFormData>, TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData>, TOnDynamic extends undefined | FormValidateOrFn<TFormData>, TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData>, TOnServer extends undefined | FormAsyncValidateOrFn<TFormData>, TSubmitMeta>(props: FormOptions<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta>) => import("@tanstack/react-form").AppFieldExtendedReactFormApi<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta, FC, FMC>;
|
|
105
105
|
useForm: <TFormData, TOnMount extends undefined | FormValidateOrFn<TFormData> = undefined, TOnChange extends undefined | FormValidateOrFn<TFormData> = undefined, TOnChangeAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined, TOnBlur extends undefined | FormValidateOrFn<TFormData> = undefined, TOnBlurAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined, TOnSubmit extends undefined | FormValidateOrFn<TFormData> = undefined, TOnSubmitAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined, TOnDynamic extends undefined | FormValidateOrFn<TFormData> = undefined, TOnDynamicAsync extends undefined | FormAsyncValidateOrFn<TFormData> = undefined, TOnServer extends undefined | FormAsyncValidateOrFn<TFormData> = undefined, TSubmitMeta = never>(options: UseFormOptions<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta>) => import("@tanstack/react-form").FormApi<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta> & import("@tanstack/react-form").ReactFormApi<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync, TOnServer, TSubmitMeta> & NoInfer<FMC> & {
|
|
@@ -114,5 +114,4 @@ declare function createZenoForm<FC extends Record<string, AnyComponent>, FMC ext
|
|
|
114
114
|
form: import("@tanstack/react-form").AppFieldExtendedReactFormApi<[unknown] extends [TFormData] ? any : TFormData, [FormValidateOrFn<TFormData> | undefined] extends [TOnMount] ? [TOnMount] extends [TOnMount & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnMount : TOnMount, [FormValidateOrFn<TFormData> | undefined] extends [TOnChange] ? [TOnChange] extends [TOnChange & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnChange : TOnChange, [FormValidateOrFn<TFormData> | undefined] extends [TOnChangeAsync] ? [TOnChangeAsync] extends [TOnChangeAsync & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnChangeAsync : TOnChangeAsync, [FormValidateOrFn<TFormData> | undefined] extends [TOnBlur] ? [TOnBlur] extends [TOnBlur & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnBlur : TOnBlur, [FormValidateOrFn<TFormData> | undefined] extends [TOnBlurAsync] ? [TOnBlurAsync] extends [TOnBlurAsync & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnBlurAsync : TOnBlurAsync, [FormValidateOrFn<TFormData> | undefined] extends [TOnSubmit] ? [TOnSubmit] extends [TOnSubmit & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnSubmit : TOnSubmit, [FormValidateOrFn<TFormData> | undefined] extends [TOnSubmitAsync] ? [TOnSubmitAsync] extends [TOnSubmitAsync & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnSubmitAsync : TOnSubmitAsync, [FormValidateOrFn<TFormData> | undefined] extends [TOnDynamic] ? [TOnDynamic] extends [TOnDynamic & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnDynamic : TOnDynamic, [FormValidateOrFn<TFormData> | undefined] extends [TOnDynamicAsync] ? [TOnDynamicAsync] extends [TOnDynamicAsync & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnDynamicAsync : TOnDynamicAsync, [FormValidateOrFn<TFormData> | undefined] extends [TOnServer] ? [TOnServer] extends [TOnServer & (FormValidateOrFn<TFormData> | undefined)] ? any : TOnServer : TOnServer, [unknown] extends [TSubmitMeta] ? any : TSubmitMeta, [unknown] extends [FC] ? any : FC, [unknown] extends [FMC] ? any : FMC>;
|
|
115
115
|
}>>;
|
|
116
116
|
};
|
|
117
|
-
//#endregion
|
|
118
|
-
export { createZenoForm };
|
|
117
|
+
//#endregion
|
package/dist/form-element.d.mts
CHANGED
|
@@ -6,8 +6,8 @@ type FormProviderProps = {
|
|
|
6
6
|
handleSubmit: () => unknown;
|
|
7
7
|
};
|
|
8
8
|
};
|
|
9
|
-
declare function FormProvider({ children, form }: FormProviderProps): import("react
|
|
9
|
+
declare function FormProvider({ children, form }: FormProviderProps): import("react").JSX.Element;
|
|
10
10
|
type FormProps = Omit<ComponentProps<"form">, "onSubmit">;
|
|
11
|
-
declare function Form({ children, className, ...props }: FormProps): import("react
|
|
11
|
+
declare function Form({ children, className, ...props }: FormProps): import("react").JSX.Element;
|
|
12
12
|
//#endregion
|
|
13
13
|
export { Form, FormProvider };
|
package/dist/lib/aria.d.mts
CHANGED
package/dist/lib/contexts.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/lib/contexts.d.ts
|
|
2
2
|
declare const fieldContext: import("react").Context<import("@tanstack/react-form").AnyFieldApi>, formContext: import("react").Context<import("@tanstack/react-form").AnyFormApi>, useFieldContext: <TData>() => import("@tanstack/react-form").FieldApi<any, string, TData, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any>, useFormContext: () => import("@tanstack/react-form").ReactFormExtendedApi<Record<string, never>, any, any, any, any, any, any, any, any, any, any, any>;
|
|
3
|
-
declare const FormProvider: import("react").Provider<import("@tanstack/react-form").AnyFormApi>;
|
|
3
|
+
export declare const FormProvider: import("react").Provider<import("@tanstack/react-form").AnyFormApi>;
|
|
4
4
|
//#endregion
|
|
5
|
-
export {
|
|
5
|
+
export { fieldContext, formContext, useFieldContext, useFormContext };
|
|
@@ -26,7 +26,7 @@ function normalizeMinus(text) {
|
|
|
26
26
|
}
|
|
27
27
|
/** Format a number for display (on blur, on reset, on first render). */
|
|
28
28
|
function formatNumber(value, { locale, maximumFractionDigits, minimumFractionDigits = 0, useGrouping = true }) {
|
|
29
|
-
if (value
|
|
29
|
+
if (value === null || value === void 0 || !Number.isFinite(value)) return "";
|
|
30
30
|
return normalizeMinus(new Intl.NumberFormat(locale, {
|
|
31
31
|
maximumFractionDigits,
|
|
32
32
|
minimumFractionDigits: Math.min(minimumFractionDigits, maximumFractionDigits),
|
|
@@ -102,19 +102,19 @@ function parseLocaleNumber(text, locale) {
|
|
|
102
102
|
}
|
|
103
103
|
function countSignificant(text, end, decimal) {
|
|
104
104
|
let count = 0;
|
|
105
|
-
for (let i = 0; i < end && i < text.length; i
|
|
105
|
+
for (let i = 0; i < end && i < text.length; i += 1) {
|
|
106
106
|
const char = text[i];
|
|
107
|
-
if (DIGIT.test(char) || char === decimal || MINUS_SIGNS.test(char)) count
|
|
107
|
+
if (DIGIT.test(char) || char === decimal || MINUS_SIGNS.test(char)) count += 1;
|
|
108
108
|
}
|
|
109
109
|
return count;
|
|
110
110
|
}
|
|
111
111
|
function caretForSignificant(text, significant, decimal) {
|
|
112
112
|
if (significant <= 0) return 0;
|
|
113
113
|
let count = 0;
|
|
114
|
-
for (let i = 0; i < text.length; i
|
|
114
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
115
115
|
const char = text[i];
|
|
116
116
|
if (DIGIT.test(char) || char === decimal || char === "-") {
|
|
117
|
-
count
|
|
117
|
+
count += 1;
|
|
118
118
|
if (count === significant) return i + 1;
|
|
119
119
|
}
|
|
120
120
|
}
|
|
@@ -5,7 +5,7 @@ function getDef(node) {
|
|
|
5
5
|
}
|
|
6
6
|
function extractDefaultForField(node) {
|
|
7
7
|
let current = node;
|
|
8
|
-
for (let depth = 0; depth < MAX_WRAPPER_DEPTH; depth
|
|
8
|
+
for (let depth = 0; depth < MAX_WRAPPER_DEPTH; depth += 1) {
|
|
9
9
|
const def = getDef(current);
|
|
10
10
|
if (!def?.type) return { ok: false };
|
|
11
11
|
switch (def.type) {
|
|
@@ -26,7 +26,7 @@ function probeContainer(expected) {
|
|
|
26
26
|
if (typeof expected !== "string") return;
|
|
27
27
|
const kind = expected.toLowerCase();
|
|
28
28
|
if (kind === "object") return {};
|
|
29
|
-
|
|
29
|
+
return kind === "array" ? [{}] : void 0;
|
|
30
30
|
}
|
|
31
31
|
function fillAt(root, keys, value) {
|
|
32
32
|
let node = root;
|
|
@@ -53,7 +53,7 @@ function validateSync(schema, value) {
|
|
|
53
53
|
function getRequiredPaths(schema) {
|
|
54
54
|
const required = /* @__PURE__ */ new Set();
|
|
55
55
|
const probe = {};
|
|
56
|
-
for (let depth = 0; depth < MAX_PROBE_DEPTH; depth
|
|
56
|
+
for (let depth = 0; depth < MAX_PROBE_DEPTH; depth += 1) {
|
|
57
57
|
const issues = validateSync(schema, probe);
|
|
58
58
|
if (!issues) return required;
|
|
59
59
|
let descended = false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zeno-lib/forms",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"repository": {
|
|
5
|
+
"url": "https://github.com/zeno-lib/zeno.git",
|
|
6
|
+
"directory": "packages/forms"
|
|
7
|
+
},
|
|
4
8
|
"type": "module",
|
|
5
9
|
"exports": {
|
|
6
10
|
".": {
|
|
@@ -25,12 +29,23 @@
|
|
|
25
29
|
"dist",
|
|
26
30
|
"src"
|
|
27
31
|
],
|
|
28
|
-
"
|
|
29
|
-
"
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@tanstack/react-form": "1.33.5",
|
|
34
|
+
"@tanstack/react-form-nextjs": "1.33.5"
|
|
30
35
|
},
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "26.6.4",
|
|
38
|
+
"@types/react": "19.3.0",
|
|
39
|
+
"@types/react-dom": "19.3.0",
|
|
40
|
+
"@zeno-lib/test": "^0.1.1",
|
|
41
|
+
"@zeno-lib/typescript": "^1.1.0",
|
|
42
|
+
"@zeno-lib/ui": "^0.0.1",
|
|
43
|
+
"lucide-react": "^1.52.0",
|
|
44
|
+
"tsdown": "^0.23.0",
|
|
45
|
+
"typescript": "7.0.2",
|
|
46
|
+
"vite": "8.3.3",
|
|
47
|
+
"vitest": "5.0.3",
|
|
48
|
+
"zod": "^4.6.5"
|
|
34
49
|
},
|
|
35
50
|
"peerDependencies": {
|
|
36
51
|
"@tanstack/react-devtools": ">=0.10",
|
|
@@ -48,26 +63,12 @@
|
|
|
48
63
|
"optional": true
|
|
49
64
|
}
|
|
50
65
|
},
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"@tanstack/react-form-nextjs": "1.33.5"
|
|
54
|
-
},
|
|
55
|
-
"devDependencies": {
|
|
56
|
-
"@types/node": "25.6.0",
|
|
57
|
-
"@types/react": "19.2.14",
|
|
58
|
-
"@types/react-dom": "19.2.3",
|
|
59
|
-
"lucide-react": "^1.23.0",
|
|
60
|
-
"tsdown": "^0.22.14",
|
|
61
|
-
"typescript": "7.0.2",
|
|
62
|
-
"vite": "8.0.16",
|
|
63
|
-
"vitest": "5.0.2",
|
|
64
|
-
"zod": "^4.3.6",
|
|
65
|
-
"@zeno-lib/test": "^0.1.0",
|
|
66
|
-
"@zeno-lib/typescript": "^1.1.0",
|
|
67
|
-
"@zeno-lib/ui": "^0.0.1"
|
|
66
|
+
"publishConfig": {
|
|
67
|
+
"access": "public"
|
|
68
68
|
},
|
|
69
69
|
"scripts": {
|
|
70
70
|
"build": "tsdown",
|
|
71
|
+
"dev": "tsdown --watch --no-clean",
|
|
71
72
|
"test": "vitest --run",
|
|
72
73
|
"test:watch": "vitest watch",
|
|
73
74
|
"types:check": "tsc --noEmit"
|
|
@@ -124,7 +124,7 @@ function ComboboxField<T = string>({
|
|
|
124
124
|
items={items}
|
|
125
125
|
onInputValueChange={onInputValueChange}
|
|
126
126
|
onValueChange={(next) => {
|
|
127
|
-
if (next
|
|
127
|
+
if (next === null) {
|
|
128
128
|
field.handleChange(undefined)
|
|
129
129
|
return
|
|
130
130
|
}
|
|
@@ -143,7 +143,7 @@ function ComboboxField<T = string>({
|
|
|
143
143
|
name={field.name}
|
|
144
144
|
onBlur={field.handleBlur}
|
|
145
145
|
placeholder={placeholder}
|
|
146
|
-
showClear={showClear && value
|
|
146
|
+
showClear={showClear && value !== null && value !== "" && !loading}
|
|
147
147
|
>
|
|
148
148
|
{loading && (
|
|
149
149
|
<InputGroupAddon align="inline-end">
|
|
@@ -11,7 +11,7 @@ type NumberFieldProps = Omit<
|
|
|
11
11
|
|
|
12
12
|
function NumberField(props: NumberFieldProps) {
|
|
13
13
|
const field = useFieldContext<number | undefined>()
|
|
14
|
-
const value = field.state
|
|
14
|
+
const { value } = field.state
|
|
15
15
|
const inputValue =
|
|
16
16
|
typeof value === "number" && !Number.isNaN(value) ? value : ""
|
|
17
17
|
|
|
@@ -51,12 +51,14 @@ function SliderField({
|
|
|
51
51
|
const schemaRequired = useIsFieldRequired(field)
|
|
52
52
|
const isRequired = required ?? schemaRequired
|
|
53
53
|
|
|
54
|
-
const value = field.state
|
|
54
|
+
const { value } = field.state
|
|
55
55
|
const readout = formatValue && value !== undefined ? formatValue(value) : null
|
|
56
|
+
// Not a truthiness check: a formatter may return the number 0, which must still render.
|
|
57
|
+
const hasReadout = readout !== null && readout !== undefined
|
|
56
58
|
|
|
57
59
|
return (
|
|
58
60
|
<Field data-field={field.name} data-invalid={isInvalid}>
|
|
59
|
-
{(label ||
|
|
61
|
+
{(label || hasReadout) && (
|
|
60
62
|
<FieldContent className="flex-row items-center justify-between">
|
|
61
63
|
{label && (
|
|
62
64
|
<FieldLabel htmlFor={field.name}>
|
|
@@ -64,7 +66,7 @@ function SliderField({
|
|
|
64
66
|
{isRequired && <RequiredIndicator />}
|
|
65
67
|
</FieldLabel>
|
|
66
68
|
)}
|
|
67
|
-
{
|
|
69
|
+
{hasReadout && (
|
|
68
70
|
<span className="text-muted-foreground text-sm tabular-nums">
|
|
69
71
|
{readout}
|
|
70
72
|
</span>
|
|
@@ -81,9 +83,9 @@ function SliderField({
|
|
|
81
83
|
name={field.name}
|
|
82
84
|
onBlur={field.handleBlur}
|
|
83
85
|
onValueChange={(next) => {
|
|
84
|
-
const
|
|
86
|
+
const nextValue: SliderValue =
|
|
85
87
|
typeof next === "number" ? next : Array.from(next)
|
|
86
|
-
field.handleChange(
|
|
88
|
+
field.handleChange(nextValue)
|
|
87
89
|
}}
|
|
88
90
|
value={value}
|
|
89
91
|
{...props}
|
package/src/form-dialog.tsx
CHANGED
|
@@ -84,7 +84,7 @@ function formatNumber(
|
|
|
84
84
|
useGrouping = true,
|
|
85
85
|
}: FormatNumberOptions
|
|
86
86
|
): string {
|
|
87
|
-
if (value
|
|
87
|
+
if (value === null || value === undefined || !Number.isFinite(value)) {
|
|
88
88
|
return ""
|
|
89
89
|
}
|
|
90
90
|
return normalizeMinus(
|
|
@@ -236,10 +236,10 @@ function parseLocaleNumber(text: string, locale?: string): number | null {
|
|
|
236
236
|
// the same logical place after separators were inserted or removed.
|
|
237
237
|
function countSignificant(text: string, end: number, decimal: string): number {
|
|
238
238
|
let count = 0
|
|
239
|
-
for (let i = 0; i < end && i < text.length; i
|
|
239
|
+
for (let i = 0; i < end && i < text.length; i += 1) {
|
|
240
240
|
const char = text[i] as string
|
|
241
241
|
if (DIGIT.test(char) || char === decimal || MINUS_SIGNS.test(char)) {
|
|
242
|
-
count
|
|
242
|
+
count += 1
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
245
|
return count
|
|
@@ -254,10 +254,10 @@ function caretForSignificant(
|
|
|
254
254
|
return 0
|
|
255
255
|
}
|
|
256
256
|
let count = 0
|
|
257
|
-
for (let i = 0; i < text.length; i
|
|
257
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
258
258
|
const char = text[i] as string
|
|
259
259
|
if (DIGIT.test(char) || char === decimal || char === "-") {
|
|
260
|
-
count
|
|
260
|
+
count += 1
|
|
261
261
|
if (count === significant) {
|
|
262
262
|
return i + 1
|
|
263
263
|
}
|
|
@@ -37,7 +37,7 @@ function getDef(node: SchemaNode | undefined): SchemaDef | undefined {
|
|
|
37
37
|
|
|
38
38
|
function extractDefaultForField(node: SchemaNode): ExtractResult {
|
|
39
39
|
let current: SchemaNode | undefined = node
|
|
40
|
-
for (let depth = 0; depth < MAX_WRAPPER_DEPTH; depth
|
|
40
|
+
for (let depth = 0; depth < MAX_WRAPPER_DEPTH; depth += 1) {
|
|
41
41
|
const def: SchemaDef | undefined = getDef(current)
|
|
42
42
|
if (!def?.type) {
|
|
43
43
|
return { ok: false }
|
|
@@ -166,7 +166,7 @@ describe("getRequiredPaths", () => {
|
|
|
166
166
|
const schema: StandardSchemaLike = {
|
|
167
167
|
"~standard": {
|
|
168
168
|
validate: (value) => {
|
|
169
|
-
const profile =
|
|
169
|
+
const { profile } = value as { profile?: unknown }
|
|
170
170
|
return profile === undefined
|
|
171
171
|
? { issues: [{ expected: "Object", path: [{ key: "profile" }] }] }
|
|
172
172
|
: {
|
|
@@ -50,6 +50,7 @@ const INDEX_SEGMENT = /^\d+$/
|
|
|
50
50
|
const INDEX_IN_NAME = /\[\d+\]|\.(\d+)(?=\.|\[|$)/g
|
|
51
51
|
|
|
52
52
|
function pathKey(part: PathPart): PropertyKey {
|
|
53
|
+
// biome-ignore lint/suspicious/noUnnecessaryConditions: issue paths come from third-party validators at runtime, and a non-conforming `null` segment would make `in` throw.
|
|
53
54
|
if (typeof part === "object" && part !== null && "key" in part) {
|
|
54
55
|
return part.key
|
|
55
56
|
}
|
|
@@ -93,10 +94,7 @@ function probeContainer(expected: unknown): unknown {
|
|
|
93
94
|
if (kind === "object") {
|
|
94
95
|
return {}
|
|
95
96
|
}
|
|
96
|
-
|
|
97
|
-
return [{}]
|
|
98
|
-
}
|
|
99
|
-
return
|
|
97
|
+
return kind === "array" ? [{}] : undefined
|
|
100
98
|
}
|
|
101
99
|
|
|
102
100
|
// Write `value` at `keys` inside `root`, only where nothing is set yet.
|
|
@@ -140,7 +138,7 @@ function validateSync(
|
|
|
140
138
|
function getRequiredPaths(schema: StandardSchemaLike): Set<string> {
|
|
141
139
|
const required = new Set<string>()
|
|
142
140
|
const probe: Record<PropertyKey, unknown> = {}
|
|
143
|
-
for (let depth = 0; depth < MAX_PROBE_DEPTH; depth
|
|
141
|
+
for (let depth = 0; depth < MAX_PROBE_DEPTH; depth += 1) {
|
|
144
142
|
const issues = validateSync(schema, probe)
|
|
145
143
|
if (!issues) {
|
|
146
144
|
return required
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { cleanup, render, screen } from "@zeno-lib/test/testing-library"
|
|
2
|
+
import { afterEach, describe, expect, test, vi } from "vitest"
|
|
3
|
+
import { Form, FormProvider, useForm } from "./create-form"
|
|
4
|
+
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
cleanup()
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
describe("SliderField", () => {
|
|
10
|
+
function Harness() {
|
|
11
|
+
const form = useForm({ defaultValues: { volume: 0 }, onSubmit: vi.fn() })
|
|
12
|
+
const { SliderField } = form
|
|
13
|
+
return (
|
|
14
|
+
<FormProvider form={form}>
|
|
15
|
+
<Form>
|
|
16
|
+
<SliderField
|
|
17
|
+
formatValue={(value) => value}
|
|
18
|
+
label="Volume"
|
|
19
|
+
name="volume"
|
|
20
|
+
/>
|
|
21
|
+
</Form>
|
|
22
|
+
</FormProvider>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
test("renders a numeric readout of 0", () => {
|
|
27
|
+
render(<Harness />)
|
|
28
|
+
expect(screen.getByText("0").tagName).toBe("SPAN")
|
|
29
|
+
})
|
|
30
|
+
})
|