",
- "styledComponent": false,
+ "styledComponent": true,
"cssComponent": false
}
diff --git a/docs/translations/api-docs/native-select/native-select.json b/docs/translations/api-docs/native-select/native-select.json
index b303222f1c7269..eb51c78befe586 100644
--- a/docs/translations/api-docs/native-select/native-select.json
+++ b/docs/translations/api-docs/native-select/native-select.json
@@ -7,6 +7,7 @@
"input": "An Input element; does not have to be a material-ui specific Input.",
"inputProps": "<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Attributes">Attributes</a> applied to the select element.",
"onChange": "Callback fired when a menu item is selected.
Signature: function(event: object) => void event: The event source of the callback. You can pull out the new value by accessing event.target.value (string).",
+ "sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the `sx` page for more details.",
"value": "The input value. The DOM API casts this to a string.",
"variant": "The variant to use."
},
diff --git a/packages/material-ui/src/NativeSelect/NativeSelect.d.ts b/packages/material-ui/src/NativeSelect/NativeSelect.d.ts
index fc2102f5edaadc..494b7132d4527e 100644
--- a/packages/material-ui/src/NativeSelect/NativeSelect.d.ts
+++ b/packages/material-ui/src/NativeSelect/NativeSelect.d.ts
@@ -1,5 +1,6 @@
import * as React from 'react';
-import { InternalStandardProps as StandardProps } from '..';
+import { SxProps } from '@material-ui/system';
+import { InternalStandardProps as StandardProps, Theme } from '..';
import { InputProps } from '../Input';
import { NativeSelectInputProps } from './NativeSelectInput';
@@ -58,6 +59,10 @@ export interface NativeSelectProps
* You can pull out the new value by accessing `event.target.value` (string).
*/
onChange?: NativeSelectInputProps['onChange'];
+ /**
+ * The system prop that allows defining system overrides as well as additional CSS styles.
+ */
+ sx?: SxProps;
/**
* The `input` value. The DOM API casts this to a string.
*/
diff --git a/packages/material-ui/src/NativeSelect/NativeSelect.js b/packages/material-ui/src/NativeSelect/NativeSelect.js
index f1c1beea345778..3e234c3a363954 100644
--- a/packages/material-ui/src/NativeSelect/NativeSelect.js
+++ b/packages/material-ui/src/NativeSelect/NativeSelect.js
@@ -1,11 +1,11 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import NativeSelectInput from './NativeSelectInput';
-import withStyles from '../styles/withStyles';
import formControlState from '../FormControl/formControlState';
import useFormControl from '../FormControl/useFormControl';
import ArrowDropDownIcon from '../internal/svg-icons/ArrowDropDown';
import Input from '../Input';
+import useThemeProps from '../styles/useThemeProps';
export const styles = (theme) => ({
/* Styles applied to the select component `root` class. */
@@ -62,7 +62,7 @@ export const styles = (theme) => ({
},
/* Styles applied to the select component `selectMenu` class. */
selectMenu: {
- height: 'auto', // Resets for multiple select with chips
+ height: 'auto', // Resets for multipile select with chips
minHeight: '1.4375em', // Required for select\text-field height consistency
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
@@ -111,7 +111,8 @@ const defaultInput = ;
/**
* An alternative to `` with a much smaller bundle size footprint.
*/
-const NativeSelect = React.forwardRef(function NativeSelect(props, ref) {
+const NativeSelect = React.forwardRef(function NativeSelect(inProps, ref) {
+ const props = useThemeProps({ name: 'MuiNativeSelect', props: inProps });
const {
children,
classes,
@@ -182,6 +183,10 @@ NativeSelect.propTypes /* remove-proptypes */ = {
* You can pull out the new value by accessing `event.target.value` (string).
*/
onChange: PropTypes.func,
+ /**
+ * The system prop that allows defining system overrides as well as additional CSS styles.
+ */
+ sx: PropTypes.object,
/**
* The `input` value. The DOM API casts this to a string.
*/
@@ -194,4 +199,4 @@ NativeSelect.propTypes /* remove-proptypes */ = {
NativeSelect.muiName = 'Select';
-export default withStyles(styles, { name: 'MuiNativeSelect' })(NativeSelect);
+export default NativeSelect;
diff --git a/packages/material-ui/src/NativeSelect/NativeSelect.test.js b/packages/material-ui/src/NativeSelect/NativeSelect.test.js
index a393af5c7744a5..7a62ebfcbbdb96 100644
--- a/packages/material-ui/src/NativeSelect/NativeSelect.test.js
+++ b/packages/material-ui/src/NativeSelect/NativeSelect.test.js
@@ -1,12 +1,11 @@
import * as React from 'react';
import { expect } from 'chai';
-import { getClasses, createMount, createClientRender, describeConformance } from 'test/utils';
+import { createMount, createClientRender, describeConformanceV5 } from 'test/utils';
import Input, { inputClasses } from '../Input';
import NativeSelect from './NativeSelect';
+import classes from './nativeSelectClasses';
describe('', () => {
- let classes;
-
const mount = createMount();
const render = createClientRender();
const defaultProps = {
@@ -21,16 +20,14 @@ describe('', () => {
],
};
- before(() => {
- classes = getClasses();
- });
-
- describeConformance(, () => ({
+ describeConformanceV5(, () => ({
classes,
inheritComponent: Input,
mount,
+ render,
refInstanceof: window.HTMLDivElement,
- skip: ['componentProp', 'rootClass'],
+ muiName: 'MuiNativeSelect',
+ skip: ['componentProp', 'componentsProp', 'rootClass', 'themeVariants', 'themeStyleOverrides'],
}));
it('should render a native select', () => {
diff --git a/packages/material-ui/src/NativeSelect/NativeSelectInput.d.ts b/packages/material-ui/src/NativeSelect/NativeSelectInput.d.ts
index a95956e04eb089..67b344b1a97dc0 100644
--- a/packages/material-ui/src/NativeSelect/NativeSelectInput.d.ts
+++ b/packages/material-ui/src/NativeSelect/NativeSelectInput.d.ts
@@ -1,10 +1,13 @@
import * as React from 'react';
+import { SxProps } from '@material-ui/system';
+import { Theme } from '..';
export interface NativeSelectInputProps extends React.SelectHTMLAttributes {
disabled?: boolean;
IconComponent?: React.ElementType;
inputRef?: React.Ref;
variant?: 'standard' | 'outlined' | 'filled';
+ sx?: SxProps;
}
declare const NativeSelectInput: React.ComponentType;
diff --git a/packages/material-ui/src/NativeSelect/NativeSelectInput.js b/packages/material-ui/src/NativeSelect/NativeSelectInput.js
index 2a359d93faf2b6..5cf8af18bc9977 100644
--- a/packages/material-ui/src/NativeSelect/NativeSelectInput.js
+++ b/packages/material-ui/src/NativeSelect/NativeSelectInput.js
@@ -1,45 +1,145 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
-import { refType } from '@material-ui/utils';
+import { refType, deepmerge } from '@material-ui/utils';
+import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled';
import capitalize from '../utils/capitalize';
+import nativeSelectClasses, { getNativeSelectUtilitiyClasses } from './nativeSelectClasses';
+import experimentalStyled from '../styles/experimentalStyled';
+
+export const overridesResolver = (props, styles) => {
+ const { styleProps } = props;
+ return deepmerge(styles.root, {
+ ...styles.select,
+ ...styles[styleProps.variant],
+ [`& .${nativeSelectClasses.icon}`]: {
+ ...styles.icon,
+ ...(styleProps.variant && styles[`icon${capitalize(styleProps.variant)}`]),
+ ...(styleProps.open && styles.iconOpen),
+ },
+ });
+};
+
+const useUtilityClasses = (styleProps) => {
+ const { classes, variant, disabled, open } = styleProps;
+
+ const slots = {
+ root: ['root', 'select', variant, disabled && 'disabled'],
+ icon: ['icon', `icon${capitalize(variant)}`, open && 'iconOpen', disabled && 'disabled'],
+ };
+
+ return composeClasses(slots, getNativeSelectUtilitiyClasses, classes);
+};
+
+const SelectRoot = experimentalStyled(
+ 'select',
+ {},
+ { name: 'MuiNativeSelect', slot: 'Root', overridesResolver },
+)(({ styleProps, theme }) => ({
+ MozAppearance: 'none', // Reset
+ WebkitAppearance: 'none', // Reset
+ // When interacting quickly, the text can end up selected.
+ // Native select can't be selected either.
+ userSelect: 'none',
+ borderRadius: 0, // Reset
+ minWidth: 16, // So it doesn't collapse.
+ cursor: 'pointer',
+ '&:focus': {
+ // Show that it's not an text input
+ backgroundColor:
+ theme.palette.mode === 'light' ? 'rgba(0, 0, 0, 0.05)' : 'rgba(255, 255, 255, 0.05)',
+ borderRadius: 0, // Reset Chrome style
+ },
+ // Remove IE11 arrow
+ '&::-ms-expand': {
+ display: 'none',
+ },
+ '&.Mui-disabled': {
+ cursor: 'default',
+ },
+ '&[multiple]': {
+ height: 'auto',
+ },
+ '&:not([multiple]) option, &:not([multiple]) optgroup': {
+ backgroundColor: theme.palette.background.paper,
+ },
+ // Bump specificity to allow extending custom inputs
+ '&&': {
+ paddingRight: 24,
+ },
+ ...(styleProps.variant === 'filled' && {
+ '&&': {
+ paddingRight: 32,
+ },
+ }),
+ ...(styleProps.variant === 'outlined' && {
+ borderRadius: theme.shape.borderRadius,
+ '&:focus': {
+ borderRadius: theme.shape.borderRadius, // Reset the reset for Chrome style
+ },
+ '&&': {
+ paddingRight: 32,
+ },
+ }),
+ ...(styleProps.selectMenu && {
+ height: 'auto', // Resets for multpile select with chips
+ minHeight: '1.4375em', // Required for select\text-field height consistency
+ textOverflow: 'ellipsis',
+ whiteSpace: 'nowrap',
+ overflow: 'hidden',
+ }),
+}));
+
+const IconRoot = experimentalStyled(
+ 'svg',
+ {},
+ { name: 'MuiNativeSelect', slot: 'Icon' },
+)(({ styleProps, theme }) => ({
+ // We use a position absolute over a flexbox in order to forward the pointer events
+ // to the input and to support wrapping tags..
+ position: 'absolute',
+ right: 0,
+ top: 'calc(50% - 12px)', // Center vertically
+ pointerEvents: 'none', // Don't block pointer events on the select under the icon.
+ color: theme.palette.action.active,
+ '&.Mui-disabled': {
+ color: theme.palette.action.disabled,
+ },
+ ...(styleProps.open && {
+ right: 7,
+ }),
+ ...(styleProps.variant === 'filled' && {
+ right: 7,
+ }),
+ ...(styleProps.variant === 'outlined' && {
+ right: 7,
+ }),
+}));
/**
* @ignore - internal component.
*/
const NativeSelectInput = React.forwardRef(function NativeSelectInput(props, ref) {
- const {
- classes,
- className,
+ const { className, disabled, IconComponent, inputRef, variant = 'standard', ...other } = props;
+
+ const styleProps = {
+ ...props,
disabled,
- IconComponent,
- inputRef,
- variant = 'standard',
- ...other
- } = props;
+ variant,
+ };
+ const classes = useUtilityClasses(styleProps);
return (
-
{props.multiple ? null : (
-
+
)}
);
@@ -55,7 +155,7 @@ NativeSelectInput.propTypes = {
* Override or extend the styles applied to the component.
* See [CSS API](#css) below for more details.
*/
- classes: PropTypes.object.isRequired,
+ classes: PropTypes.object,
/**
* The CSS class name of the select element.
*/
diff --git a/packages/material-ui/src/NativeSelect/NativeSelectInput.test.js b/packages/material-ui/src/NativeSelect/NativeSelectInput.test.js
index 4d2044aa7e5249..563a5ce7f9e105 100644
--- a/packages/material-ui/src/NativeSelect/NativeSelectInput.test.js
+++ b/packages/material-ui/src/NativeSelect/NativeSelectInput.test.js
@@ -1,7 +1,7 @@
import * as React from 'react';
import { expect } from 'chai';
import { spy } from 'sinon';
-import { createMount, describeConformance, createClientRender } from 'test/utils';
+import { createMount, describeConformanceV5, createClientRender } from 'test/utils';
import NativeSelectInput from './NativeSelectInput';
describe('', () => {
@@ -25,10 +25,11 @@ describe('', () => {
],
};
- describeConformance( {}} />, () => ({
+ describeConformanceV5( {}} />, () => ({
mount,
only: ['refForwarding'],
refInstanceof: window.HTMLSelectElement,
+ muiName: 'MuiNativeSelectInput',
}));
it('should render a native select', () => {
diff --git a/packages/material-ui/src/NativeSelect/index.d.ts b/packages/material-ui/src/NativeSelect/index.d.ts
index b98e687a6d6fa1..026e02bdf5c8a9 100644
--- a/packages/material-ui/src/NativeSelect/index.d.ts
+++ b/packages/material-ui/src/NativeSelect/index.d.ts
@@ -1,2 +1,4 @@
export { default } from './NativeSelect';
export * from './NativeSelect';
+export { default as nativeSelectClasses } from './nativeSelectClasses';
+export * from './nativeSelectClasses';
diff --git a/packages/material-ui/src/NativeSelect/index.js b/packages/material-ui/src/NativeSelect/index.js
index e2f92130f3481e..1377cb53267a13 100644
--- a/packages/material-ui/src/NativeSelect/index.js
+++ b/packages/material-ui/src/NativeSelect/index.js
@@ -1 +1,3 @@
export { default } from './NativeSelect';
+export { default as nativeSelectClasses } from './nativeSelectClasses';
+export * from './nativeSelectClasses';
diff --git a/packages/material-ui/src/NativeSelect/nativeSelectClasses.d.ts b/packages/material-ui/src/NativeSelect/nativeSelectClasses.d.ts
new file mode 100644
index 00000000000000..8f484a275c2fd0
--- /dev/null
+++ b/packages/material-ui/src/NativeSelect/nativeSelectClasses.d.ts
@@ -0,0 +1,7 @@
+import { NativeSelectClassKey } from './NativeSelect';
+
+declare const nativeSelectClasses: Record;
+
+export function getNativeSelectUtilityClasses(slot: string): string;
+
+export default nativeSelectClasses;
diff --git a/packages/material-ui/src/NativeSelect/nativeSelectClasses.js b/packages/material-ui/src/NativeSelect/nativeSelectClasses.js
new file mode 100644
index 00000000000000..70cfb0a4434ee9
--- /dev/null
+++ b/packages/material-ui/src/NativeSelect/nativeSelectClasses.js
@@ -0,0 +1,21 @@
+import { generateUtilityClass, generateUtilityClasses } from '@material-ui/unstyled';
+
+export function getNativeSelectUtilitiyClasses(slot) {
+ return generateUtilityClass('MuiNativeSelect', slot);
+}
+
+const nativeSelectClasses = generateUtilityClasses('MuiNativeSelect', [
+ 'root',
+ 'select',
+ 'filled',
+ 'outlined',
+ 'selectMenu',
+ 'disabled',
+ 'icon',
+ 'iconOpen',
+ 'iconFilled',
+ 'iconOutlined',
+ 'nativeInput',
+]);
+
+export default nativeSelectClasses;