Skip to content

Commit 6b5b5e6

Browse files
[material-ui][BottomNavigationAction] Add slots and slotProps (@sai6855) (#45875)
Co-authored-by: sai chand <[email protected]>
1 parent f221ef0 commit 6b5b5e6

File tree

5 files changed

+140
-32
lines changed

5 files changed

+140
-32
lines changed

docs/pages/material-ui/api/bottom-navigation-action.json

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@
55
"icon": { "type": { "name": "node" } },
66
"label": { "type": { "name": "node" } },
77
"showLabel": { "type": { "name": "bool" } },
8+
"slotProps": {
9+
"type": {
10+
"name": "shape",
11+
"description": "{ label?: func<br>&#124;&nbsp;object, root?: func<br>&#124;&nbsp;object }"
12+
},
13+
"default": "{}"
14+
},
15+
"slots": {
16+
"type": { "name": "shape", "description": "{ label?: elementType, root?: elementType }" },
17+
"default": "{}"
18+
},
819
"sx": {
920
"type": {
1021
"name": "union",
@@ -19,25 +30,27 @@
1930
"import BottomNavigationAction from '@mui/material/BottomNavigationAction';",
2031
"import { BottomNavigationAction } from '@mui/material';"
2132
],
33+
"slots": [
34+
{
35+
"name": "root",
36+
"description": "The component that renders the root.",
37+
"default": "ButtonBase",
38+
"class": "MuiBottomNavigationAction-root"
39+
},
40+
{
41+
"name": "label",
42+
"description": "The component that renders the label.",
43+
"default": "span",
44+
"class": "MuiBottomNavigationAction-label"
45+
}
46+
],
2247
"classes": [
2348
{
2449
"key": "iconOnly",
2550
"className": "MuiBottomNavigationAction-iconOnly",
2651
"description": "State class applied to the root element if `showLabel={false}` and not selected.",
2752
"isGlobal": false
2853
},
29-
{
30-
"key": "label",
31-
"className": "MuiBottomNavigationAction-label",
32-
"description": "Styles applied to the label's span element.",
33-
"isGlobal": false
34-
},
35-
{
36-
"key": "root",
37-
"className": "MuiBottomNavigationAction-root",
38-
"description": "Styles applied to the root element.",
39-
"isGlobal": false
40-
},
4154
{
4255
"key": "selected",
4356
"className": "Mui-selected",

docs/translations/api-docs/bottom-navigation-action/bottom-navigation-action.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
"showLabel": {
1111
"description": "If <code>true</code>, the <code>BottomNavigationAction</code> will show its label. By default, only the selected <code>BottomNavigationAction</code> inside <code>BottomNavigation</code> will show its label.<br>The prop defaults to the value (<code>false</code>) inherited from the parent BottomNavigation component."
1212
},
13+
"slotProps": { "description": "The props used for each slot inside." },
14+
"slots": { "description": "The components used for each slot inside." },
1315
"sx": {
1416
"description": "The system prop that allows defining system overrides as well as additional CSS styles."
1517
},
@@ -23,15 +25,14 @@
2325
"nodeName": "the root element",
2426
"conditions": "<code>showLabel={false}</code> and not selected"
2527
},
26-
"label": {
27-
"description": "Styles applied to {{nodeName}}.",
28-
"nodeName": "the label&#39;s span element"
29-
},
30-
"root": { "description": "Styles applied to the root element." },
3128
"selected": {
3229
"description": "State class applied to {{nodeName}} if {{conditions}}.",
3330
"nodeName": "the root element",
3431
"conditions": "selected"
3532
}
33+
},
34+
"slotDescriptions": {
35+
"label": "The component that renders the label.",
36+
"root": "The component that renders the root."
3637
}
3738
}

packages/mui-material/src/BottomNavigationAction/BottomNavigationAction.d.ts

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,45 @@
11
import * as React from 'react';
22
import { SxProps } from '@mui/system';
3-
import { Theme } from '..';
4-
import { ButtonBaseTypeMap, ExtendButtonBase, ExtendButtonBaseTypeMap } from '../ButtonBase';
3+
import { CreateSlotsAndSlotProps, SlotProps, Theme } from '..';
4+
import {
5+
ButtonBaseProps,
6+
ButtonBaseTypeMap,
7+
ExtendButtonBase,
8+
ExtendButtonBaseTypeMap,
9+
} from '../ButtonBase';
510
import { OverrideProps } from '../OverridableComponent';
611
import { BottomNavigationActionClasses } from './bottomNavigationActionClasses';
712

8-
export interface BottomNavigationActionOwnProps {
13+
export interface BottomNavigationActionSlots {
14+
/**
15+
* The component that renders the root.
16+
* @default ButtonBase
17+
*/
18+
root: React.ElementType;
19+
/**
20+
* The component that renders the label.
21+
* @default span
22+
*/
23+
label: React.ElementType;
24+
}
25+
26+
export type BottomNavigationActionSlotsAndSlotProps = CreateSlotsAndSlotProps<
27+
BottomNavigationActionSlots,
28+
{
29+
/**
30+
* Props forwarded to the root slot.
31+
* By default, the avaible props are based on the ButtonBase element.
32+
*/
33+
root: SlotProps<React.ElementType<ButtonBaseProps>, {}, BottomNavigationActionOwnerState>;
34+
/**
35+
* Props forwarded to the label slot.
36+
* By default, the avaible props are based on the span element.
37+
*/
38+
label: SlotProps<'span', {}, BottomNavigationActionOwnerState>;
39+
}
40+
>;
41+
42+
export interface BottomNavigationActionOwnProps extends BottomNavigationActionSlotsAndSlotProps {
943
/**
1044
* This prop isn't supported.
1145
* Use the `component` prop if you need to change the children structure.
@@ -71,4 +105,7 @@ export type BottomNavigationActionProps<
71105
component?: React.ElementType;
72106
};
73107

108+
export interface BottomNavigationActionOwnerState
109+
extends Omit<BottomNavigationActionProps, 'slots' | 'slotProps'> {}
110+
74111
export default BottomNavigationAction;

packages/mui-material/src/BottomNavigationAction/BottomNavigationAction.js

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import unsupportedProp from '../utils/unsupportedProp';
1111
import bottomNavigationActionClasses, {
1212
getBottomNavigationActionUtilityClass,
1313
} from './bottomNavigationActionClasses';
14+
import useSlot from '../utils/useSlot';
1415

1516
const useUtilityClasses = (ownerState) => {
1617
const { classes, showLabel, selected } = ownerState;
@@ -100,6 +101,8 @@ const BottomNavigationAction = React.forwardRef(function BottomNavigationAction(
100101
selected,
101102
showLabel,
102103
value,
104+
slots = {},
105+
slotProps = {},
103106
...other
104107
} = props;
105108

@@ -116,20 +119,45 @@ const BottomNavigationAction = React.forwardRef(function BottomNavigationAction(
116119
}
117120
};
118121

122+
const externalForwardedProps = {
123+
slots,
124+
slotProps,
125+
};
126+
127+
const [RootSlot, rootProps] = useSlot('root', {
128+
elementType: BottomNavigationActionRoot,
129+
externalForwardedProps: {
130+
...externalForwardedProps,
131+
...other,
132+
},
133+
shouldForwardComponentProp: true,
134+
ownerState,
135+
ref,
136+
className: clsx(classes.root, className),
137+
additionalProps: {
138+
focusRipple: true,
139+
},
140+
getSlotProps: (handlers) => ({
141+
...handlers,
142+
onClick: (event) => {
143+
handlers.onClick?.(event);
144+
handleChange(event);
145+
},
146+
}),
147+
});
148+
149+
const [LabelSlot, labelProps] = useSlot('label', {
150+
elementType: BottomNavigationActionLabel,
151+
externalForwardedProps,
152+
ownerState,
153+
className: classes.label,
154+
});
155+
119156
return (
120-
<BottomNavigationActionRoot
121-
ref={ref}
122-
className={clsx(classes.root, className)}
123-
focusRipple
124-
onClick={handleChange}
125-
ownerState={ownerState}
126-
{...other}
127-
>
157+
<RootSlot {...rootProps}>
128158
{icon}
129-
<BottomNavigationActionLabel className={classes.label} ownerState={ownerState}>
130-
{label}
131-
</BottomNavigationActionLabel>
132-
</BottomNavigationActionRoot>
159+
<LabelSlot {...labelProps}>{label}</LabelSlot>
160+
</RootSlot>
133161
);
134162
});
135163

@@ -175,6 +203,22 @@ BottomNavigationAction.propTypes /* remove-proptypes */ = {
175203
* The prop defaults to the value (`false`) inherited from the parent BottomNavigation component.
176204
*/
177205
showLabel: PropTypes.bool,
206+
/**
207+
* The props used for each slot inside.
208+
* @default {}
209+
*/
210+
slotProps: PropTypes.shape({
211+
label: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
212+
root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
213+
}),
214+
/**
215+
* The components used for each slot inside.
216+
* @default {}
217+
*/
218+
slots: PropTypes.shape({
219+
label: PropTypes.elementType,
220+
root: PropTypes.elementType,
221+
}),
178222
/**
179223
* The system prop that allows defining system overrides as well as additional CSS styles.
180224
*/

packages/mui-material/src/BottomNavigationAction/BottomNavigationAction.test.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import BottomNavigationAction, {
88
import ButtonBase from '@mui/material/ButtonBase';
99
import describeConformance from '../../test/describeConformance';
1010

11+
const CustomButtonBase = React.forwardRef(({ focusRipple, ...props }, ref) => (
12+
<ButtonBase ref={ref} {...props} />
13+
));
14+
1115
describe('<BottomNavigationAction />', () => {
1216
const { render } = createRenderer();
1317

@@ -20,6 +24,15 @@ describe('<BottomNavigationAction />', () => {
2024
testVariantProps: { showLabel: true },
2125
testDeepOverrides: { slotName: 'label', slotClassName: classes.label },
2226
skip: ['componentProp', 'componentsProp'],
27+
slots: {
28+
root: {
29+
expectedClassName: classes.root,
30+
testWithElement: CustomButtonBase,
31+
},
32+
label: {
33+
expectedClassName: classes.label,
34+
},
35+
},
2336
}));
2437

2538
it('adds a `selected` class when selected', () => {

0 commit comments

Comments
 (0)