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

Refs #471: Introducing field clearing button. #472

Closed
wants to merge 10 commits into from
2 changes: 1 addition & 1 deletion playground/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>react-jsonschema-form playground</title>
<link rel="stylesheet" id="theme" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<link rel="stylesheet" id="theme" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="//cdn.polyfill.io/v2/polyfill.min.js"></script>
</head>
<body>
Expand Down
2 changes: 1 addition & 1 deletion playground/index.prod.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>react-jsonschema-form playground</title>
<link rel="stylesheet" id="theme" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<link rel="stylesheet" id="theme" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="./styles.css">
<script src="//cdn.polyfill.io/v2/polyfill.min.js"></script>
</head>
Expand Down
3 changes: 2 additions & 1 deletion playground/samples/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ module.exports = {
type: "string",
title: "First name",
minLength: 8,
pattern: "\\d+"
pattern: "\\d+",
"description": "Note: this field isn't required, but will be validated for length and pattern as soon as a value is entered.",
},
active: {
type: "boolean",
Expand Down
1 change: 1 addition & 0 deletions src/components/widgets/AltDateWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ function DateElement(props) {
const {SelectWidget} = registry.widgets;
return (
<SelectWidget
clearable={false}
schema={{type: "integer"}}
id={id}
className="form-control"
Expand Down
31 changes: 24 additions & 7 deletions src/components/widgets/BaseInput.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,54 @@
import React, {PropTypes} from "react";

import ClearableWidget from "./ClearableWidget";


function BaseInput(props) {
// Note: since React 15.2.0 we can't forward unknown element attributes, so we
// exclude the "options" and "schema" ones here.
// exclude some props like "options" and "schema" here.
const {
value,
clearable,
disabled,
readonly,
autofocus,
onBlur,
options, // eslint-disable-line
schema, // eslint-disable-line
formContext, // eslint-disable-line
registry, // eslint-disable-line
options, // eslint-disable-line
schema, // eslint-disable-line
formContext, // eslint-disable-line
registry, // eslint-disable-line
onChange,
...inputProps
} = props;
const _onChange = ({target: {value}}) => {
return props.onChange(value === "" ? undefined : value);
return onChange(value);
};
return (
const input = (
<input
{...inputProps}
className="form-control"
readOnly={readonly}
disabled={disabled}
autoFocus={autofocus}
value={typeof value === "undefined" ? "" : value}
onChange={_onChange}
onBlur={onBlur && (event => onBlur(inputProps.id, event.target.value))}/>
);
return !clearable ? input : (
<ClearableWidget
onChange={onChange}
disabled={disabled}
readonly={readonly}
value={value}>
{input}
</ClearableWidget>
);
}

BaseInput.defaultProps = {
type: "text",
required: false,
clearable: true,
disabled: false,
readonly: false,
autofocus: false,
Expand All @@ -44,6 +60,7 @@ if (process.env.NODE_ENV !== "production") {
placeholder: PropTypes.string,
value: PropTypes.any,
required: PropTypes.bool,
clearable: PropTypes.bool,
disabled: PropTypes.bool,
readonly: PropTypes.bool,
autofocus: PropTypes.bool,
Expand Down
35 changes: 23 additions & 12 deletions src/components/widgets/CheckboxWidget.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,40 @@
import React, {PropTypes} from "react";

import ClearableWidget from "./ClearableWidget";


function CheckboxWidget({
schema,
id,
value,
required,
readonly,
disabled,
label,
autofocus,
onChange,
}) {
return (
<div className={`checkbox ${disabled ? "disabled" : ""}`}>
<label>
<input type="checkbox"
id={id}
checked={typeof value === "undefined" ? false : value}
required={required}
disabled={disabled}
autoFocus={autofocus}
onChange={(event) => onChange(event.target.checked)}/>
<span>{label}</span>
</label>
</div>
<ClearableWidget
onChange={onChange}
disabled={disabled}
readonly={readonly}
value={value}>
<div className="form-control">
<div className={`checkbox ${disabled ? "disabled" : ""}`} style={{margin: "inherit"}}>
<label>
<input type="checkbox"
id={id}
checked={typeof value === "undefined" ? false : value}
required={required}
disabled={disabled}
autoFocus={autofocus}
onChange={(event) => onChange(event.target.checked)}/>
<span>{label}</span>
</label>
</div>
</div>
</ClearableWidget>
);
}

Expand Down
77 changes: 43 additions & 34 deletions src/components/widgets/CheckboxesWidget.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, {PropTypes} from "react";

import ClearableWidget from "./ClearableWidget";


function selectValue(value, selected, all) {
const at = all.indexOf(value);
Expand All @@ -14,44 +16,51 @@ function deselectValue(value, selected) {
}

function CheckboxesWidget(props) {
const {id, disabled, options, value, autofocus, onChange} = props;
const {id, disabled, readonly, options, value, autofocus, onChange} = props;
const {enumOptions, inline} = options;
return (
<div className="checkboxes" id={id}>{
enumOptions.map((option, index) => {
const checked = value.indexOf(option.value) !== -1;
const disabledCls = disabled ? "disabled" : "";
const checkbox = (
<span>
<input type="checkbox"
id={`${id}_${index}`}
checked={checked}
disabled={disabled}
autoFocus={autofocus && index === 0}
onChange={(event) => {
const all = enumOptions.map(({value}) => value);
if (event.target.checked) {
onChange(selectValue(option.value, value, all));
} else {
onChange(deselectValue(option.value, value));
}
}}/>
<span>{option.label}</span>
</span>
);
return inline ? (
<label key={index} className={`checkbox-inline ${disabledCls}`}>
{checkbox}
</label>
) : (
<div key={index} className={`checkbox ${disabledCls}`}>
<label>
<ClearableWidget
onChange={onChange}
disabled={disabled}
readonly={readonly}
value={value}>
<div id={id} className="checkboxes form-control"
style={{paddingTop: 0, paddingBottom: 0, float: "none"}}>{
enumOptions.map((option, index) => {
const checked = value.indexOf(option.value) !== -1;
const disabledCls = disabled ? "disabled" : "";
const checkbox = (
<span>
<input type="checkbox"
id={`${id}_${index}`}
checked={checked}
disabled={disabled}
autoFocus={autofocus && index === 0}
onChange={(event) => {
const all = enumOptions.map(({value}) => value);
if (event.target.checked) {
onChange(selectValue(option.value, value, all));
} else {
onChange(deselectValue(option.value, value));
}
}}/>
<span>{option.label}</span>
</span>
);
return inline ? (
<label key={index} className={`checkbox-inline ${disabledCls}`}>
{checkbox}
</label>
</div>
);
})
}</div>
) : (
<div key={index} className={`checkbox ${disabledCls}`}>
<label>
{checkbox}
</label>
</div>
);
})
}</div>
</ClearableWidget>
);
}

Expand Down
45 changes: 45 additions & 0 deletions src/components/widgets/ClearableWidget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, {PropTypes} from "react";


function ClearableWidget({onChange, disabled, readonly, value, children}) {
const _onClear = (event) => {
event.preventDefault();
if (typeof value !== "undefined") {
return onChange(undefined);
}
};
const cleared = typeof value === "undefined";
const clearBtnCls = "glyphicon glyphicon-remove-sign clear-btn";
const clearBtnStyles = {
pointerEvents: "auto",
textDecoration: "none",
cursor: cleared ? "no-drop" : "pointer",
color: cleared ? "#aaa" : "#888",
};
return disabled || readonly ? children : (
<div className="input-group">
{children}
<div className="input-group-addon">
<a className={clearBtnCls} onClick={_onClear} style={clearBtnStyles}
title="Reset field value"/>
</div>
</div>
);
}

ClearableWidget.defaultProps = {
disabled: false,
readonly: false,
};

if (process.env.NODE_ENV !== "production") {
ClearableWidget.propTypes = {
children: React.PropTypes.element.isRequired,
onChange: PropTypes.func,
value: PropTypes.any,
disabled: PropTypes.bool,
readonly: PropTypes.bool,
};
}

export default ClearableWidget;
2 changes: 2 additions & 0 deletions src/components/widgets/DateTimeWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ function toJSONDate(dateString) {

function DateTimeWidget(props) {
const {value, onChange} = props;
// Note: native HTML date widgets are already clearable.
return (
<BaseInput
type="datetime-local"
{...props}
clearable={false}
value={fromJSONDate(value)}
onChange={(value) => onChange(toJSONDate(value))}/>
);
Expand Down
2 changes: 2 additions & 0 deletions src/components/widgets/DateWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import BaseInput from "./BaseInput";

function DateWidget(props) {
const {onChange} = props;
// Note: native HTML date widgets are already clearable.
return (
<BaseInput
type="date"
{...props}
clearable={false}
onChange={(value) => onChange(value || undefined)}/>
);
}
Expand Down
Loading