Skip to content
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

[TextField] Migrate Input component to emotion #24638

Merged
merged 11 commits into from
Jan 27, 2021
Merged
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
3 changes: 2 additions & 1 deletion docs/pages/api-docs/input.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"required": { "type": { "name": "bool" } },
"rows": { "type": { "name": "union", "description": "number<br>&#124;&nbsp;string" } },
"startAdornment": { "type": { "name": "node" } },
"sx": { "type": { "name": "object" } },
"type": { "type": { "name": "string" }, "default": "'text'" },
"value": { "type": { "name": "any" } }
},
Expand Down Expand Up @@ -54,6 +55,6 @@
"filename": "/packages/material-ui/src/Input/Input.js",
"inheritance": { "component": "InputBase", "pathname": "/api/input-base/" },
"demos": "<ul><li><a href=\"/components/text-fields/\">Text Fields</a></li></ul>",
"styledComponent": false,
"styledComponent": true,
"cssComponent": false
}
1 change: 1 addition & 0 deletions docs/translations/api-docs/input/input.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"required": "If <code>true</code>, the <code>input</code> element is required. The prop defaults to the value (<code>false</code>) inherited from the parent FormControl component.",
"rows": "Number of rows to display when multiline option is set to true.",
"startAdornment": "Start <code>InputAdornment</code> for this component.",
"sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the <a href=\"/system/basics/#the-sx-prop\">`sx` page</a> for more details.",
"type": "Type of the <code>input</code> element. It should be <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Form_%3Cinput%3E_types\">a valid HTML5 input type</a>.",
"value": "The value of the <code>input</code> element, required for a controlled component."
},
Expand Down
12 changes: 6 additions & 6 deletions packages/material-ui/src/Autocomplete/Autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,20 @@ export const styles = (theme) => ({
width: 0,
minWidth: 30,
},
'&[class*="MuiInput-root"]': {
'&.MuiInput-root': {
paddingBottom: 1,
'& $input': {
'& .MuiInput-input': {
padding: 4,
},
'& $input:first-child': {
'& .MuiInput-input:first-child': {
padding: '6px 0',
},
},
'&[class*="MuiInput-root"][class*="MuiInput-sizeSmall"]': {
'& $input': {
'&.MuiInput-root.MuiInputBase-sizeSmall': {
'& .MuiInput-input': {
padding: '2px 4px 3px',
},
'& $input:first-child': {
'& .MuiInput-input:first-child': {
padding: '1px 0 4px',
},
},
Expand Down
7 changes: 6 additions & 1 deletion packages/material-ui/src/Input/Input.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { InternalStandardProps as StandardProps } from '..';
import { SxProps } from '@material-ui/system';
import { InternalStandardProps as StandardProps, Theme } from '..';
import { InputBaseProps } from '../InputBase';

export interface InputProps extends StandardProps<InputBaseProps> {
Expand Down Expand Up @@ -39,6 +40,10 @@ export interface InputProps extends StandardProps<InputBaseProps> {
* If `true`, the `input` will not have an underline.
*/
disableUnderline?: boolean;
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx?: SxProps<Theme>;
}

export type InputClassKey = keyof NonNullable<InputProps['classes']>;
Expand Down
121 changes: 65 additions & 56 deletions packages/material-ui/src/Input/Input.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,49 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { refType } from '@material-ui/utils';
import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled';
import { deepmerge, refType } from '@material-ui/utils';
import InputBase from '../InputBase';
import withStyles from '../styles/withStyles';
import experimentalStyled, { shouldForwardProp } from '../styles/experimentalStyled';
import useThemeProps from '../styles/useThemeProps';
import { getInputUtilityClass } from './inputClasses';
import {
overridesResolver as inputBaseOverridesResolver,
InputBaseRoot,
} from '../InputBase/InputBase';

export const styles = (theme) => {
const overridesResolver = (props, styles) => {
const { styleProps } = props;
return deepmerge(inputBaseOverridesResolver(props, styles), {
...(!styleProps.disableUnderline && styles.underline),
});
};

const useUtilityClasses = (styleProps) => {
const { classes, disableUnderline } = styleProps;

const slots = {
root: ['root', !disableUnderline && 'underline'],
input: ['input'],
};

return composeClasses(slots, getInputUtilityClass, classes);
};

const InputRoot = experimentalStyled(
InputBaseRoot,
{ shouldForwardProp: (prop) => shouldForwardProp(prop) || prop === 'classes' },
{ name: 'MuiInput', slot: 'Root', overridesResolver },
)(({ theme, styleProps }) => {
const light = theme.palette.mode === 'light';
const bottomLineColor = light ? 'rgba(0, 0, 0, 0.42)' : 'rgba(255, 255, 255, 0.7)';

return {
/* Styles applied to the root element. */
root: {
position: 'relative',
},
/* Styles applied to the root element if the component is a descendant of `FormControl`. */
formControl: {
position: 'relative',
...(styleProps.formControl && {
'label + &': {
marginTop: 16,
},
},
/* Styles applied to the root element if the component is focused. */
focused: {},
/* Styles applied to the root element if `disabled={true}`. */
disabled: {},
/* Styles applied to the root element if color secondary. */
colorSecondary: {
'&$underline:after': {
borderBottomColor: theme.palette.secondary.main,
},
},
/* Styles applied to the root element unless `disableUnderline={true}`. */
underline: {
}),
...(!styleProps.disableUnderline && {
'&:after': {
borderBottom: `2px solid ${theme.palette.primary.main}`,
left: 0,
Expand All @@ -46,11 +58,14 @@ export const styles = (theme) => {
easing: theme.transitions.easing.easeOut,
}),
pointerEvents: 'none', // Transparent to the hover style.
...(styleProps.color === 'secondary' && {
borderBottomColor: theme.palette.secondary.main,
}),
},
'&$focused:after': {
'&.Mui-focused:after': {
transform: 'scaleX(1)',
},
'&$error:after': {
'&.Mui-error:after': {
borderBottomColor: theme.palette.error.main,
transform: 'scaleX(1)', // error is always underlined in red
},
Expand All @@ -67,62 +82,52 @@ export const styles = (theme) => {
}),
pointerEvents: 'none', // Transparent to the hover style.
},
'&:hover:not($disabled):before': {
'&:hover:not(.Mui-disabled):before': {
borderBottom: `2px solid ${theme.palette.text.primary}`,
// Reset on touch devices, it doesn't add specificity
'@media (hover: none)': {
borderBottom: `1px solid ${bottomLineColor}`,
},
},
'&$disabled:before': {
'&.Mui-disabled:before': {
borderBottomStyle: 'dotted',
},
},
/* Pseudo-class applied to the root element if `error={true}`. */
error: {},
/* Styles applied to the input element if `size="small"`. */
sizeSmall: {},
/* Styles applied to the root element if `multiline={true}`. */
multiline: {},
/* Styles applied to the root element if `fullWidth={true}`. */
fullWidth: {},
/* Styles applied to the input element. */
input: {},
/* Styles applied to the input element if `size="small"`. */
inputSizeSmall: {},
/* Styles applied to the input element if `multiline={true}`. */
inputMultiline: {},
/* Styles applied to the input element if `type="search"`. */
inputTypeSearch: {},
}),
};
};
});

const Input = React.forwardRef(function Input(props, ref) {
const Input = React.forwardRef(function Input(inProps, ref) {
const props = useThemeProps({ props: inProps, name: 'MuiInput' });
const {
disableUnderline,
classes,
fullWidth = false,
inputComponent = 'input',
multiline = false,
type = 'text',
...other
} = props;

const styleProps = {
...props,
fullWidth,
inputComponent,
multiline,
type,
};

const classes = useUtilityClasses(props);

return (
<InputBase
classes={{
...classes,
root: clsx(classes.root, {
[classes.underline]: !disableUnderline,
}),
underline: null,
}}
components={{ Root: InputRoot }}
componentsProps={{ root: { styleProps } }}
fullWidth={fullWidth}
inputComponent={inputComponent}
multiline={multiline}
ref={ref}
type={type}
{...other}
classes={classes}
/>
);
});
Expand Down Expand Up @@ -249,6 +254,10 @@ Input.propTypes = {
* Start `InputAdornment` for this component.
*/
startAdornment: PropTypes.node,
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx: PropTypes.object,
/**
* Type of the `input` element. It should be [a valid HTML5 input type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Form_%3Cinput%3E_types).
* @default 'text'
Expand All @@ -262,4 +271,4 @@ Input.propTypes = {

Input.muiName = 'Input';

export default withStyles(styles, { name: 'MuiInput' })(Input);
export default Input;
16 changes: 8 additions & 8 deletions packages/material-ui/src/Input/Input.test.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import * as React from 'react';
import { getClasses, createMount, describeConformance } from 'test/utils';
import { createMount, describeConformanceV5 } from 'test/utils';
import Input from './Input';
import InputBase from '../InputBase';
import classes from './inputClasses';

describe('<Input />', () => {
let classes;
const mount = createMount();

before(() => {
classes = getClasses(<Input />);
});

describeConformance(<Input />, () => ({
describeConformanceV5(<Input />, () => ({
classes,
inheritComponent: InputBase,
mount,
refInstanceof: window.HTMLDivElement,
skip: ['componentProp'],
muiName: 'MuiInput',
testDeepOverrides: { slotName: 'input', slotClassName: classes.input },
testVariantProps: { variant: 'contained', fullWidth: true },
testStateOverrides: { prop: 'size', value: 'small', styleKey: 'sizeSmall' },
skip: ['componentProp', 'componentsProp'],
}));
});
2 changes: 2 additions & 0 deletions packages/material-ui/src/Input/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export { default } from './Input';
export * from './Input';
export { default as inputClasses } from './inputClasses';
export * from './inputClasses';
2 changes: 2 additions & 0 deletions packages/material-ui/src/Input/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export { default } from './Input';
export { default as inputClasses } from './inputClasses';
export * from './inputClasses';
11 changes: 11 additions & 0 deletions packages/material-ui/src/Input/inputClasses.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface InputClasses {
root: string;
underline: string;
input: string;
}

declare const inputClasses: InputClasses;

export function getInputUtilityClass(slot: string): string;

export default inputClasses;
9 changes: 9 additions & 0 deletions packages/material-ui/src/Input/inputClasses.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { generateUtilityClasses, generateUtilityClass } from '@material-ui/unstyled';

export function getInputUtilityClass(slot) {
return generateUtilityClass('MuiInput', slot);
}

const inputClasses = generateUtilityClasses('MuiInput', ['root', 'underline', 'input']);

export default inputClasses;
5 changes: 2 additions & 3 deletions packages/material-ui/src/NativeSelect/NativeSelect.test.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import * as React from 'react';
import { expect } from 'chai';
import { getClasses, createMount, createClientRender, describeConformance } from 'test/utils';
import Input from '../Input';
import Input, { inputClasses } from '../Input';
import NativeSelect from './NativeSelect';

describe('<NativeSelect />', () => {
let classes;
let inputClasses;

const mount = createMount();
const render = createClientRender();
const defaultProps = {
Expand All @@ -23,7 +23,6 @@ describe('<NativeSelect />', () => {

before(() => {
classes = getClasses(<NativeSelect {...defaultProps} />);
inputClasses = getClasses(<Input />);
});

describeConformance(<NativeSelect {...defaultProps} />, () => ({
Expand Down
10 changes: 5 additions & 5 deletions packages/material-ui/src/TextField/TextField.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import * as React from 'react';
import { expect } from 'chai';
import { getClasses, createMount, createClientRender, describeConformance } from 'test/utils';
import FormControl from '../FormControl';
import Input from '../Input';
import OutlinedInput from '../OutlinedInput';
import TextField from './TextField';
import MenuItem from '../MenuItem';
import { inputBaseClasses } from '../InputBase';

describe('<TextField />', () => {
let classes;
Expand All @@ -32,14 +32,14 @@ describe('<TextField />', () => {
});

it('should forward the multiline prop to Input', () => {
const inputClasses = getClasses(<Input />);
const { getByRole } = render(<TextField variant="standard" multiline />);

expect(getByRole('textbox', { hidden: false })).to.have.class(inputClasses.inputMultiline);
expect(getByRole('textbox', { hidden: false })).to.have.class(
inputBaseClasses.inputMultiline,
);
});

it('should forward the fullWidth prop to Input', () => {
const inputClasses = getClasses(<Input />);
const { getByTestId } = render(
<TextField
variant="standard"
Expand All @@ -48,7 +48,7 @@ describe('<TextField />', () => {
/>,
);

expect(getByTestId('mui-input-base-root')).to.have.class(inputClasses.fullWidth);
expect(getByTestId('mui-input-base-root')).to.have.class(inputBaseClasses.fullWidth);
});
});

Expand Down