Skip to content

Commit

Permalink
SelectField initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
kybarg committed Mar 8, 2017
1 parent c8f2390 commit 850565b
Show file tree
Hide file tree
Showing 8 changed files with 316 additions and 0 deletions.
44 changes: 44 additions & 0 deletions docs/site/src/demos/select-field/SimpleSelectField.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// @flow weak

import React, { Component } from 'react';
import { createStyleSheet } from 'jss-theme-reactor';
import customPropTypes from 'material-ui/utils/customPropTypes';
import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/Menu/MenuItem';
import Icon from 'material-ui/Icon';

// const styleSheet = createStyleSheet('SimpleBadge', (theme) => ({
// badge: {
// margin: `0 ${theme.spacing.unit * 2}px`,
// },
// }));

export default class SimpleSelectField extends Component {
// const classes = context.styleManager.render(styleSheet);
state = {
value: '',
};

handleChange = (event, index, value) => this.setState({value});

render() {
return (
<div>
<SelectField
label="Sample"
value={this.state.value}
onChange={this.handleChange}
>
<MenuItem value=''>None</MenuItem>
<MenuItem value={1}>One</MenuItem>
<MenuItem value={2}>Two</MenuItem>
<MenuItem value={3}>Three</MenuItem>
</SelectField>
</div>
);
}
}

SimpleSelectField.contextTypes = {
styleManager: customPropTypes.muiRequired,
};
10 changes: 10 additions & 0 deletions docs/site/src/demos/select-field/select-field.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Select Filed

To learn more about SelectField please visit the specifications [here](https://material.io/guidelines/components/menus.html#menus-usage).

## Simple examples

**SelectField** is implemented as a controlled component, with the current selection set through the **value** property. The **SelectField** can be disabled with the **disabled** property.

{{demo='demos/select-field/SimpleSelectField.js'}}

165 changes: 165 additions & 0 deletions src/SelectField/SelectField.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// @flow weak

import React, { Component, PropTypes } from 'react';
import { createStyleSheet } from 'jss-theme-reactor';
import customPropTypes from '../utils/customPropTypes';
import TextField from '../TextField';
import SelectFieldInput from './SelectFieldInput';

import ArrowDropDownIcon from '../svg-icons/arrow-drop-down';
import FormControl from 'material-ui/Form/FormControl';
import InputLabel from 'material-ui/Input/InputLabel';
import Input from 'material-ui/Input/Input';
import Menu from 'material-ui/Menu/Menu';

// import DropDownMenu from '../DropDownMenu';

export const styleSheet = createStyleSheet('MuiButton', (theme) => {
return {
label: {
paddingLeft: 0,
},
icon: {
right: 0,
},
hideDropDownUnderline: {
borderTop: 'none',
},
dropDownMenu: {
display: 'block',
},
};
});

/**
* ```jsx
* <SelectField />
* ```
*/
class SelectField extends Component {
static propTypes = {
/**
* The `MenuItem` elements to populate the select field with.
*/
children: PropTypes.node,
/**
* If `true`, the select field will be disabled.
*/
disabled: PropTypes.bool,
/**
* The content of the label.
*/
label: PropTypes.node,
/** @ignore */
onBlur: PropTypes.func,
/**
* Callback function fired when a menu item is selected.
*
* @param {object} event TouchTap event targeting the menu item
* that was selected.
* @param {number} key The index of the selected menu item.
* @param {any} payload The `value` prop of the selected menu item.
*/
onChange: PropTypes.func,
/** @ignore */
onFocus: PropTypes.func,
/**
* The value that is currently selected.
*/
value: PropTypes.any,
};

static defaultProps = {
disabled: false,
};

static contextTypes = {
styleManager: customPropTypes.muiRequired,
};

state = {
anchorEl: undefined,
open: false,
selectedIndex: undefined,
};

handleMouseDown = (event) => event.preventDefault();

handleClick = (event) => this.setState({ open: true, anchorEl: event.currentTarget });

handleRequestClose = () => this.setState({ open: false })

handleItemClick = (event, index, value) => {
event.persist();
this.setState({
open: false,
}, () => {
if (this.props.onChange) {
this.props.onChange(event, index, value);
}

// this.close(Events.isKeyboard(event));
});
};

render() {
const {
children,
className,
disabled,
error,
inputClassName,
inputProps,
label,
labelClassName,
menuProps,
required,
type,
value,
...other
} = this.props;

const classes = this.context.styleManager.render(styleSheet);

return (
<FormControl
className={className}
error={error}
required={required}
{...other}
>
{label && (
<InputLabel className={labelClassName} shrink={value && true}>
{label}
</InputLabel>
)}
<Input
className={inputClassName}
value={value}
type={type}
disabled={disabled}
onMouseDown={this.handleMouseDown}
onClick={this.handleClick}
component={SelectFieldInput}
options={children}
{...inputProps}
/>
<Menu
anchorEl={this.state.anchorEl}
open={this.state.open}
onRequestClose={this.handleRequestClose}
{...menuProps}
>
{React.Children.map(children, (child, index) =>
React.cloneElement(child, {
selected: value === child.props.value,
onClick : (event) => this.handleItemClick(event, index, child.props.value)
})
)}
</Menu>
</FormControl>
);
}
}

export default SelectField;
24 changes: 24 additions & 0 deletions src/SelectField/SelectField.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* eslint-env mocha */
import React, {PropTypes} from 'react';
import {mount} from 'enzyme';
import {assert} from 'chai';
import getMuiTheme from '../styles/getMuiTheme';
import SelectField from './SelectField';
import TouchRipple from '../internal/TouchRipple';

describe('<SelectField />', () => {
const muiTheme = getMuiTheme();
const mountWithContext = (node) => mount(node, {
context: {muiTheme},
childContextTypes: {muiTheme: PropTypes.object},
});

describe('prop: disabled', () => {
it('disables the ripple effect', () => {
const wrapper = mountWithContext(
<SelectField disabled={true} />
);
assert.strictEqual(wrapper.find(TouchRipple).length, 0, 'should not contain a TouchRipple');
});
});
});
53 changes: 53 additions & 0 deletions src/SelectField/SelectFieldInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// @flow weak

import React, { Component, PropTypes } from 'react';
import { createStyleSheet } from 'jss-theme-reactor';
import classNames from 'classnames'
import customPropTypes from '../utils/customPropTypes';
import Input from 'material-ui/Input';
import ArrowDropDownIcon from '../svg-icons/arrow-drop-down';

const styleSheet = createStyleSheet('MuiSelectFieldInput', (theme) => {
return {
root: {
minWidth: 182
},
select: {
cursor: 'pointer',
position: 'relative',
zIndex: 2,
},
icon: {
color: theme.palette.text.secondary,
position: 'absolute',
right: 0,
top: 4,
zIndex: 1,
},
}
})

export default class SelectFieldInput extends Component {
static contextTypes = {
styleManager: customPropTypes.muiRequired,
};

render() {
const classes = this.context.styleManager.render(styleSheet)
console.log(this.props)
return (
<div className={classes.root}>
<select {...this.props} className={classNames(this.props.className, classes.select)}>
{React.Children.map(this.props.options, (option, index) =>
React.createElement('option', {
key: index,
value: option.props.value,
children: option.props.value && option.props.children,
})
)}
</select>
<ArrowDropDownIcon className={classes.icon} />
</div>
);
}
}
1 change: 1 addition & 0 deletions src/SelectField/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default from './SelectField';
4 changes: 4 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ export {
TouchRipple,
} from './Ripple';

export {
SelectField,
} from './SelectField';

export {
MuiThemeProvider,
} from './styles';
Expand Down
15 changes: 15 additions & 0 deletions src/svg-icons/arrow-drop-down.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* eslint-disable */

import React from 'react';
import pure from 'recompose/pure';
import SvgIcon from '../SvgIcon';

let ArrowDropDown = (props) => (
<SvgIcon {...props}>
<path d="M7,10L12,15L17,10H7Z"/>
</SvgIcon>
);
ArrowDropDown = pure(ArrowDropDown);
ArrowDropDown.muiName = 'SvgIcon';

export default ArrowDropDown;

0 comments on commit 850565b

Please sign in to comment.