Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions packages/mui-material/src/Fab/Fab.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,31 +103,34 @@ const FabRoot = styled(ButtonBase, {
props: { variant: 'extended' },
style: {
borderRadius: 48 / 2,
padding: '0 16px',
// Vertical padding and a content-sized height rather than a fixed
// height, so a label that wraps stays inside the button. `fit-content`
// rather than `auto` keeps a flex or grid parent from stretching it.
padding: '8px 16px',
width: 'auto',
minHeight: 'auto',
height: 'fit-content',
minWidth: 48,
height: 48,
minHeight: 48,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Breaking change: a smaller height customization no longer applies.

On master the extended variant set minHeight: 'auto', so a smaller height from sx, styleOverrides.extended or a theme variant took effect. Now this minHeight wins, and so do the 34 and 40 below for the small and medium sizes:

  • <Fab variant="extended" sx={{ height: 30 }}>: 30 → 48
  • <Fab variant="extended" size="small" sx={{ height: 28 }}>: 28 → 34

Custom sizes added through module augmentation are hit hardest. No built-in variant matches them, so they inherit minHeight: 48 and padding: '8px 16px'. A custom xsmall size with height: 28 renders at 48.

Users can fix it by also setting minHeight, but nothing warns them, so their layouts just change. I don't see a CSS approach that grows with the content and still keeps a smaller explicit height. So I think this has to be called out as a breaking change, with a changelog entry and a migration note, or held for the next major. Please also update the PR description, which currently says nothing moves.

},
},
{
props: { variant: 'extended', size: 'small' },
style: {
width: 'auto',
padding: '0 8px',
padding: '2px 8px',
borderRadius: 34 / 2,
minWidth: 34,
height: 34,
minHeight: 34,
},
},
{
props: { variant: 'extended', size: 'medium' },
style: {
width: 'auto',
padding: '0 16px',
padding: '4px 16px',
borderRadius: 40 / 2,
minWidth: 40,
height: 40,
minHeight: 40,
},
},
{
Expand Down
53 changes: 53 additions & 0 deletions packages/mui-material/src/Fab/Fab.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,59 @@ describe('<Fab />', () => {
expect(button).to.have.class(classes.sizeMedium);
});

// JSDOM does no layout, so the one-line heights are only measurable in a real
// browser. The content is 1.75em tall whatever the font family, so these are
// the minimums rather than anything font-dependent.
it.skipIf(isJsdom())('keeps the one-line height of each extended size', () => {
render(
<React.Fragment>
<Fab variant="extended" data-testid="large">
Save
</Fab>
<Fab variant="extended" size="medium" data-testid="medium">
Save
</Fab>
<Fab variant="extended" size="small" data-testid="small">
Save
</Fab>
</React.Fragment>,
);

expect(screen.getByTestId('large').getBoundingClientRect().height).to.equal(48);
expect(screen.getByTestId('medium').getBoundingClientRect().height).to.equal(40);
expect(screen.getByTestId('small').getBoundingClientRect().height).to.equal(34);
});

it.skipIf(isJsdom())('does not stretch to fill a flex parent', () => {
render(
<div style={{ display: 'flex', height: 160 }}>
<Fab variant="extended">Save</Fab>
</div>,
);

expect(screen.getByRole('button').getBoundingClientRect().height).to.equal(48);
});

// JSDOM does no layout, so the overflow this guards against is only visible
// in a real browser.
it.skipIf(isJsdom())('keeps a wrapping label inside the extended button', () => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only covers the large size, and no test checks that a one-line label keeps its height. Could you add:

  • a check that a one-line label measures 48, 40 and 34px for the large, medium and small sizes
  • a check that a flex parent doesn't stretch the Fab, so the regression flagged in Fab.js can't come back. This one fails on the current branch, which measures 100px:
it.skipIf(isJsdom())('is not stretched by a flex parent', () => {
  render(
    <div style={{ display: 'flex', height: 100 }}>
      <Fab variant="extended">Add</Fab>
    </div>,
  );
  expect(screen.getByRole('button').getBoundingClientRect().height).to.equal(48);
});

render(
<div style={{ width: 120 }}>
<Fab variant="extended">
<span data-testid="label">Create new deployment</span>
</Fab>
</div>,
);

const fab = screen.getByRole('button').getBoundingClientRect();
const label = screen.getByTestId('label').getBoundingClientRect();

// The button grew past its one-line height, so the label wrapped.
expect(fab.height).to.be.greaterThan(48);
expect(label.top).to.be.at.least(fab.top);
expect(label.bottom).to.be.at.most(fab.bottom);
});

it('should have a ripple', async () => {
const { container } = render(<Fab>Fab</Fab>);
await ripple.startTouch(screen.getByRole('button'));
Expand Down
Loading