diff --git a/CHANGELOG.md b/CHANGELOG.md index 939f8efe5..3639e5830 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## Next + +- **Fixed:** Regression of accepting non-string labels in `useField` and `connectField`. [\#816](https://github.com/vazco/uniforms/issues/816) + ## [v3.0.0-rc.5](https://github.com/vazco/uniforms/tree/v3.0.0-rc.5) (2020-09-30) - **Added:** New `disableItem` prop in `SelectField`. [\#736](https://github.com/vazco/uniforms/issues/736) diff --git a/packages/uniforms/__tests__/connectField.tsx b/packages/uniforms/__tests__/connectField.tsx index aafabd746..beea5dd46 100644 --- a/packages/uniforms/__tests__/connectField.tsx +++ b/packages/uniforms/__tests__/connectField.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { ReactNode } from 'react'; import { SimpleSchema } from 'simpl-schema'; import { Context, connectField, randomIds } from 'uniforms'; import { SimpleSchemaBridge } from 'uniforms-bridge-simple-schema'; @@ -125,4 +125,61 @@ describe('connectField', () => { expect(onChange).toBeCalledWith('field', 'initialValueExample'); }); }); + + // TODO: Write tests for `placeholder`. + describe('when rendered with label', () => { + const labelA = Error; + const labelB = OK; + + it.each([ + ['Props', '', false, 'Props'], + ['Props', '', true, 'Props'], + ['Props', 'Schema', false, 'Props'], + ['Props', 'Schema', true, 'Props'], + ['Props', labelB, false, 'Props'], + ['Props', labelB, true, 'Props'], + [false, '', false, ''], + [false, '', true, ''], + [false, 'Schema', false, ''], + [false, 'Schema', true, ''], + [false, labelB, false, ''], + [false, labelB, true, ''], + [labelA, '', false, labelA], + [labelA, '', true, labelA], + [labelA, 'Schema', false, labelA], + [labelA, 'Schema', true, labelA], + [labelA, labelB, false, labelA], + [labelA, labelB, true, labelA], + [true, '', false, ''], + [true, '', true, ''], + [true, 'Schema', false, 'Schema'], + [true, 'Schema', true, 'Schema'], + [true, labelB, false, labelB], + [true, labelB, true, labelB], + [undefined, '', false, ''], + [undefined, '', true, ''], + [undefined, 'Schema', false, ''], + [undefined, 'Schema', true, 'Schema'], + [undefined, labelB, false, ''], + [undefined, labelB, true, labelB], + ] as [ReactNode, ReactNode, boolean, ReactNode][])( + 'resolves it correctly (%#)', + (prop, schema, state, result) => { + const context: typeof reactContext = { + context: { + ...reactContext.context, + state: { ...reactContext.context.state, label: state }, + }, + }; + + jest + .spyOn(context.context.schema, 'getProps') + .mockReturnValueOnce({ label: schema }); + + const Field = connectField(Test); + const wrapper = mount(, context); + expect(wrapper.find(Test).prop('label')).toBe(result); + }, + ); + }); }); diff --git a/packages/uniforms/src/useField.tsx b/packages/uniforms/src/useField.tsx index 8a4ce49a5..96d5710f5 100644 --- a/packages/uniforms/src/useField.tsx +++ b/packages/uniforms/src/useField.tsx @@ -1,30 +1,31 @@ import get from 'lodash/get'; import mapValues from 'lodash/mapValues'; -import { useCallback, useEffect, useMemo } from 'react'; +import { ReactNode, useCallback, useEffect, useMemo } from 'react'; import { joinName } from './joinName'; import { GuaranteedProps } from './types'; import { useForm } from './useForm'; function propagate( - prop: unknown, - schema: unknown, + prop: ReactNode, + schema: ReactNode, state: boolean, - fallback: string, -): [string, string] { - const schemaDisabled = schema === false || schema === ''; - const schemaValue = - typeof schema === 'string' ? schema : schemaDisabled ? '' : fallback; - const resultValue = - typeof prop === 'string' - ? prop - : prop === null || - prop === false || - (prop === undefined && !state) || - schemaDisabled + fallback: ReactNode, +): [ReactNode, ReactNode] { + const forcedFallbackInProp = prop === true || prop === undefined; + const forcedFallbackInSchema = schema === true || schema === undefined; + + const schemaValue = forcedFallbackInSchema ? fallback : schema; + const value = + prop === '' || + prop === false || + (forcedFallbackInProp && (forcedFallbackInSchema || !state)) ? '' - : schemaValue; - return [resultValue, schemaValue]; + : forcedFallbackInProp + ? schemaValue + : prop; + + return [value, schemaValue]; } export function useField< @@ -112,7 +113,8 @@ export function useField< ...props, label, name, - placeholder, + // TODO: Should we assert `typeof placeholder === 'string'`? + placeholder: placeholder as string, }; return [fieldProps, context] as [typeof fieldProps, typeof context];