6
votes

I'm using the radio button components available from material-ui.com and have set labelPlacement="start". This has placed the label on the left side, but I would also like the labels to be left aligned while leaving the radio buttons on the right.

<RadioGroup
    name="switching"
    value="switching"
    onChange={this.handleEstablishingChange.bind(this)}
>
    <FormControlLabel value="switching" control={<Radio />} labelPlacement="start" label={this.props.lang().justswitching} />
    <hr />
    <FormControlLabel value="new_source" control={<Radio />} labelPlacement="start" label={this.props.lang().newsource} />
</RadioGroup>

enter image description here

2
Some code demonstrating the issue would be nice.Thomas Hennes
I've edited the question to include the code. I hope you will consider removing any downvotes as this is a serious well-specified question. Thanks!Ben McCann
How are you styling your material components? Are you using JSS as suggested by Material-UI's own docs? Component CSS overrides? I see absolutely no styling attempt in your code, only the use of component props. Please remember that in order to help you, fellow SO users need to be able to reproduce your issue, and frankly I'm surprised someone with your experience would need to be reminded that.Thomas Hennes
I'm also disputing the notion that your question is well-specified. You say you want the labels to be left-aligned, but do you need the radio buttons to remain right-aligned? Do you want the whole label+radio groups to be aligned left, center or right? Or do you want the labels all the way to the left and the radio buttons all the way to the right?Thomas Hennes
I need the labels to be left aligned and the radio buttons to be right aligned. I.e. the labels all the way to the left and the radio buttons all the way to the right. I've tried tons of different things. I'm not sure it's useful for me to include all of the things that didn't work. I will accept any working solution whether it's JSS, CSS, or any other implementation.Ben McCann

2 Answers

4
votes

Here is a simple & straightforward solution to your problem using CSS override on the FormControlLabel component (which encapsulates both the label and the actual control).

We use Material-UI's makeStyles helper to define the class we'll use to override the default styling of FormControlLabel. We specifically want to target the root key (the full list of available CSS override keys for FormControlLabel is available here), hence we name our class root to benefit from destructuring and assignment simplification.

We assign the classes object returned from the useStyles hook call to the classes prop of each FormControlLabel. The long-form of that assignment would be classes={{ root: classes.root }} but because we named our class root (which is the name of the key we're targeting) we can simply write classes={classes}.

import React, { useState } from "react";
import ReactDOM from "react-dom";
import { makeStyles } from "@material-ui/core/styles";
import { RadioGroup, FormControlLabel, Radio } from "@material-ui/core";

const useStyles = makeStyles({
  root: {
    // component default is "inline-flex", using "flex" makes the
    // label + control group use the entire width of the parent element
    display: "flex",
    // component default is "flex-start", using "space-between" pushes
    // both flexed content to the right and left edges of the flexbox
    // Note: the content is aligned to the right by default because
    // the 'labelPlacement="start"' component prop changes the flexbox
    // direction to "row-reverse"
    justifyContent: "space-between",
  },
});

const App = () => {
  const [source, setSource] = useState("switching");
  const classes = useStyles();
  return (
    <div>
      <RadioGroup
        name="source"
        value={source}
        onChange={e => setSource(e.target.value)}
      >
        <FormControlLabel
          value="switching"
          control={<Radio />}
          labelPlacement="start"
          label={"Switching"}
          classes={classes}
        />
        <hr />
        <FormControlLabel
          value="new_source"
          control={<Radio />}
          labelPlacement="start"
          label={"New Service"}
          classes={classes}
        />
      </RadioGroup>
    </div>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Working codesandbox demo

Update: additional content about hooks

Say you have the following code (which will fail):

import React from "react"
import { RadioGroup, FormControlLabel, Radio } from "@material-ui/core"
import { makeStyles } from "@material-ui/core/styles"

const useStyles = makeStyles({
  root: {
    display: "flex",
    justifyContent: "space-between",
  },
})

class ComponentGenerator extends React.Component {

  // ... stuff here ...

  render() {
    const classes = useStyles() // <-- NO! Not a functional component & not
                                // top-level, hooks cannot be used here
    return (
      <RadioGroup
        name="source"
        value={source}
        onChange={this.handleEstablishingChange.bind(this)}
      >
        <FormControlLabel
          value="switching"
          control={<Radio />}
          labelPlacement="start"
          label={"Switching"}
          classes={classes}
        />
        <hr />
        <FormControlLabel
          value="new_source"
          control={<Radio />}
          labelPlacement="start"
          label={"New Service"}
          classes={classes}
        />
      </RadioGroup>
    )
  }
}

A solution is to externalize the inner component that uses hooks:

src/components/UI/MyRadio.js

import { FormControlLabel, Radio } from "@material-ui/core"
import { makeStyles } from "@material-ui/core/styles"

const useStyles = makeStyles({
  root: {
    display: "flex",
    justifyContent: "space-between",
  },
})

const MyRadio = ({ value, label }) => {

  const classes = useStyles() // <-- YES! Functional component & top-level

  return (
    <FormControlLabel
      value={value}
      control={<Radio />}
      labelPlacement="start"
      label={label}
      classes={classes}
    />
  )
}

export default MyRadio

and in your component generator:

src/components/ComponentGenerator.js

import React from "react"
import { RadioGroup } from "@material-ui/core"
import MyRadio from "./UI/MyRadio"

class ComponentGenerator extends React.Component {

  // ... stuff here ...

  render() {
    return (
      <RadioGroup
        name="source"
        value={source}
        onChange={this.handleEstablishingChange.bind(this)}
      >
        <MyRadio
          value="switching"
          label={"Switching"}
        />
        <hr />
        <MyRadio
          value="new_source"
          label={"New Service"}
        />
      </RadioGroup>
    )
  }
}
3
votes

Problem

After looking at the css material-UI adds on passing start in labelPlacement prop, it gives property

flex-direction: row-reverse;

so it is expected that the element starts from the end (horizontally).


Solution

The workaround solution of it is, add a property

justifyContent: 'space-between';

to the labelPlacementStart class.

     <FormControlLabel
        value="new_source"
        control={<Radio />}
        labelPlacement="start"
        label={"World"}
        classes={{
          labelPlacementStart: classes.labelPlacementStart
        }}
      />

    const STYLES = theme => createStyles({
      labelPlacementStart: {
         justifyContent: 'space-between'
      }
    })

enter image description here

Look at this codesandbox