Categories
Material UI

Material UI — Utility Components

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to add a click away listener, CSS resets, and portals with Material UI.

Click Away Listener

We can add a click away listener to listen to when we click outside an element.

This lets us add things like menus easily.

For example, we can write:

import React from "react";
import ClickAwayListener from "@material-ui/core/ClickAwayListener";

export default function App() {
  const [open, setOpen] = React.useState(false);

const handleClick = () => {
    setOpen(prev => !prev);
  };

const handleClickAway = () => {
    setOpen(false);
  };

return (
    <ClickAwayListener onClickAway={handleClickAway}>
      <div>
        <button type="button" onClick={handleClick}>
          menu
        </button>
        {open ? <div>some menu</div> : null}
      </div>
    </ClickAwayListener>
  );
}

to add a menu to our app.

We have the ClickAwayListener to our App .

Then we put the stuff that we want to do something with when the user click away from inside.

We have a div that will be removed when we click outside of it.

The onClickAway prop takes a function that runs when we click away from something.

The handleClick prop toggles the open prop to toggle the menu.

handleClickAway sets open to false to close the menu.

When we click the button the menu will open.

When we click outside it, it’ll close.

Portal

The Portal component is like the React portal component.

It lets us render the dropdown into a new location outside of the DOM hierarchy.

To use it, we can write:

import React from "react";
import ClickAwayListener from "@material-ui/core/ClickAwayListener";
import Portal from "@material-ui/core/Portal";

export default function App() {
  const [open, setOpen] = React.useState(false);

  const handleClick = () => {
    setOpen(prev => !prev);
  };

  const handleClickAway = () => {
    setOpen(false);
  };

  return (
    <ClickAwayListener onClickAway={handleClickAway}>
      <div>
        <button type="button" onClick={handleClick}>
          menu
        </button>
        {open ? (
          <Portal>
            <div>some menu.</div>
          </Portal>
        ) : null}
      </div>
    </ClickAwayListener>
  );
}

We added the Portal component, which will render the div inside in the body instead of inside the div inside the ClickAwayListener .

Leading Edge

We can configure the ClickAwayListener to listen to leading events like mouse down and touch start.

For example, we can write:

import React from "react";
import ClickAwayListener from "@material-ui/core/ClickAwayListener";
import Portal from "@material-ui/core/Portal";

export default function App() {
  const [open, setOpen] = React.useState(false);

  const handleClick = () => {
    setOpen(prev => !prev);
  };

  const handleClickAway = () => {
    setOpen(false);
  };

  return (
    <ClickAwayListener
      mouseEvent="onMouseDown"
      touchEvent="onTouchStart"
      onClickAway={handleClickAway}
    >
      <div>
        <button type="button" onClick={handleClick}>
          menu
        </button>
        {open ? <div>some menu.</div> : null}
      </div>
    </ClickAwayListener>
  );
}

We have a mouseEvent and touchEvent props to set the events that we listen to.

Therefore, we listen to the mouse down and touch start events.

CSS Baseline

The CssBaseline component provides CSS resets.

It provides style normalization provided by normalize.css.

It resets the CSS defaults to the same ones across every browser.

For instance, we can use it by writing:

import React from "react";
import CssBaseline from "@material-ui/core/CssBaseline";

export default function App() {
  return (
    <>
      <CssBaseline />
      reset
    </>
  );
}

The CssBaseline component provides a global CSS reset.

If we want to scope the CSS reset to some components, we can use the ScopedCssBaseline component.

For example, we can write:

import React from "react";
import ScopedCssBaseline from "@material-ui/core/ScopedCssBaseline";

export default function App() {
  return (
    <>
      <ScopedCssBaseline>reset</ScopedCssBaseline>
    </>
  );
}

We wrap the ScopedCssBaseline component around the elements that we want to reset the defaults for.

Conclusion

We can use the click away listener to listen for users clicking away from elements.

Material UI provides CSS reset components we can use to reset the defaults to make them consistent everywhere.

Portals let us render items anywhere in the DOM hierarchy.

Categories
Material UI

Material UI — Tooltips

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to add tooltips with Material UI.

Simple Tooltips

We can add simple tooltips that display when we hover over another component.

For example, we can add a floating action button with the Fab component.

Then we surround that with a Tooltip to display a tooltip.

For example, we can write:

import React from "react";
import AddIcon from "@material-ui/icons/Add";
import Fab from "@material-ui/core/Fab";
import DeleteIcon from "@material-ui/icons/Delete";
import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";

export default function App() {
  return (
    <div>
      <Tooltip title="Delete File">
        <IconButton>
          <DeleteIcon />
        </IconButton>
      </Tooltip>
      <Tooltip title="Add Task">
        <Fab color="primary">
          <AddIcon />
        </Fab>
      </Tooltip>
    </div>
  );
}

to add tooltips with the Tooltip component.

We can surround it around an IconButton or a Fab .

The title has th content that’ll be displayed in the tooltip.

Positioned Tooltips

We can put the tooltip in the position we want.

For example, we can write:

import React from "react";
import AddIcon from "@material-ui/icons/Add";
import Fab from "@material-ui/core/Fab";
import DeleteIcon from "@material-ui/icons/Delete";
import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";

export default function App() {
  return (
    <div>
      <Tooltip title="Delete File" placement="left-end">
        <IconButton>
          <DeleteIcon />
        </IconButton>
      </Tooltip>
      <Tooltip title="Add Task" placement="left-start">
        <Fab color="primary">
          <AddIcon />
        </Fab>
      </Tooltip>
    </div>
  );
}

to add a placement prop to each tooltip to change the placement.

Customized Tooltips

We can style our tooltips with the withStyles function.

For example, we can write:

import React from "react";
import { withStyles } from "@material-ui/core/styles";
import DeleteIcon from "@material-ui/icons/Delete";
import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";

const LightTooltip = withStyles(theme => ({
  tooltip: {
    backgroundColor: theme.palette.common.white,
    color: "green",
    boxShadow: theme.shadows[1],
    fontSize: 11
  }
}))(Tooltip);

export default function App() {
  return (
    <div>
      <LightTooltip title="Delete File" placement="left-end">
        <IconButton>
          <DeleteIcon />
        </IconButton>
      </LightTooltip>
    </div>
  );
}

We made the backgroundColor white and the content color green.

Also, we change the fontSize to 11.

We can also use the makeStyles function to create classes that we can pass into the tooltip.

For example, we can write:

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import DeleteIcon from "@material-ui/icons/Delete";
import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";

const useStyles = makeStyles(theme => ({
  arrow: {
    color: theme.palette.common.black
  },
  tooltip: {
    backgroundColor: theme.palette.common.black
  }
}));

function BlackTooltip(props) {
  const classes = useStyles();

  return <Tooltip arrow classes={classes} {...props} />;
}

export default function App() {
  return (
    <div>
      <BlackTooltip title="Delete File">
        <IconButton>
          <DeleteIcon />
        </IconButton>
      </BlackTooltip>
    </div>
  );
}

We created a BlackTooltip component that adds some classes to the Tooltip .

Now we have a black tooltip with a black arrow color as indicated in the arrow and tooltip properties.

Arrow Tooltips

To add an arrow to a tooltip, we can add the arrow prop.

For example, we can write;

import React from "react";
import DeleteIcon from "@material-ui/icons/Delete";
import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";

export default function App() {
  return (
    <div>
      <Tooltip title="Delete File" arrow>
        <IconButton>
          <DeleteIcon />
        </IconButton>
      </Tooltip>
    </div>
  );
}

We use the arrow prop on the Tooltip to add it.

Custom Child Element

To add a custom child element, we’ve to use the forwardRef and return our component inside the callback.

This way, the DOM event listeners for the element are applied.

For example, we can write:

import React from "react";
import DeleteIcon from "@material-ui/icons/Delete";
import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";

const Content = React.forwardRef((props, ref) => {
  return (
    <div {...props} ref={ref}>
      <IconButton>
        <DeleteIcon />
      </IconButton>
    </div>
  );
});

export default function App() {
  return (
    <div>
      <Tooltip title="Delete File" arrow>
        <Content />
      </Tooltip>
    </div>
  );
}

to create a Content component that we can put between the Tooltip tags.

We put our content inside the forwardRef callback so the tooltip will show properly.

Conclusion

We can create tooltips with various styles and positions.

The content can also be changed to show what we want.

The styling can be changed in various ways.

Categories
Material UI

Material UI — Tables

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to add tables with Material UI.

Dense Table

We can change the size prop of the table to small to make the table denser.

For example, we can write:

import React from "react";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableContainer from "@material-ui/core/TableContainer";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper";

function createData(firstName, lastName, age) {
  return { firstName, lastName, age };
}

const rows = [
  createData("james", "smith", 20),
  createData("mary", "jones", 30),
  createData("may", "wong", 30)
];

export default function App() {
  return (
    <TableContainer component={Paper}>
      <Table size="small">
        <TableHead>
          <TableRow>
            <TableCell>first name</TableCell>
            <TableCell>last name</TableCell>
            <TableCell align="right">age</TableCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {rows.map(row => (
            <TableRow key={row.firstName}>
              <TableCell component="th" scope="row">
                {row.firstName}
              </TableCell>
              <TableCell>{row.lastName}</TableCell>
              <TableCell align="right">{row.age}</TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  );
}

then we display the table rows in a denser way than the default.

Sorting and Selecting

We can add sorting with the TableSortLabel component.

For example, we can write:

import React from "react";
import PropTypes from "prop-types";
import clsx from "clsx";
import { lighten, makeStyles } from "@material-ui/core/styles";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableContainer from "@material-ui/core/TableContainer";
import TableHead from "@material-ui/core/TableHead";
import TablePagination from "@material-ui/core/TablePagination";
import TableRow from "@material-ui/core/TableRow";
import TableSortLabel from "@material-ui/core/TableSortLabel";
import Toolbar from "@material-ui/core/Toolbar";
import Typography from "@material-ui/core/Typography";
import Paper from "@material-ui/core/Paper";
import Checkbox from "@material-ui/core/Checkbox";
import IconButton from "@material-ui/core/IconButton";
import Tooltip from "@material-ui/core/Tooltip";

import DeleteIcon from "@material-ui/icons/Delete";
import FilterListIcon from "@material-ui/icons/FilterList";

function createData(name, age) {
  return { name, age };
}

const rows = [
  createData("james", 30),
  createData("mary", 45),
  createData("alex", 26)
];

function descendingComparator(a, b, orderBy) {
  if (b[orderBy] < a[orderBy]) {
    return -1;
  }
  if (b[orderBy] > a[orderBy]) {
    return 1;
  }
  return 0;
}

function getComparator(order, orderBy) {
  return order === "desc"
    ? (a, b) => descendingComparator(a, b, orderBy)
    : (a, b) => -descendingComparator(a, b, orderBy);
}

function stableSort(array, comparator) {
  const stabilizedThis = array.map((el, index) => [el, index]);
  stabilizedThis.sort((a, b) => {
    const order = comparator(a[0], b[0]);
    if (order !== 0) return order;
    return a[1] - b[1];
  });
  return stabilizedThis.map(el => el[0]);
}

const headCells = [
  {
    id: "name",
    label: "name"
  },
  { id: "age", numeric: true, label: "age" }
];

function EnhancedTableHead(props) {
  const {
    classes,
    onSelectAllClick,
    order,
    orderBy,
    numSelected,
    rowCount,
    onRequestSort
  } = props;
  const createSortHandler = property => event => {
    onRequestSort(event, property);
  };

  return (
    <TableHead>
      <TableRow>
        <TableCell padding="checkbox">
          <Checkbox
            indeterminate={numSelected > 0 && numSelected < rowCount}
            checked={rowCount > 0 && numSelected === rowCount}
            onChange={onSelectAllClick}
            inputProps={{ "aria-label": "select all desserts" }}
          />
        </TableCell>
        {headCells.map(headCell => (
          <TableCell
            key={headCell.id}
            align={headCell.numeric ? "right" : "left"}
            padding={headCell.disablePadding ? "none" : "default"}
            sortDirection={orderBy === headCell.id ? order : false}
          >
            <TableSortLabel
              active={orderBy === headCell.id}
              direction={orderBy === headCell.id ? order : "asc"}
              onClick={createSortHandler(headCell.id)}
            >
              {headCell.label}
              {orderBy === headCell.id ? (
                <span className={classes.visuallyHidden}>
                  {order === "desc" ? "sorted descending" : "sorted ascending"}
                </span>
              ) : null}
            </TableSortLabel>
          </TableCell>
        ))}
      </TableRow>
    </TableHead>
  );
}

EnhancedTableHead.propTypes = {
  classes: PropTypes.object.isRequired,
  numSelected: PropTypes.number.isRequired,
  onRequestSort: PropTypes.func.isRequired,
  onSelectAllClick: PropTypes.func.isRequired,
  order: PropTypes.oneOf(["asc", "desc"]).isRequired,
  orderBy: PropTypes.string.isRequired,
  rowCount: PropTypes.number.isRequired
};

const useToolbarStyles = makeStyles(theme => ({
  root: {
    paddingLeft: theme.spacing(2),
    paddingRight: theme.spacing(1)
  },
  highlight:
    theme.palette.type === "light"
      ? {
          color: theme.palette.secondary.main,
          backgroundColor: lighten(theme.palette.secondary.light, 0.85)
        }
      : {
          color: theme.palette.text.primary,
          backgroundColor: theme.palette.secondary.dark
        },
  title: {
    flex: "1 1 100%"
  }
}));

const EnhancedTableToolbar = props => {
  const classes = useToolbarStyles();
  const { numSelected } = props;

return (
    <Toolbar
      className={clsx(classes.root, {
        [classes.highlight]: numSelected > 0
      })}
    >
      {numSelected > 0 ? (
        <Typography
          className={classes.title}
          color="inherit"
          variant="subtitle1"
          component="div"
        >
          {numSelected} selected
        </Typography>
      ) : (
        <Typography
          className={classes.title}
          variant="h6"
          id="tableTitle"
          component="div"
        >
          Names
        </Typography>
      )}

{numSelected > 0 ? (
        <Tooltip title="Delete">
          <IconButton aria-label="delete">
            <DeleteIcon />
          </IconButton>
        </Tooltip>
      ) : (
        <Tooltip title="Filter list">
          <IconButton aria-label="filter list">
            <FilterListIcon />
          </IconButton>
        </Tooltip>
      )}
    </Toolbar>
  );
};

EnhancedTableToolbar.propTypes = {
  numSelected: PropTypes.number.isRequired
};

const useStyles = makeStyles(theme => ({
  root: {
    width: "100%"
  },
  paper: {
    width: "100%",
    marginBottom: theme.spacing(2)
  },
  table: {
    minWidth: 750
  },
  visuallyHidden: {
    border: 0,
    clip: "rect(0 0 0 0)",
    height: 1,
    margin: -1,
    overflow: "hidden",
    padding: 0,
    position: "absolute",
    top: 20,
    width: 1
  }
}));

export default function App() {
  const classes = useStyles();
  const [order, setOrder] = React.useState("asc");
  const [orderBy, setOrderBy] = React.useState("calories");
  const [selected, setSelected] = React.useState([]);
  const [page, setPage] = React.useState(0);
  const [rowsPerPage, setRowsPerPage] = React.useState(5);

  const handleRequestSort = (event, property) => {
    const isAsc = orderBy === property && order === "asc";
    setOrder(isAsc ? "desc" : "asc");
    setOrderBy(property);
  };

  const handleSelectAllClick = event => {
    if (event.target.checked) {
      const newSelecteds = rows.map(n => n.name);
      setSelected(newSelecteds);
      return;
    }
    setSelected([]);
  };

  const handleClick = (event, name) => {
    const selectedIndex = selected.indexOf(name);
    let newSelected = [];

    if (selectedIndex === -1) {
      newSelected = newSelected.concat(selected, name);
    } else if (selectedIndex === 0) {
      newSelected = newSelected.concat(selected.slice(1));
    } else if (selectedIndex === selected.length - 1) {
      newSelected = newSelected.concat(selected.slice(0, -1));
    } else if (selectedIndex > 0) {
      newSelected = newSelected.concat(
        selected.slice(0, selectedIndex),
        selected.slice(selectedIndex + 1)
      );
    }

    setSelected(newSelected);
  };

  const handleChangePage = (event, newPage) => {
    setPage(newPage);
  };

  const handleChangeRowsPerPage = event => {
    setRowsPerPage(parseInt(event.target.value, 10));
    setPage(0);
  };

  const isSelected = name => selected.indexOf(name) !== -1;

  const emptyRows =
    rowsPerPage - Math.min(rowsPerPage, rows.length - page * rowsPerPage);

  return (
    <div className={classes.root}>
      <Paper className={classes.paper}>
        <EnhancedTableToolbar numSelected={selected.length} />
        <TableContainer>
          <Table className={classes.table}>
            <EnhancedTableHead
              classes={classes}
              numSelected={selected.length}
              order={order}
              orderBy={orderBy}
              onSelectAllClick={handleSelectAllClick}
              onRequestSort={handleRequestSort}
              rowCount={rows.length}
            />
            <TableBody>
              {stableSort(rows, getComparator(order, orderBy))
                .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
                .map((row, index) => {
                  const isItemSelected = isSelected(row.name);

return (
                    <TableRow
                      hover
                      onClick={event => handleClick(event, row.name)}
                      role="checkbox"
                      aria-checked={isItemSelected}
                      tabIndex={-1}
                      key={row.name}
                      selected={isItemSelected}
                    >
                      <TableCell padding="checkbox">
                        <Checkbox checked={isItemSelected} />
                      </TableCell>
                      <TableCell component="th" scope="row" padding="none">
                        {row.name}
                      </TableCell>
                      <TableCell align="right">{row.age}</TableCell>
                    </TableRow>
                  );
                })}
              {emptyRows > 0 && (
                <TableRow>
                  <TableCell />
                </TableRow>
              )}
            </TableBody>
          </Table>
        </TableContainer>
        <TablePagination
          rowsPerPageOptions={[5, 10, 25]}
          component="div"
          count={rows.length}
          rowsPerPage={rowsPerPage}
          page={page}
          onChangePage={handleChangePage}
          onChangeRowsPerPage={handleChangeRowsPerPage}
        />
      </Paper>
    </div>
  );
}

to add a table with sorting and selection.

We started with the example from https://material-ui.com/components/tables/#sorting-amp-selecting.

We have the stableSort function to do the sorting.

And we have the checkbox on the row to show let us select items.

We also added a toolbar to the bottom to let us select the number of rows to display with the TablePagination component.

The EnhancedTableHead has the table heading with sorting.

We used the TableSortLabel to let us do the sorting.

Conclusion

We can add dense tables and tables with sorting with the built-in components.

Categories
Material UI

Material UI — Popovers

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to add popovers with Material UI.

Simple Popover

Popovers let us display some content over other things.

To add it, we can use the Popover component.

For example, we can write:

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import Popover from "@material-ui/core/Popover";
import Typography from "@material-ui/core/Typography";
import Button from "@material-ui/core/Button";

const useStyles = makeStyles(theme => ({
  typography: {
    padding: theme.spacing(3)
  }
}));

export default function App() {
  const classes = useStyles();
  const [anchorEl, setAnchorEl] = React.useState(null);

  const handleClick = event => {
    setAnchorEl(event.currentTarget);
  };

  const handleClose = () => {
    setAnchorEl(null);
  };

  const open = Boolean(anchorEl);

  return (
    <div>
      <Button variant="contained" color="primary" onClick={handleClick}>
        Open Popover
      </Button>
      <Popover
        open={open}
        anchorEl={anchorEl}
        onClose={handleClose}
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "center"
        }}
        transformOrigin={{
          vertical: "top",
          horizontal: "center"
        }}
      >
        <Typography className={classes.typography}>Popover content.</Typography>
      </Popover>
    </div>
  );
}

to add a button to open the popover and the popover itself.

The anchorEl prop has the anchor element of the popover, which is the button itself.

anchorOrigin specifies where relative to the button do we display the popover.

transformOrigin lets us change the position of the popover itself.

open sets whether the popover ios open or not.

If the anchorEl is set, then it’s open.

Otherwise, it’s not open.

onClose is a function that runs when the popover closes.

Mouse Over Interaction

We can open a popover when we move our mouse over an element.

For example, we can write:

import React from "react";
import Popover from "@material-ui/core/Popover";
import Typography from "@material-ui/core/Typography";
import { makeStyles } from "@material-ui/core/styles";

const useStyles = makeStyles(theme => ({
  popover: {
    pointerEvents: "none"
  },
  paper: {
    padding: theme.spacing(3)
  }
}));

export default function App() {
  const classes = useStyles();
  const [anchorEl, setAnchorEl] = React.useState(null);

  const handlePopoverOpen = event => {
    setAnchorEl(event.currentTarget);
  };

  const handlePopoverClose = () => {
    setAnchorEl(null);
  };

  const open = Boolean(anchorEl);

  return (
    <div>
      <Typography
        onMouseEnter={handlePopoverOpen}
        onMouseLeave={handlePopoverClose}
      >
        Hover me
      </Typography>
      <Popover
        className={classes.popover}
        classes={{
          paper: classes.paper
        }}
        open={open}
        anchorEl={anchorEl}
        anchorOrigin={{
          vertical: "bottom",
          horizontal: "left"
        }}
        transformOrigin={{
          vertical: "top",
          horizontal: "left"
        }}
        onClose={handlePopoverClose}
        disableRestoreFocus
      >
        <Typography>some popover.</Typography>
      </Popover>
    </div>
  );
}

We pass in functions to the onMouseEnter prop to let us open the popover when we hover over the text.

Similarly, we pass in a function to the onMouseLeave prop to close the modal when our mouse leaves the element respectively.

The other parts are the same as the previous example.

PopupState Helper

To make managing the popover state easier, we can use the material-ui-popup-state library to manage the state.

To install it, we run:

npm install --save material-ui-popup-state

Then we can write:

import React from "react";
import Typography from "@material-ui/core/Typography";
import Box from "@material-ui/core/Box";
import Button from "@material-ui/core/Button";
import Popover from "@material-ui/core/Popover";
import PopupState, { bindTrigger, bindPopover } from "material-ui-popup-state";

export default function App() {
  return (
    <PopupState variant="popover">
      {popupState => (
        <div>
          <Button
            variant="contained"
            color="primary"
            {...bindTrigger(popupState)}
          >
            Open Popover
          </Button>
          <Popover
            {...bindPopover(popupState)}
            anchorOrigin={{
              vertical: "bottom",
              horizontal: "center"
            }}
            transformOrigin={{
              vertical: "top",
              horizontal: "center"
            }}
          >
            <Box p={3}>
              <Typography>some popover.</Typography>
            </Box>
          </Popover>
        </div>
      )}
    </PopupState>
  );
}

We have the PopupState coponent wirh the variant set to popover to display a popover.

Then inside it, we have a function that takes the popupState parameter, which has the open state of the popover.

We use the bindTrigger with it to return an object to toggle the popup in the button.

In the popover, we call bindPopover to bind the open prop to the popupState .

The rest of the code is the same as previous examples.

Conclusion

We can add popovers with the Popover component.

The material-ui-popup-state library can make managing the popover state easier.

Categories
Material UI

Material UI — More Tooltip Tricks and Typography

Material UI is a Material Design library made for React.

It’s a set of React components that have Material Design styles.

In this article, we’ll look at how to customize tooltips and add typography with Material UI.

Disabled Elements

If we want to show a tooltip on a disabled element, then we’ve to wrap the disabled element with a wrapper element.

For example, we can write:

import React from "react";
import Button from "@material-ui/core/Button";
import Tooltip from "@material-ui/core/Tooltip";

export default function App() {
  return (
    <Tooltip title="disabled">
      <span>
        <Button disabled>Disabled Button</Button>
      </span>
    </Tooltip>
  );
}

We wrap the disabled button with a span so that we can see the tooltip when we hover over it.

Transitions

We can show various transitions when we show our tooltip.

For example, we can write:

import React from "react";
import Button from "@material-ui/core/Button";
import Tooltip from "@material-ui/core/Tooltip";
import Fade from "@material-ui/core/Fade";

export default function App() {
  return (
    <Tooltip
      TransitionComponent={Fade}
      TransitionProps={{ timeout: 600 }}
      title="tooltip"
    >
      <Button>Fade</Button>
    </Tooltip>
  );
}

to add a fade transition when we show or hide the tooltip.

Other effects include the zoom effect:

import React from "react";
import Button from "@material-ui/core/Button";
import Tooltip from "@material-ui/core/Tooltip";
import Zoom from "@material-ui/core/Zoom";

export default function App() {
  return (
    <Tooltip
      TransitionComponent={Zoom}
      TransitionProps={{ timeout: 600 }}
      title="tooltip"
    >
      <Button>Fade</Button>
    </Tooltip>
  );
}

We set the TransitionProps to change the duration of the effect.

Showing and Hiding

We can also add a delay for showing and hiding the tooltip.

For example, we can write:

import React from "react";
import Button from "@material-ui/core/Button";
import Tooltip from "@material-ui/core/Tooltip";

export default function App() {
  return (
    <Tooltip title="tooltip" enterDelay={500} leaveDelay={200}>
      <Button>button</Button>
    </Tooltip>
  );
}

We add a delay when we show the tooltip and when we hide it.

The numbers are in milliseconds.

Typography

We can change the font of our app with the Typography component.

The Roboto font isn’t automatically by Material UI.

To load it, we can add the following CSS:

<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />

We can also install the fontsource-roboto package:

npm install fontsource-roboto

and write:

import 'fontsource-roboto';

Typography Component

We can add the Typography component to render various text styles.

For example, we can write:

import React from "react";
import Typography from "@material-ui/core/Typography";

export default function App() {
  return (
    <>
      <Typography variant="h1" component="h2" gutterBottom>
        h1.
      </Typography>
      <Typography variant="h2" gutterBottom>
        h2.
      </Typography>
      <Typography variant="h3" gutterBottom>
        h3.
      </Typography>
      <Typography variant="h4" gutterBottom>
        h4.
      </Typography>
      <Typography variant="h5" gutterBottom>
        h5.
      </Typography>
      <Typography variant="h6" gutterBottom>
        h6.
      </Typography>
    </>
  );
}

to render various headings with it.

The element we render is set in the variant prop.

gutterBottom adds some margins to the bottom.

There are also various body text styles we can render:

import React from "react";
import Typography from "@material-ui/core/Typography";

export default function App() {
  return (
    <>
      <Typography variant="subtitle1" gutterBottom>
        Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quos
        blanditiis tenetur
      </Typography>
      <Typography variant="subtitle2" gutterBottom>
        Morbi maximus maximus nisl, in tristique augue convallis vel. Praesent
        eget scelerisque ex, a sagittis libero.
      </Typography>
      <Typography variant="body1" gutterBottom>
        Vivamus ultrices nunc a est pulvinar sodales.
      </Typography>
      <Typography variant="body2" gutterBottom>
        Proin tincidunt nunc sed orci suscipit varius. Suspendisse volutpat
        interdum iaculis.
      </Typography>
    </>
  );
}

We set the variant to subtitle1 , subtitle2 , body1 or body2 to display various body text styles.

There’re also styles for buttons, captions, and overline text:

import React from "react";
import Typography from "@material-ui/core/Typography";

export default function App() {
  return (
    <>
      <Typography variant="button" display="block" gutterBottom>
        button text
      </Typography>
      <Typography variant="caption" display="block" gutterBottom>
        caption text
      </Typography>
      <Typography variant="overline" display="block" gutterBottom>
        overline text
      </Typography>
    </>
  );
}

Theme

We can use the typography key of a theme to style our text.

For example, we can write:

import React from "react";
import { makeStyles } from "@material-ui/core/styles";

const useStyles = makeStyles(theme => ({
  root: {
    ...theme.typography.button,
    backgroundColor: theme.palette.background.paper,
    padding: theme.spacing(1)
  }
}));

export default function App() {
  const classes = useStyles();

  return <div className={classes.root}>some div</div>;
}

We added the theme.typography.button style to our styles so that we can apply it anywhere.

Conclusion

We can tooltips anywhere.

Typography components can be added to display text.