-
-
Notifications
You must be signed in to change notification settings - Fork 32.6k
[material-next][ButtonGroup] Add ButtonGroup component with Material You design #39699
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from 32 commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
8f12caf
copy MD2 ButtonGroup component
lhilgert9 91ba67b
fix ButtonGroup capitalize import
lhilgert9 fb3d6fc
fix ButtonGroup generateUtilityClass import
lhilgert9 ce4ab8b
rearrange useUtilityClasses in ButtonGroup
lhilgert9 6f239e7
migrate index to TS
lhilgert9 0e515c5
fix ButtonGroup.d.ts OverridableComponent import
lhilgert9 26ef58b
migrate component types to .types
lhilgert9 9725b50
migrate component file to .tsx
lhilgert9 822b0c2
fix ButtonGroup.types Theme import
lhilgert9 796dc2b
delete default export from ButtonGroup.types
lhilgert9 5bea74d
fix type errors in ButtonGroup component
lhilgert9 ef86ee2
prettify buttonGroupClasses
lhilgert9 8e76568
add ButtonGroup to material-next index
lhilgert9 fb182a4
add missing exports to index
lhilgert9 ae571c4
[Button] add ButtonGroupContext logic
lhilgert9 b4fbbdb
Apply MD3 ButtonGroup style
lhilgert9 739a1fb
Add MD3 ButtonGroup playground
lhilgert9 ccc62b3
fix boxShadow was visible on focus
lhilgert9 99de694
add size prop to playground
lhilgert9 cd74152
prettify playground
lhilgert9 48172c8
export ButtonGroupRoot
lhilgert9 59e8baa
add classes to buttonGroupClasses
lhilgert9 c529dd9
fix ButtonGroup style
lhilgert9 c6925b3
fix imports for test
lhilgert9 4823cc7
prettify ButtonGroup
lhilgert9 6e1370b
fix classes in test file
lhilgert9 9999933
replace disableFocusRipple with disableTouchRipple
lhilgert9 4b96d60
prettify and update types
lhilgert9 e73b50f
add missing CssVarsProvider and extendTheme
lhilgert9 7a22f5c
.test: disableFocusRipple to disableTouchRipple
lhilgert9 40b8552
fix buttonClass.colorSecondary in .test file
lhilgert9 f47f775
.test: change hard typed classNames to classes
lhilgert9 3538ad9
convert .test file to TS
lhilgert9 723984f
prettify .test file
lhilgert9 98f9485
fix matchMedia error in unit test
lhilgert9 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
50 changes: 50 additions & 0 deletions
50
docs/data/material/components/button-group/ButtonGroupMaterialYouPlayground.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import * as React from 'react'; | ||
import Button from '@mui/material-next/Button'; | ||
import ButtonGroup from '@mui/material-next/ButtonGroup'; | ||
import MaterialYouUsageDemo from 'docs/src/modules/components/MaterialYouUsageDemo'; | ||
|
||
export default function ButtonGroupMaterialYouPlayground() { | ||
return ( | ||
<MaterialYouUsageDemo | ||
componentName="ButtonGroup" | ||
data={[ | ||
{ | ||
propName: 'variant', | ||
defaultValue: 'outlined', | ||
options: ['text', 'outlined', 'filled', 'filledTonal', 'elevated'], | ||
knob: 'select', | ||
}, | ||
{ | ||
propName: 'orientation', | ||
defaultValue: 'horizontal', | ||
options: ['vertical', 'horizontal'], | ||
knob: 'select', | ||
}, | ||
{ | ||
propName: 'color', | ||
defaultValue: 'primary', | ||
knob: 'select', | ||
options: ['primary', 'secondary', 'tertiary'], | ||
}, | ||
{ | ||
propName: 'size', | ||
defaultValue: 'medium', | ||
knob: 'select', | ||
options: ['small', 'medium', 'large'], | ||
}, | ||
{ | ||
propName: 'disabled', | ||
defaultValue: false, | ||
knob: 'switch', | ||
}, | ||
]} | ||
renderDemo={(props) => ( | ||
<ButtonGroup {...props}> | ||
<Button>One</Button> | ||
<Button>Two</Button> | ||
<Button>Three</Button> | ||
</ButtonGroup> | ||
)} | ||
/> | ||
); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
257 changes: 257 additions & 0 deletions
257
packages/mui-material-next/src/ButtonGroup/ButtonGroup.test.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,257 @@ | ||
import * as React from 'react'; | ||
import { expect } from 'chai'; | ||
import { createRenderer, describeConformance, screen } from '@mui-internal/test-utils'; | ||
import ButtonGroup, { buttonGroupClasses as classes } from '@mui/material-next/ButtonGroup'; | ||
import Button, { buttonClasses } from '@mui/material-next/Button'; | ||
import ButtonGroupContext from './ButtonGroupContext'; | ||
import { CssVarsProvider, extendTheme } from '../styles'; | ||
|
||
describe('<ButtonGroup />', () => { | ||
const { render } = createRenderer(); | ||
|
||
describeConformance( | ||
<ButtonGroup> | ||
<Button>Conformance?</Button> | ||
</ButtonGroup>, | ||
() => ({ | ||
classes, | ||
inheritComponent: 'div', | ||
render, | ||
refInstanceof: window.HTMLDivElement, | ||
testComponentPropWith: 'span', | ||
muiName: 'MuiButtonGroup', | ||
testVariantProps: { variant: 'filled' }, | ||
skip: ['componentsProp'], | ||
ThemeProvider: CssVarsProvider, | ||
createTheme: extendTheme, | ||
}), | ||
); | ||
|
||
it('should render with the root class but no others', () => { | ||
const { container } = render( | ||
<ButtonGroup> | ||
<Button>Hello World</Button> | ||
</ButtonGroup>, | ||
); | ||
const buttonGroup = container.firstChild; | ||
expect(buttonGroup).to.have.class(classes.root); | ||
expect(buttonGroup).not.to.have.class(classes.filled); | ||
expect(buttonGroup).not.to.have.class(classes.fullWidth); | ||
}); | ||
|
||
it('should render an outlined button', () => { | ||
const { getByRole } = render( | ||
<ButtonGroup> | ||
<Button>Hello World</Button> | ||
</ButtonGroup>, | ||
); | ||
const button = getByRole('button'); | ||
expect(button).to.have.class(buttonClasses.outlined); | ||
expect(button).to.have.class(classes.grouped); | ||
expect(button).to.have.class(classes.groupedOutlined); | ||
expect(button).to.have.class(classes.groupedOutlinedPrimary); | ||
expect(button).not.to.have.class(classes.groupedOutlinedSecondary); | ||
}); | ||
|
||
it('can render an outlined primary button', () => { | ||
const { getByRole } = render( | ||
<ButtonGroup color="primary"> | ||
<Button>Hello World</Button> | ||
</ButtonGroup>, | ||
); | ||
const button = getByRole('button'); | ||
expect(button).to.have.class(buttonClasses.outlined); | ||
expect(button).to.have.class(buttonClasses.colorPrimary); | ||
expect(button).to.have.class(classes.grouped); | ||
expect(button).to.have.class(classes.groupedOutlined); | ||
expect(button).to.have.class(classes.groupedOutlinedPrimary); | ||
expect(button).not.to.have.class(classes.groupedOutlinedSecondary); | ||
}); | ||
|
||
it('can render a filled button', () => { | ||
const { getByRole } = render( | ||
<ButtonGroup variant="filled"> | ||
<Button>Hello World</Button> | ||
</ButtonGroup>, | ||
); | ||
const button = getByRole('button'); | ||
expect(button).to.have.class(buttonClasses.filled); | ||
expect(button).to.have.class(classes.grouped); | ||
expect(button).to.have.class(classes.groupedFilled); | ||
expect(button).to.have.class(classes.groupedFilledPrimary); | ||
expect(button).not.to.have.class(classes.groupedFilledSecondary); | ||
}); | ||
|
||
it('can render a small button', () => { | ||
const { getByRole } = render( | ||
<ButtonGroup size="small"> | ||
<Button>Hello World</Button> | ||
</ButtonGroup>, | ||
); | ||
const button = getByRole('button'); | ||
expect(button).to.have.class(buttonClasses.sizeSmall); | ||
}); | ||
|
||
it('can render a large button', () => { | ||
const { getByRole } = render( | ||
<ButtonGroup size="large"> | ||
<Button>Hello World</Button> | ||
</ButtonGroup>, | ||
); | ||
const button = getByRole('button'); | ||
expect(button).to.have.class(buttonClasses.sizeLarge); | ||
}); | ||
|
||
it('should have a ripple by default', () => { | ||
const { container } = render( | ||
<ButtonGroup> | ||
<Button TouchRippleProps={{ classes: { root: 'touchRipple' } }}>Hello World</Button> | ||
</ButtonGroup>, | ||
); | ||
expect(container.querySelector('.touchRipple')).not.to.equal(null); | ||
}); | ||
|
||
it('can disable the elevation', () => { | ||
const { getByRole } = render( | ||
<ButtonGroup disableElevation> | ||
<Button>Hello World</Button> | ||
</ButtonGroup>, | ||
); | ||
const button = getByRole('button'); | ||
expect(button).to.have.class(buttonClasses.disableElevation); | ||
}); | ||
|
||
it('can disable the ripple', () => { | ||
const { container } = render( | ||
<ButtonGroup disableRipple> | ||
<Button TouchRippleProps={{ classes: { root: 'touchRipple' } }}>Hello World</Button> | ||
</ButtonGroup>, | ||
); | ||
expect(container.querySelector('.touchRipple')).to.equal(null); | ||
}); | ||
|
||
it('should not be fullWidth by default', () => { | ||
const { container, getByRole } = render( | ||
<ButtonGroup> | ||
<Button>Hello World</Button> | ||
</ButtonGroup>, | ||
); | ||
const button = getByRole('button'); | ||
const buttonGroup = container.firstChild; | ||
expect(buttonGroup).not.to.have.class(classes.fullWidth); | ||
expect(button).not.to.have.class(buttonClasses.fullWidth); | ||
}); | ||
|
||
it('can pass fullWidth to Button', () => { | ||
const { container, getByRole } = render( | ||
<ButtonGroup fullWidth> | ||
<Button>Hello World</Button> | ||
</ButtonGroup>, | ||
); | ||
const buttonGroup = container.firstChild; | ||
const button = getByRole('button'); | ||
expect(buttonGroup).to.have.class(classes.fullWidth); | ||
expect(button).to.have.class(buttonClasses.fullWidth); | ||
}); | ||
|
||
it('classes.grouped should be merged with Button className', () => { | ||
render( | ||
<ButtonGroup> | ||
<Button className="foo-bar">Hello World</Button> | ||
</ButtonGroup>, | ||
); | ||
expect(screen.getByRole('button')).to.have.class(classes.grouped); | ||
expect(screen.getByRole('button')).to.have.class('foo-bar'); | ||
}); | ||
|
||
it('should forward the context to children', () => { | ||
let context; | ||
render( | ||
<ButtonGroup size="large" variant="filled"> | ||
<ButtonGroupContext.Consumer> | ||
{(value) => { | ||
context = value; | ||
}} | ||
</ButtonGroupContext.Consumer> | ||
</ButtonGroup>, | ||
); | ||
expect(context.variant).to.equal('filled'); | ||
expect(context.size).to.equal('large'); | ||
expect(context.fullWidth).to.equal(false); | ||
expect(context.disableRipple).to.equal(false); | ||
expect(context.disableTouchRipple).to.equal(false); | ||
expect(context.disableElevation).to.equal(false); | ||
expect(context.disabled).to.equal(false); | ||
expect(context.color).to.equal('primary'); | ||
}); | ||
|
||
describe('theme default props on Button', () => { | ||
it('should override default variant prop', () => { | ||
render( | ||
<CssVarsProvider | ||
theme={extendTheme({ | ||
components: { | ||
MuiButton: { | ||
defaultProps: { | ||
color: 'primary', | ||
size: 'medium', | ||
variant: 'filled', | ||
}, | ||
}, | ||
}, | ||
})} | ||
> | ||
<ButtonGroup variant="outlined" size="small" color="secondary"> | ||
<Button>Hello World</Button> | ||
</ButtonGroup> | ||
</CssVarsProvider>, | ||
); | ||
|
||
expect(screen.getByRole('button')).to.have.class(buttonClasses.outlined); | ||
expect(screen.getByRole('button')).to.have.class(buttonClasses.sizeSmall); | ||
expect(screen.getByRole('button')).to.have.class(buttonClasses.colorSecondary); | ||
}); | ||
}); | ||
|
||
describe('position classes', () => { | ||
it('correctly applies position classes to buttons', () => { | ||
render( | ||
<ButtonGroup> | ||
<Button>Button 1</Button> | ||
<Button>Button 2</Button> | ||
<Button>Button 3</Button> | ||
</ButtonGroup>, | ||
); | ||
|
||
const firstButton = screen.getAllByRole('button')[0]; | ||
const middleButton = screen.getAllByRole('button')[1]; | ||
const lastButton = screen.getAllByRole('button')[2]; | ||
|
||
expect(firstButton).to.have.class(classes.firstButton); | ||
expect(firstButton).not.to.have.class(classes.middleButton); | ||
expect(firstButton).not.to.have.class(classes.lastButton); | ||
|
||
expect(middleButton).to.have.class(classes.middleButton); | ||
expect(middleButton).not.to.have.class(classes.firstButton); | ||
expect(middleButton).not.to.have.class(classes.lastButton); | ||
|
||
expect(lastButton).to.have.class(classes.lastButton); | ||
expect(lastButton).not.to.have.class(classes.middleButton); | ||
expect(lastButton).not.to.have.class(classes.firstButton); | ||
}); | ||
|
||
it('does not apply any position classes to a single button', () => { | ||
render( | ||
<ButtonGroup> | ||
<Button>Single Button</Button> | ||
</ButtonGroup>, | ||
); | ||
|
||
const button = screen.getByRole('button'); | ||
|
||
expect(button).not.to.have.class(classes.firstButton); | ||
expect(button).not.to.have.class(classes.middleButton); | ||
expect(button).not.to.have.class(classes.lastButton); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To fix the unit test, you need to kind of mock
window.matchMedia
like thisAnd as part of this, the test should be converted to TS as well ~