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

fixes plotly/dash-core_components#169 #277

Closed
wants to merge 13 commits into from
Closed
Changes from 10 commits
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
131 changes: 110 additions & 21 deletions src/components/Input.react.js
Expand Up @@ -2,6 +2,21 @@ import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {omit} from 'ramda';

/**
* A function to handle the formating of the `value` prop of the react `input`.
* @param {string} val the `value` prop of the react `input` component.
* @param {string} type the `type` prop of the react `input` component.
* @returns {string} a value formated as a number if `type` is equal to `number`
*/
function formatValue(val, type) {
if (!val === '') {
if (type === 'number') {
return Number(val);
}
}
return val;
}

/**
* A basic HTML input control for entering text, numbers, or passwords.
*
Expand All @@ -12,39 +27,82 @@ import {omit} from 'ramda';
export default class Input extends Component {
constructor(props) {
super(props);
this.state = {value: props.value};
this.state = {
value: formatValue(props.value, props.type),
};
}

componentWillReceiveProps(nextProps) {
this.setState({value: nextProps.value});
this.setState({value: formatValue(nextProps.value, nextProps.type)});
}

render() {
const {
fireEvent,
setProps,
type
} = this.props;
const {fireEvent, setProps, type, updatemode} = this.props;

const {value} = this.state;
return (
<input
onBlur={() => {
const newValue = formatValue(this.state.value, type);
if (updatemode === 'blur') {
Copy link
Contributor

Choose a reason for hiding this comment

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

Here, if updatemode is an array (i.e. ['blur', 'enter']) this will not trigger. The best way to do this would probably be to first see if updatemode is an array (you could use Ramda's is function for that, something like R.is(Array, updatemode)) and then check if that array contains blur (Ramda has a contains function that can do that easily)

Copy link
Contributor

Choose a reason for hiding this comment

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

Or maybe even if (updatemode === 'blur' || R.contains('blur', updatemode)) {} will work!

Copy link
Author

Choose a reason for hiding this comment

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

actually I implemented with updatemode being blur (update when pressing tab or clicking away) or keypress (update at everykeystrokes), I wasn't sure the if we really need to have ['blur', 'enter'] but I kept it in the Input.Props

this.setState({value: newValue});
if (setProps) {
setProps({value: newValue});
}
if (fireEvent) {
fireEvent({event: 'blur'});
}
}
}}
onChange={e => {
this.setState({value: e.target.value});
if (setProps) {
if (type === 'number') {
setProps({value: Number(e.target.value)});
const newValue = formatValue(e.target.value, type);
this.setState({value: newValue});
if (updatemode === 'keypress') {
Copy link
Contributor

Choose a reason for hiding this comment

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

Similarly here - this if only works if updatemode is a string, not when it's an array.

if (setProps) {
setProps({value: newValue});
}
}
if (fireEvent) {
fireEvent({event: 'change'});
}
}}
onKeyPress={e => {
if (type === 'number') {
const curVal = this.state.value;
const keyCode = e.keyCode || e.which;
const keyValue = String.fromCharCode(keyCode);
const unicodePlus = 43;
const unicodeNine = 57;
if (
keyCode <= unicodePlus ||
keyCode > unicodeNine ||
keyValue === '/'
) {
e.preventDefault();
}
else {
setProps({value: e.target.value});
if (keyValue === '-' && curVal.length > 0) {
e.preventDefault();
}
if (
(keyValue === '.' || keyValue === ',') &&
/[.]|,/.test(curVal.value)
) {
e.preventDefault();
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you explain a bit about what this code is supposed to do? Is it really necessary?

Copy link
Author

Choose a reason for hiding this comment

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

This rejects the onKeyPress event and the onChange event is therefore not triggered. This is used to make sure the user can only press on 0-9, dot and comma and that the user cannot input Something like 0.122.,233,44.4, it prevents the use of another dot or comma when either one of them is already present

if (fireEvent) {fireEvent({event: 'change'});}
}}
onBlur={() => {
if (fireEvent) {fireEvent({event: 'blur'});}
onClick={e => {
const newValue = formatValue(e.target.value, type);
this.setState({value: newValue});
if (setProps) {
setProps({value: newValue});
}
}}
value={value}
{...omit(['fireEvent', 'setProps', 'value'], this.props)}
{...omit(
['fireEvent', 'setProps', 'value', 'updatemode'],
this.props
)}
/>
);
}
Expand Down Expand Up @@ -78,7 +136,15 @@ Input.propTypes = {
*/
type: PropTypes.oneOf([
// Only allowing the input types with wide browser compatability
'text', 'number', 'password', 'email', 'range', 'search', 'tel', 'url', 'hidden'
'text',
'number',
'password',
'email',
'range',
'search',
'tel',
'url',
'hidden',
]),

/**
Expand Down Expand Up @@ -150,7 +216,7 @@ Input.propTypes = {
/**
* URL input. Use type="url" if possible instead.
*/
'url'
'url',
]),

/**
Expand All @@ -173,7 +239,6 @@ Input.propTypes = {
*/
maxlength: PropTypes.string,


/**
* The minimum (numeric or date-time) value for this item, which must not be greater than its maximum (max attribute) value.
*/
Expand Down Expand Up @@ -244,6 +309,25 @@ Input.propTypes = {
*/
step: PropTypes.string,

/**
* Sets the number of important digits. It can be the string any or a positive floating point number. If this attribute is not set to any, the default is zero.
*/
precision: PropTypes.string,

/**
* Determines when the component should update
* its value. If `keypress`, then the input
* will trigger its value when the user presses on
* a key while the selector is active inside the input.
* If `blur`, then the input will trigger its value when
* the user click outside the input zone or press tab.
* If `enter` ...
*/
updatemode: PropTypes.oneOfType([
PropTypes.oneOf(['keypress', 'blur', 'enter']),
PropTypes.arrayOf(PropTypes.oneOf(['keypress', 'blur', 'enter'])),
]),

/**
* Dash-assigned callback that gets fired when the input changes.
*/
Expand All @@ -254,5 +338,10 @@ Input.propTypes = {
*/
setProps: PropTypes.func,

dashEvents: PropTypes.oneOf(['blur', 'change'])
dashEvents: PropTypes.oneOf(['blur', 'change']),
};

Input.defaultProps = {
type: 'text',
updatemode: 'keypress',
};