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 InputAdornment to emotion #25279

Merged
merged 12 commits into from
Mar 14, 2021
3 changes: 2 additions & 1 deletion docs/pages/api-docs/input-adornment.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"disablePointerEvents": { "type": { "name": "bool" } },
"disableTypography": { "type": { "name": "bool" } },
"position": { "type": { "name": "enum", "description": "'end'<br>&#124;&nbsp;'start'" } },
"sx": { "type": { "name": "object" } },
"variant": {
"type": {
"name": "enum",
Expand All @@ -32,6 +33,6 @@
"filename": "/packages/material-ui/src/InputAdornment/InputAdornment.js",
"inheritance": null,
"demos": "<ul><li><a href=\"/components/text-fields/\">Text Fields</a></li></ul>",
"styledComponent": false,
"styledComponent": true,
"cssComponent": false
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"disablePointerEvents": "Disable pointer events on the root. This allows for the content of the adornment to focus the <code>input</code> on click.",
"disableTypography": "If children is a string then disable wrapping in a Typography component.",
"position": "The position this adornment should appear relative to the <code>Input</code>.",
"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.",
"variant": "The variant to use. Note: If you are using the <code>TextField</code> component or the <code>FormControl</code> component you do not have to set this manually."
},
"classDescriptions": {
Expand Down
6 changes: 3 additions & 3 deletions packages/material-ui/src/ButtonGroup/ButtonGroup.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ const ButtonGroup = React.forwardRef(function ButtonGroup(inProps, ref) {
children,
className,
color = 'primary',
component: Component = 'div',
component = 'div',
disabled = false,
disableElevation = false,
disableFocusRipple = false,
Expand All @@ -206,7 +206,7 @@ const ButtonGroup = React.forwardRef(function ButtonGroup(inProps, ref) {
const styleProps = {
...props,
color,
component: Component,
component,
disabled,
disableElevation,
disableFocusRipple,
Expand All @@ -221,7 +221,7 @@ const ButtonGroup = React.forwardRef(function ButtonGroup(inProps, ref) {

return (
<ButtonGroupRoot
as={Component}
as={component}
role="group"
className={clsx(classes.root, className)}
ref={ref}
Expand Down
6 changes: 6 additions & 0 deletions packages/material-ui/src/InputAdornment/InputAdornment.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as React from 'react';
import { SxProps } from '@material-ui/system';
import { OverridableComponent, OverrideProps } from '../OverridableComponent';
import { Theme } from '..';

export interface InputAdornmentTypeMap<P = {}, D extends React.ElementType = 'div'> {
props: P & {
Expand Down Expand Up @@ -41,6 +43,10 @@ export interface InputAdornmentTypeMap<P = {}, D extends React.ElementType = 'di
* The position this adornment should appear relative to the `Input`.
*/
position?: 'start' | 'end';
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx?: SxProps<Theme>;
/**
* The variant to use.
* Note: If you are using the `TextField` component or the `FormControl` component
Expand Down
132 changes: 86 additions & 46 deletions packages/material-ui/src/InputAdornment/InputAdornment.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,99 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { deepmerge } from '@material-ui/utils';
import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled';
import capitalize from '../utils/capitalize';
import Typography from '../Typography';
import withStyles from '../styles/withStyles';
import FormControlContext, { useFormControl } from '../FormControl/FormControlContext';
import experimentalStyled from '../styles/experimentalStyled';
import inputAdornmentClasses, { getInputAdornmentUtilityClass } from './inputAdornmentClasses';
import useThemeProps from '../styles/useThemeProps';

export const styles = (theme) => ({
/* Styles applied to the root element. */
root: {
display: 'flex',
height: '0.01em', // Fix IE11 flexbox alignment. To remove at some point.
maxHeight: '2em',
alignItems: 'center',
whiteSpace: 'nowrap',
color: theme.palette.action.active,
const overridesResolver = (props, styles) => {
const { styleProps } = props;

return deepmerge(
{
...styles[`position${capitalize(styleProps.position)}`],
...(styleProps.disablePointerEvents === true && styles.disablePointerEvents),
...(styleProps.variant === 'filled' && styles.filled),
},
styles.root || {},
);
};

const useUtilityClasses = (styleProps) => {
const { classes, disablePointerEvents, position, variant } = styleProps;
const slots = {
root: [
'root',
disablePointerEvents && 'disablePointerEvents',
position && `position${capitalize(position)}`,
variant,
'hiddenLabel',
'sizeSmall',
],
};

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

const InputAdornmentRoot = experimentalStyled(
'div',
{},
{
name: 'MuiInputAdornment',
slot: 'Root',
overridesResolver,
},
/* Styles applied to the root element if `variant="filled"`. */
filled: {
'&$positionStart:not($hiddenLabel)': {
)(({ theme, styleProps }) => ({
display: 'flex',
height: '0.01em', // Fix IE11 flexbox alignment. To remove at some point.
maxHeight: '2em',
alignItems: 'center',
whiteSpace: 'nowrap',
color: theme.palette.action.active,
...(styleProps.variant === 'filled' && {
// Styles applied to the root element if `variant="filled"`.
[`&.${inputAdornmentClasses.positionStart}&:not(.Mui-hiddenLabel)`]: {
marginTop: 16,
},
},
/* Styles applied to the root element if `position="start"`. */
positionStart: {
}),
...(styleProps.position === 'start' && {
// Styles applied to the root element if `position="start"`.
marginRight: 8,
},
/* Styles applied to the root element if `position="end"`. */
positionEnd: {
}),
...(styleProps.position === 'end' && {
// Styles applied to the root element if `position="end"`.
marginLeft: 8,
},
/* Styles applied to the root element if `disablePointerEvents={true}`. */
disablePointerEvents: {
}),
...(styleProps.disablePointerEvents === true && {
// Styles applied to the root element if `disablePointerEvents={true}`.
pointerEvents: 'none',
},
/* Styles applied if the adornment is used inside <FormControl hiddenLabel />. */
hiddenLabel: {},
/* Styles applied if the adornment is used inside <FormControl size="small" />. */
sizeSmall: {},
});
}),
}));

const InputAdornment = React.forwardRef(function InputAdornment(props, ref) {
const InputAdornment = React.forwardRef(function InputAdornment(inProps, ref) {
const props = useThemeProps({ props: inProps, name: 'MuiInputAdornment' });
const {
children,
classes,
className,
component: Component = 'div',
component = 'div',
disablePointerEvents = false,
disableTypography = false,
position,
variant: variantProp,
...other
} = props;

const styleProps = {
...props,
disablePointerEvents,
position,
variant: variantProp,
};

const muiFormControl = useFormControl() || {};

let variant = variantProp;
Expand All @@ -68,23 +111,16 @@ const InputAdornment = React.forwardRef(function InputAdornment(props, ref) {

if (muiFormControl && !variant) {
variant = muiFormControl.variant;
styleProps.variant = variant;
}
const classes = useUtilityClasses(styleProps);

return (
<FormControlContext.Provider value={null}>
<Component
className={clsx(
classes.root,
{
[classes.filled]: variant === 'filled',
[classes.positionStart]: position === 'start',
[classes.positionEnd]: position === 'end',
[classes.disablePointerEvents]: disablePointerEvents,
[classes.sizeSmall]: muiFormControl.size === 'small',
[classes.hiddenLabel]: muiFormControl.hiddenLabel,
},
className,
)}
<InputAdornmentRoot
as={component}
styleProps={styleProps}
className={clsx(classes.root, className)}
ref={ref}
{...other}
>
Expand All @@ -101,7 +137,7 @@ const InputAdornment = React.forwardRef(function InputAdornment(props, ref) {
{children}
</React.Fragment>
)}
</Component>
</InputAdornmentRoot>
</FormControlContext.Provider>
);
});
Expand Down Expand Up @@ -143,6 +179,10 @@ InputAdornment.propTypes /* remove-proptypes */ = {
* The position this adornment should appear relative to the `Input`.
*/
position: PropTypes.oneOf(['end', 'start']),
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx: PropTypes.object,
/**
* The variant to use.
* Note: If you are using the `TextField` component or the `FormControl` component
Expand All @@ -151,4 +191,4 @@ InputAdornment.propTypes /* remove-proptypes */ = {
variant: PropTypes.oneOf(['filled', 'outlined', 'standard']),
};

export default withStyles(styles, { name: 'MuiInputAdornment' })(InputAdornment);
export default InputAdornment;
23 changes: 11 additions & 12 deletions packages/material-ui/src/InputAdornment/InputAdornment.test.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
import * as React from 'react';
import { expect } from 'chai';
import { getClasses, createMount, createClientRender, describeConformance } from 'test/utils';
import { typographyClasses } from '../Typography';
import InputAdornment from './InputAdornment';
import TextField from '../TextField';
import FormControl from '../FormControl';
import Input from '../Input';
import { createMount, createClientRender, describeConformanceV5 } from 'test/utils';
import { typographyClasses } from '@material-ui/core/Typography';
import InputAdornment, { inputAdornmentClasses as classes } from '@material-ui/core/InputAdornment';
import TextField from '@material-ui/core/TextField';
import FormControl from '@material-ui/core/FormControl';
import Input from '@material-ui/core/Input';

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

before(() => {
classes = getClasses(<InputAdornment position="start">foo</InputAdornment>);
});

describeConformance(<InputAdornment position="start">foo</InputAdornment>, () => ({
describeConformanceV5(<InputAdornment position="start">foo</InputAdornment>, () => ({
classes,
inheritComponent: 'div',
mount,
render,
muiName: 'MuiInputAdornment',
testVariantProps: { color: 'primary' },
refInstanceof: window.HTMLDivElement,
skip: ['componentsProp'],
testComponentPropWith: 'span',
}));

Expand Down
5 changes: 4 additions & 1 deletion packages/material-ui/src/InputAdornment/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export { default } from './InputAdornment';
export * from './InputAdornment';
export { default } from './InputAdornment';

export { default as inputAdornmentClasses } from './inputAdornmentClasses';
export * from './inputAdornmentClasses';
3 changes: 3 additions & 0 deletions packages/material-ui/src/InputAdornment/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
export { default } from './InputAdornment';

export { default as inputAdornmentClasses } from './inputAdornmentClasses';
export * from './inputAdornmentClasses';
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { InputAdornmentClassKey } from './InputAdornment';

export type InputAdornmentClasses = Record<InputAdornmentClassKey, string>;

declare const inputAdornmentClasses: InputAdornmentClasses;

export function getInputAdornmentUtilityClass(slot: string): string;

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

export function getInputAdornmentUtilityClass(slot) {
return generateUtilityClass('MuiInputAdornment', slot);
}

const inputAdornmentClasses = generateUtilityClasses('MuiInputAdornment', [
'root',
'filled',
'positionStart',
'positionEnd',
'disablePointerEvents',
'hiddenLabel',
'sizeSmall',
]);

export default inputAdornmentClasses;