Repository navigation
v0.0.19
This release contains breaking changes.
- Inputs are now declaritive instead of imperative.
- Removed the
serializeSettingsanddeserializeSettingsmethods - settings are now required to use JSON - Added helper hooks to support the new declaritive pattern
- The
validateSettingsinput method no longer requires deserializing settings - The Key Value Input now uses an array for the value (
[string, string][]), simplifying logic and fixing edge cases. TherenderHtmlmethod still returns a key-value object. While the Input will accept both data formats, supplying a key-value object is deprecated in favour of the array format and will be removed in a future release. - If a server-side error occurs, this is now returned in the response in development environments
Required Changes (Example)
interface TextInputSettings {
maxLength: number
required: boolean
}
const input: Input<string, TextInputSettings> = {
id: 'text',
name: 'Text',
serialize: (data) => data,
deserialize: (data) => data,
- renderInput: () => forwardRef((props, ref) => {
- const { maxLength, required } = props.settings;
- const field = useField({
- mode: 'uncontrolled',
- initialValue: props.value || '',
- validateOnChange: true,
- validate: (value) => {
- if (required && !value) return `${props.name} is required`;
- if (maxLength && maxLength !== 0 && value.length > maxLength) {
- return `${props.name} has a maximum length of ${maxLength}`;
- }
- return null;
- },
- onValueChange: props.notifyChanges
- });
-
- useImperativeHandle(ref, () => ({
- getValues: () => field.getValue(),
- hasError: async () => !!(await field.validate())
- }));
+ renderInput: ({
+ name, description, settings, value, onChange, registerValidator, unregisterValidator
+ }) => {
+ const { maxLength, required } = settings;
+ const error = useInputValidator((v) => {
+ if (required && !v) return `${name} is required`;
+ if (maxLength && maxLength !== 0 && v.length > maxLength) {
+ return `${name} has a maximum length of ${maxLength}`;
+ }
+ return null;
+ }, registerValidator, unregisterValidator);
return (
- <TextInput label={props.name} description={props.description} required={required}
- maxLength={maxLength > 1 ? maxLength : null} {...field.getInputProps()} />
+ <TextInput
+ label={name}
+ description={description}
+ required={required}
+ maxLength={maxLength > 1 ? maxLength : null}
+ error={error}
+ value={value}
+ onChange={(v) => onChange(v.target.value)} />
);
- }),
-
- serializeSettings: (data) => JSON.stringify(data),
- deserializeSettings: (data) => JSON.parse(data),
-
- renderSettings: () => forwardRef((props, ref) => {
- const form = useForm({
- mode: 'uncontrolled',
- initialValues: {
- maxLength: props.settings?.maxLength || 0,
- required: props.settings?.required ?? true
- },
- validateInputOnChange: true,
- validate: (values) => ({
- maxLength: values.maxLength < 0 ? 'Max length must be positive' : null
- }),
- });
+ },
- useImperativeHandle(ref, () => ({
- getValues: () => form.getValues(),
- hasError: () => form.validate().hasErrors
- }));
+ renderSettings: ({ settings, onChange, registerValidator, unregisterValidator }) => {
+ const errors = useInputValidator<TextInputSettings>(
+ (v) => ({
+ maxLength: v.maxLength < 0 ? 'Max length must be positive' : null
+ }),
+ registerValidator,
+ unregisterValidator
+ );
- useEffect(() => {
- form.validate();
- }, []);
+ const [merged, changeSetting] = useSettingsHandler({
+ maxLength: settings?.maxLength || 0,
+ required: settings?.required ?? true
+ }, settings, onChange);
return (
<Stack>
<NumberInput label="Max Length" allowDecimal={false}
description="Set to 0 to disable length limit" required
- {...form.getInputProps('maxLength')} key={form.key('maxLength')} />
- <Checkbox label="Required" {...form.getInputProps('required', { type: 'checkbox' })}
- key={form.key('required')} />
+ error={errors?.maxLength} value={merged.maxLength}
+ onChange={(v: number) => changeSetting('maxLength', v)} />
+ <Checkbox label="Required" error={errors?.required}
+ checked={merged.required}
+ onChange={(v) => changeSetting('required', v.target.checked)} />
</Stack>
);
- }),
+ },
validate: (stringifiedValue, deserialize, settings) => {
if (settings.required && !stringifiedValue) {
throw new ExpressError('Required Text Input does not have a value');
}
const value = deserialize(stringifiedValue);
if (settings.maxLength > 0 && value.length > settings.maxLength) {
throw new ExpressError('Text Input value is longer than allowed');
}
},
- validateSettings: (serializedSettings, deserialize) => {
- if (!serializedSettings) {
+ validateSettings: (settings) => {
+ if (!settings) {
throw new ExpressError('Settings are required');
}
- const settings = deserialize(serializedSettings);
-
if (typeof settings.maxLength !== 'number') {
throw new ExpressError('Max Length must be a number');
}
if (settings.maxLength < 0) {
throw new ExpressError('Max Length cannot be negative');
}
if (typeof settings.required !== 'boolean') {
throw new ExpressError('Required must be a boolean');
}
},
renderHtml: (value) => value
};
export default input;