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

[InputAction] Initial implementation of a InputAction component #8365

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 13 additions & 1 deletion docs/src/pages/demos/text-fields/ComposedTextField.js
Expand Up @@ -3,8 +3,10 @@
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from 'material-ui/styles';
import Input, { InputLabel } from 'material-ui/Input';
import IconButton from 'material-ui/IconButton';
import Input, { InputLabel, InputAction } from 'material-ui/Input';
import { FormControl, FormHelperText } from 'material-ui/Form';
import DeleteIcon from 'material-ui-icons/Delete';

const styles = theme => ({
container: {
Expand Down Expand Up @@ -49,6 +51,16 @@ class ComposedTextField extends React.Component {
<Input id="name-error" value={this.state.name} onChange={this.handleChange} />
<FormHelperText>Error</FormHelperText>
</FormControl>
<FormControl className={classes.formControl}>
<InputLabel htmlFor="name-error">Name</InputLabel>
<Input id="name-error" value={this.state.name} onChange={this.handleChange} />
<InputAction>
<IconButton>
<DeleteIcon />
</IconButton>
</InputAction>
<FormHelperText>Input as an action</FormHelperText>
</FormControl>
</div>
);
}
Expand Down
6 changes: 5 additions & 1 deletion src/Form/FormControl.js
Expand Up @@ -125,15 +125,19 @@ class FormControl extends React.Component<DefaultProps & Props, State> {
};

getChildContext() {
const { disabled, error, required, margin } = this.props;
const { disabled, error, required, margin, children: childrenProp } = this.props;
const { dirty, focused } = this.state;

const children = React.Children.toArray(childrenProp);
const hasInputAction = children && children.some(value => isMuiElement(value, ['InputAction']));

return {
muiFormControl: {
dirty,
disabled,
error,
focused,
hasInputAction,
margin,
required,
onDirty: this.handleDirty,
Expand Down
7 changes: 7 additions & 0 deletions src/Input/Input.js
Expand Up @@ -183,6 +183,9 @@ export const styles = (theme: Object) => {
fullWidth: {
width: '100%',
},
inputAction: {
paddingRight: theme.spacing.unit * 3,
},
};
};

Expand Down Expand Up @@ -461,6 +464,7 @@ class Input extends React.Component<DefaultProps & Props, State> {
let disabled = disabledProp;
let error = errorProp;
let margin = marginProp;
let hasInputAction = false;

if (muiFormControl) {
if (typeof disabled === 'undefined') {
Expand All @@ -474,6 +478,8 @@ class Input extends React.Component<DefaultProps & Props, State> {
if (typeof margin === 'undefined') {
margin = muiFormControl.margin;
}

hasInputAction = muiFormControl.hasInputAction;
}

const className = classNames(
Expand All @@ -485,6 +491,7 @@ class Input extends React.Component<DefaultProps & Props, State> {
[classes.focused]: this.state.focused,
[classes.formControl]: muiFormControl,
[classes.inkbar]: !disableUnderline,
[classes.inputAction]: hasInputAction,
[classes.multiline]: multiline,
[classes.underline]: !disableUnderline,
},
Expand Down
7 changes: 7 additions & 0 deletions src/Input/InputAction.d.ts
@@ -0,0 +1,7 @@
import { StyledComponent } from "..";

export interface InputActionProps {
component?: React.ReactType;
}

export default class InputAction extends StyledComponent<InputActionProps> {}
53 changes: 53 additions & 0 deletions src/Input/InputAction.js
@@ -0,0 +1,53 @@
// @flow weak

import React from 'react';
import type { Node, ElementType } from 'react';
import classNames from 'classnames';
import withStyles from '../styles/withStyles';

export const styles = (theme: Object) => ({
root: {
position: 'absolute',
right: -theme.spacing.unit * 2,
top: theme.spacing.unit,
},
});

type Default = {
classes: Object,
};

export type Props = {
/**
* The content of the component, normally an `IconButton`.
*/
children?: Node,
/**
* Useful to extend the style applied to components.
*/
classes?: Object,
/**
* @ignore
*/
className?: string,
/**
*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing

*/
component?: ElementType,
};

function InputAction(props: Default & Props) {
const { children, component, classes, className, ...other } = props;

const Component = component || 'div';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React provides a mechanism for default, let's use it, it automatically added in the docs.


return (
<Component {...other} className={classNames(classes.root, className)}>
{children}
</Component>
);
}

InputAction.muiName = 'InputAction';

export default withStyles(styles, { name: 'MuiInputAction' })(InputAction);
38 changes: 38 additions & 0 deletions src/Input/InputAction.spec.js
@@ -0,0 +1,38 @@
// @flow

import React from 'react';
import { assert } from 'chai';
import { createShallow, getClasses } from '../test-utils';
import InputAction from './InputAction';

describe('<InputAction />', () => {
let shallow;
let classes;

before(() => {
shallow = createShallow({ untilSelector: InputAction });
classes = getClasses(<InputAction />);
});

it('should render a div', () => {
const wrapper = shallow(<InputAction />);
assert.strictEqual(wrapper.name(), 'div');
assert.strictEqual(wrapper.hasClass(classes.root), true);
});

it('should render with the user and root classes', () => {
const wrapper = shallow(<InputAction className="woofInputAction" />);
assert.strictEqual(wrapper.hasClass('woofInputAction'), true);
assert.strictEqual(wrapper.hasClass(classes.root), true);
});

it('should render with the user and root classes', () => {
const wrapper = shallow(<InputAction other="woofInputAction" />);
assert.strictEqual(wrapper.prop('other'), 'woofInputAction');
});

it('should render Chidren', () => {
const wrapper = shallow(<InputAction>Foo</InputAction>);
assert.strictEqual(wrapper.childAt(0).node, 'Foo');
});
});
1 change: 1 addition & 0 deletions src/Input/index.js
@@ -1,4 +1,5 @@
// @flow

export { default } from './Input';
export { default as InputAction } from './InputAction';
export { default as InputLabel } from './InputLabel';