teslamotors / informed

A lightweight framework and utility for building powerful forms in React applications
https://teslamotors.github.io/informed
MIT License
956 stars 173 forks source link

Guidance needed for customizing Radio #419

Closed akshare closed 2 years ago

akshare commented 2 years ago

Hi,

I need to change the position of label in Radio, but wasn't sure how to go on about it.

I've read custom inputs doc, but since RadioGroup and Radio seem to work together, I'm looking for some guidance on how to do this.

I tried custom input for just Radio, but that didn't seem to work.

joepuzzo commented 2 years ago

Hi sorry just seeing this. I would suggest you look at the source code here https://github.com/teslamotors/informed/tree/master/src/components/form-fields

specifically

https://github.com/teslamotors/informed/blob/master/src/components/form-fields/Radio.js

and

https://github.com/teslamotors/informed/blob/master/src/components/form-fields/RadioGroup.js

AmirL commented 2 years ago

It works with useRadioGroup

const { radioGroupApi, radioGroupState } = useRadioGroup();

How can I use it? It's not exported.

joepuzzo commented 2 years ago

I wrote that myself just for my inputs. Its not doing anything fancy under hood. Just take a look at souse code and do same thing I did :)

akshare commented 2 years ago

I created an example sandbox for how I understood the usage to be.

Basically, the key being, customizing Radio requires creating and managing your own Context (createContext, useContext).

Hopefully it helps someone.

https://codesandbox.io/s/snowy-monad-mewji5?file=/src/App.js

akshare commented 2 years ago
import React, {createContext, useContext} from "react";
import {Form, useField } from "informed";

const App = () => {
  return (
    <Form>
      <IRadioGroup name="radiogroup" label="Number Selection" initialValue="one">
        <IRadio value="one" label="One" id="one" />
        <IRadio value="two" label="Two" id="two" />
        <IRadio value="three" label="Three" id="three" />
      </IRadioGroup>
    </Form>
  );
}

export default App;

//create Context
const RadioGroupContext = createContext();

//custom Radio Group
const IRadioGroup = props => {
  const { fieldApi, fieldState, userProps } = useField(props);

  const groupContext = {
    radioGroupApi: fieldApi,
    radioGroupState: fieldState,
    ...props
  };

  const { label, id, options, children } = userProps;
  const { showError, error } = fieldState;

  return (
    <RadioGroupContext.Provider value={groupContext}>
      <fieldset aria-describedby={`${id}-error`}>
        {label ? <legend>{label}</legend> : null}
        {children}
        {showError ? (
          <small role="alert" id={`${id}-error`}>
            {error}
          </small>
        ) : null}
      </fieldset>
    </RadioGroupContext.Provider>
  );
}

//custom Radio
const IRadio = ({ label, value, ...props }) => {

  const { radioGroupApi, radioGroupState } = useContext(RadioGroupContext);

  const { setValue, setTouched } = radioGroupApi;

  const { value: groupValue, showError } = radioGroupState;

  return (
    <>
      <input
        {...props}
        aria-invalid={!!showError}
        value={value}
        checked={groupValue === value}
        onChange={e => {
          if (!e.target.checked) {
            return;
          }
          setValue(value, e);
        }}
        onBlur={e => {
          setTouched(true, e);
        }}
        type="radio"
        id={value}
      />
      <label htmlFor={value}>{label}</label>
    </>
  );
};