vazco / uniforms

A React library for building forms from any schema.

Home Page:https://uniforms.tools

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Pass in parameters to custom field when applying component to AutoField.

emmett08 opened this issue · comments

Firstly, thank you to make uniforms open source. I'm trying to create a generic checkbox list to show in AutoForm (below). The function takes parameters so I can reuse it. I'm missing something obvious with UnknownObject -> Record so not sure if this is a feature request or usage issue? (Nor how to style custom components so that the background changes colour like OOTB autofields do).

export function CheckboxList({ checkboxes }: CheckboxListProps) {
  return (
    <FormControl component="fieldset">
      <FormGroup>
        {checkboxes.map((checkbox) => (
          <FormControlLabel
            key={checkbox.key}
            control={
              <Checkbox
                checked={checkbox.checked}
                onChange={checkbox.onChange}
              />
            }
            label={checkbox.label}
          />
        ))}
      </FormGroup>
    </FormControl>
  );
}

const default Options = connectField<CheckboxListProps, unknown>(CheckboxList);

<AutoField name="environments" field="environments" component={Options} with={checkboxes} />

Hi @emmett08. It looks like you are creating a custom component. In that case, you probably want to wire it up with the form's model. Here's my first question - what is the checkboxes in your example? Or rather, how would you like to use it?

Second thing is the AutoField usage: are the field and with props specific application? These are not used in uniforms.

And lastly, remember that all fields rendered by uniforms have to be a part of the schema, and if you'd like to render some fields dynamically (e.g., make the number of fields depend on other fields), it has to be reflected in the schema too.

Hi @radekmie - field was a github copilot addition which I didn't check. with is what I'd like to have to pass an array to the component but not needed since I haven't wired the component to the forms model (I thought that was the function of connectField e.g. connectField<CheckboxListProps, unknown>(CheckboxList);).

How do I pass in the choices array to the component when registering it in an AutoForm please?

const choices = [
  {key: 'aws', label: 'AWS', checked: false, onChange: (event: React.ChangeEvent<HTMLInputElement>) => {console.log(event.target.checked)}},
  {key: 'gcp', label: 'GCP', checked: false, onChange: (event: React.ChangeEvent<HTMLInputElement>) => {console.log(event.target.checked)}}
];

// custom uniforms component
// <CheckboxList checkboxes={choices} />

This is where I use the component and connectField:

export type VaultProps = HTMLFieldProps<string, HTMLDivElement> &
  CheckboxListProps;

export const sourceApplicationLocations: CheckboxListProps = {
  checkboxes: [
    {
      key: "aws",
      label: "AWS",
      checked: false,
      onChange: (event: React.ChangeEvent<HTMLInputElement>) => {
        console.log(event.target.checked);
      },
    },
    {
      key: "gcp",
      label: "GCP",
      checked: false,
      onChange: (event: React.ChangeEvent<HTMLInputElement>) => {
        console.log(event.target.checked);
      },
    },
  ],
};

const XOptions = connectField<CheckboxListProps, unknown>(CheckboxList);
export default XOptions;

In case this helps anyone else...

import { Autocomplete, Box, Paper, TextField } from "@mui/material";
import { connectField, HTMLFieldProps } from "uniforms";

export type EnvironmentProps = HTMLFieldProps<string, HTMLDivElement> & {
  environments: string[];
};

function Environment({ value, label, environments }:EnvironmentProps) {

  const CustomPaper = (props: any) => {
    return <Paper elevation={8} {...props} />;
  };
  
  return (
    <Box sx={{ mt: 1, mb: 1 }}>
      <Autocomplete
        multiple
        id="selected-environments-outlined"
        // onChange={onSelectedValuesChange}
        options={environments.map((option) => option)}
        getOptionLabel={(option) => option} 
        isOptionEqualToValue={(a, b) => a === b}
        PaperComponent={CustomPaper}
        filterSelectedOptions
        renderInput={(params) => (
          <TextField
            {...params}
            variant="outlined"
            label={label} // TODO: add props. This has no context
            placeholder={value} // TODO: add props. This has no context
            // onChange={handleOnChange}
          />
        )}
      />
    </Box>
  );
}

export default connectField(Environment);


Usage:

export type EnvironmentsData = {
    globalPlatform: string[];
};

const schema: JSONSchemaType<EnvironmentsData> = {
    title: 'Environments Required',
    type: 'object',
    properties: {
        globalPlatform: {
            type: 'array',
            minItems: 0,
            items: {
              type: 'string'
            },
            uniforms: { component: EnvironmentField, value: "Desired Platforms", label: 'Global', environments: ['Dev', 'Stage', 'Prod', 'Mgmt'] }
        },
    },
    required: [],
};

I'm glad you figured it out! Just to make it clear: what you proposed in this comment could work, but is not in-align with how uniforms are meant to be used. The onChange should not be passed to any field, as a field is "just" a part of the form, not a standalone input.

And as for this code - you could simplify it a little. The Environment components has no need to be wrapped in connectField and it'd be enough to forward all of the props from it to the TextField inside. If you'd need to read a value or other prop, you can always use useField to do that.

I'll close this one, but feel free to comment further.