diff --git a/.changeset/friendly-books-compare.md b/.changeset/friendly-books-compare.md new file mode 100644 index 00000000..4da7a222 --- /dev/null +++ b/.changeset/friendly-books-compare.md @@ -0,0 +1,5 @@ +--- +'@cube-dev/ui-kit': minor +--- + +Add casting property to Field component to cast Field value to different type that input allows diff --git a/src/components/forms/FileInput/FileInput.tsx b/src/components/forms/FileInput/FileInput.tsx index de69117e..9e9466e0 100644 --- a/src/components/forms/FileInput/FileInput.tsx +++ b/src/components/forms/FileInput/FileInput.tsx @@ -22,7 +22,7 @@ import { tasty, } from '../../../tasty'; import { FieldBaseProps } from '../../../shared'; -import { FieldWrapper } from '../FieldWrapper'; +import { wrapWithField } from '../wrapper'; import type { AriaTextFieldProps } from '@react-types/textfield'; @@ -135,6 +135,8 @@ function extractContents(element, callback) { } function FileInput(props: CubeFileInputProps, ref) { + props = useProviderProps(props); + let { id, name, @@ -163,11 +165,14 @@ function FileInput(props: CubeFileInputProps, ref) { type = 'file', inputProps, ...otherProps - } = useProviderProps(props); + } = props; + const [value, setValue] = useState(); const [dragHover, setDragHover] = useState(false); + let domRef = useRef(null); let defaultInputRef = useRef(null); + inputRef = inputRef || defaultInputRef; let styles = extractStyles(otherProps, CONTAINER_STYLES); @@ -247,31 +252,10 @@ function FileInput(props: CubeFileInputProps, ref) { ); - return ( - - ); + return wrapWithField(fileInput, domRef, { + ...props, + styles, + }); } /** diff --git a/src/components/forms/Form/ComplexForm.stories.tsx b/src/components/forms/Form/ComplexForm.stories.tsx index 695a529e..0cee8fdf 100644 --- a/src/components/forms/Form/ComplexForm.stories.tsx +++ b/src/components/forms/Form/ComplexForm.stories.tsx @@ -36,43 +36,43 @@ export default { parameters: { controls: { exclude: baseProps } }, }; -const UnknownSubmitErrorTemplate: StoryFn = (args) => { - const [form] = Form.useForm(); - - return ( -
{ - console.log('onSubmit:', v); - - throw new Error('Unknown error'); - }} - onSubmitFailed={(e) => { - console.log('onSubmitFailed', e); - }} - onValuesChange={(v) => { - console.log('onChange', v); - }} - > - ({ - async validator() { - await timeout(1000); - }, - }), - ]} - > - - - Submit - - - ); -}; +// const UnknownSubmitErrorTemplate: StoryFn = (args) => { +// const [form] = Form.useForm(); +// +// return ( +//
{ +// console.log('onSubmit:', v); +// +// throw new Error('Unknown error'); +// }} +// onSubmitFailed={(e) => { +// console.log('onSubmitFailed', e); +// }} +// onValuesChange={(v) => { +// console.log('onChange', v); +// }} +// > +// ({ +// async validator() { +// await timeout(1000); +// }, +// }), +// ]} +// > +// +// +// Submit +// +// +// ); +// }; const CustomSubmitErrorTemplate: StoryFn = (args) => { const [form] = Form.useForm(); @@ -387,8 +387,8 @@ ErrorMessage.play = CustomErrorMessage.play = async ({ canvasElement }) => { await waitFor(() => expect(canvas.getByRole('alert')).toBeInTheDocument()); }; -export const UnknownErrorMessage = UnknownSubmitErrorTemplate.bind({}); - +// export const UnknownErrorMessage = UnknownSubmitErrorTemplate.bind({}); +// // UnknownErrorMessage.play = async ({ canvasElement }) => { // const canvas = within(canvasElement); // const button = await canvas.getByRole('button'); diff --git a/src/components/forms/Form/types.ts b/src/components/forms/Form/types.ts index aed693f4..1c2189c3 100644 --- a/src/components/forms/Form/types.ts +++ b/src/components/forms/Form/types.ts @@ -8,7 +8,7 @@ export type CubeFieldData = { readonly name: Name; errors: ReactNode[]; value?: Value; - inputValue?: Value; + inputValue?: Value | string | undefined | null; touched?: boolean; rules?: any[]; validating?: boolean; diff --git a/src/components/forms/Form/use-field/use-field-props.tsx b/src/components/forms/Form/use-field/use-field-props.tsx index 5061d938..b8ec2868 100644 --- a/src/components/forms/Form/use-field/use-field-props.tsx +++ b/src/components/forms/Form/use-field/use-field-props.tsx @@ -77,7 +77,6 @@ export function useFieldProps< // eslint-disable-next-line react-hooks/rules-of-hooks const onBlurChained = useChainedCallback( field?.onBlur, - // TODO: remove type casting after updating to typescipt@4.9 'onBlur' in props ? (props as any).onBlur : undefined, ); diff --git a/src/components/forms/Form/use-field/use-field.ts b/src/components/forms/Form/use-field/use-field.ts index e0cc1794..4d13db04 100644 --- a/src/components/forms/Form/use-field/use-field.ts +++ b/src/components/forms/Form/use-field/use-field.ts @@ -172,11 +172,13 @@ export function useField>( } }); + let inputValue = field?.inputValue; + return useMemo( () => ({ id: fieldId, name: fieldName, - value: field?.inputValue, + value: inputValue, validateTrigger, form, field, diff --git a/src/components/forms/Slider/SliderBase.tsx b/src/components/forms/Slider/SliderBase.tsx index 5cebb9b3..20bf29fc 100644 --- a/src/components/forms/Slider/SliderBase.tsx +++ b/src/components/forms/Slider/SliderBase.tsx @@ -4,10 +4,11 @@ import { FocusableRef } from '@react-types/shared'; import { SliderState, useSliderState } from 'react-stately'; import { useSlider, useNumberFormatter } from 'react-aria'; -import { FieldWrapper } from '../FieldWrapper'; import { extractStyles, OUTER_STYLES, tasty } from '../../../tasty'; import { useFieldProps, useFormProps } from '../Form'; import { Text } from '../../content/Text'; +import { mergeProps } from '../../../utils/react'; +import { wrapWithField } from '../wrapper'; import { SliderControlsElement, SliderElement } from './elements'; import { CubeSliderBaseProps } from './types'; @@ -211,27 +212,17 @@ function SliderBase( styles = extractStyles(otherProps, OUTER_STYLES, styles); - return ( - + extra, + }, + { labelProps }, + ), ); } diff --git a/src/components/forms/wrapper.tsx b/src/components/forms/wrapper.tsx index bef1ced6..47527e2c 100644 --- a/src/components/forms/wrapper.tsx +++ b/src/components/forms/wrapper.tsx @@ -1,4 +1,5 @@ import { ReactElement, RefObject } from 'react'; +import { FocusableRef } from '@react-types/shared'; import { FieldBaseProps, FormBaseProps } from '../../shared'; import { BaseProps, Styles } from '../../tasty'; @@ -11,7 +12,7 @@ interface WrapWithFieldProps extends FieldBaseProps, BaseProps, FormBaseProps { export function wrapWithField( component: ReactElement, - ref: RefObject, + ref: RefObject | FocusableRef, props: T, ) { let { @@ -20,13 +21,14 @@ export function wrapWithField( labelPosition = 'top', labelStyles, isRequired, + isDisabled, necessityIndicator, - necessityLabel, - validationState, message, + messageStyles, description, - isDisabled, + validationState, labelProps, + fieldProps, requiredMark = true, tooltip, isHidden, @@ -38,23 +40,24 @@ export function wrapWithField( return ( ( qa, label, extra, - labelPosition = 'top', labelStyles, isRequired, necessityIndicator, @@ -174,7 +173,6 @@ export const ComboBox = forwardRef(function ComboBox( autoComplete = 'off', direction = 'bottom', shouldFlip = true, - requiredMark = true, menuTrigger = 'input', suffixPosition = 'before', loadingState, @@ -400,31 +398,14 @@ export const ComboBox = forwardRef(function ComboBox( ); - return ( - + return wrapWithField, 'children'>>( + comboBoxField, + ref, + mergeProps({ ...props, styles }, { labelProps }), ); }) as unknown as (( props: CubeComboBoxProps & { ref?: ForwardedRef }, -) => JSX.Element) & { Item: typeof Item }; +) => ReactElement) & { Item: typeof Item }; ComboBox.Item = Item; Object.defineProperty(ComboBox, 'cubeInputType', { diff --git a/src/components/pickers/Select/Select.tsx b/src/components/pickers/Select/Select.tsx index a7c82a51..2abbb9eb 100644 --- a/src/components/pickers/Select/Select.tsx +++ b/src/components/pickers/Select/Select.tsx @@ -20,8 +20,8 @@ import { useOverlayPosition, useSelect, } from 'react-aria'; -import { DOMRef } from '@react-types/shared'; import styled from 'styled-components'; +import { DOMRef } from '@react-types/shared'; import { useFieldProps, useFormProps } from '../../forms'; import { useProviderProps } from '../../../provider'; @@ -37,13 +37,13 @@ import { tasty, } from '../../../tasty'; import { useFocus } from '../../../utils/react/interactions'; -import { FieldWrapper } from '../../forms/FieldWrapper'; import { OverlayWrapper } from '../../overlays/OverlayWrapper'; import { FieldBaseProps } from '../../../shared'; import { getOverlayTransitionCSS } from '../../../utils/transitions'; import { mergeProps, useCombinedRefs } from '../../../utils/react'; import { DEFAULT_INPUT_STYLES, INPUT_WRAPPER_STYLES } from '../../forms'; import { DEFAULT_BUTTON_STYLES } from '../../actions'; +import { wrapWithField } from '../../forms/wrapper'; import { LoadingIcon } from '../../../icons'; import { InvalidIcon } from '../../shared/InvalidIcon'; import { ValidIcon } from '../../shared/ValidIcon'; @@ -296,7 +296,6 @@ function Select( label, extra, icon, - labelPosition = 'top', labelStyles, isRequired, necessityIndicator, @@ -320,7 +319,6 @@ function Select( description, direction = 'bottom', shouldFlip = true, - requiredMark = true, placeholder, tooltip, size, @@ -471,28 +469,16 @@ function Select( ); - return ( - , 'children'>>( + selectField, + ref, + mergeProps( + { + ...props, styles, - isRequired, - labelStyles, - necessityIndicator, - labelProps, - isDisabled, - validationState, - message, - description, - requiredMark, - tooltip, - labelSuffix, - Component: selectField, - ref: ref, - }} - /> + }, + { labelProps }, + ), ); } diff --git a/src/shared/form.ts b/src/shared/form.ts index 324306a5..e096e360 100644 --- a/src/shared/form.ts +++ b/src/shared/form.ts @@ -57,6 +57,8 @@ export interface FieldBaseProps extends FormBaseProps { labelStyles?: Styles; /** Whether the field is inside the form. Private field. */ insideForm?: boolean; + fieldProps?: Props; + messageStyles?: Styles; } export interface FormBaseProps {