mirror of https://github.com/apache/superset.git
Better looking checkboxes (#3345)
Also showing icon only on hover on control headers
This commit is contained in:
parent
e79adbbc5f
commit
254645773c
|
@ -9,15 +9,19 @@ const propTypes = {
|
||||||
icon: PropTypes.string,
|
icon: PropTypes.string,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
|
placement: PropTypes.string,
|
||||||
};
|
};
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
icon: 'question-circle-o',
|
icon: 'info-circle',
|
||||||
|
className: 'text-muted',
|
||||||
|
placement: 'right',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function InfoTooltipWithTrigger({ label, tooltip, icon, className, onClick }) {
|
export default function InfoTooltipWithTrigger({
|
||||||
|
label, tooltip, icon, className, onClick, placement }) {
|
||||||
return (
|
return (
|
||||||
<OverlayTrigger
|
<OverlayTrigger
|
||||||
placement="right"
|
placement={placement}
|
||||||
overlay={<Tooltip id={`${slugify(label)}-tooltip`}>{tooltip}</Tooltip>}
|
overlay={<Tooltip id={`${slugify(label)}-tooltip`}>{tooltip}</Tooltip>}
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
|
|
|
@ -56,6 +56,7 @@ const defaultProps = {
|
||||||
export default class Control extends React.PureComponent {
|
export default class Control extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
this.state = { hovered: false };
|
||||||
this.validate = this.validate.bind(this);
|
this.validate = this.validate.bind(this);
|
||||||
this.onChange = this.onChange.bind(this);
|
this.onChange = this.onChange.bind(this);
|
||||||
}
|
}
|
||||||
|
@ -65,6 +66,9 @@ export default class Control extends React.PureComponent {
|
||||||
onChange(value, errors) {
|
onChange(value, errors) {
|
||||||
this.validateAndSetValue(value, errors);
|
this.validateAndSetValue(value, errors);
|
||||||
}
|
}
|
||||||
|
setHover(hovered) {
|
||||||
|
this.setState({ hovered });
|
||||||
|
}
|
||||||
validateAndSetValue(value, errors) {
|
validateAndSetValue(value, errors) {
|
||||||
let validationErrors = this.props.validationErrors;
|
let validationErrors = this.props.validationErrors;
|
||||||
let currentErrors = this.validate(value);
|
let currentErrors = this.validate(value);
|
||||||
|
@ -96,9 +100,14 @@ export default class Control extends React.PureComponent {
|
||||||
const ControlType = controlMap[this.props.type];
|
const ControlType = controlMap[this.props.type];
|
||||||
const divStyle = this.props.hidden ? { display: 'none' } : null;
|
const divStyle = this.props.hidden ? { display: 'none' } : null;
|
||||||
return (
|
return (
|
||||||
<div style={divStyle}>
|
<div
|
||||||
|
style={divStyle}
|
||||||
|
onMouseEnter={this.setHover.bind(this, true)}
|
||||||
|
onMouseLeave={this.setHover.bind(this, false)}
|
||||||
|
>
|
||||||
<ControlType
|
<ControlType
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
|
hovered={this.state.hovered}
|
||||||
{...this.props}
|
{...this.props}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,74 +10,92 @@ const propTypes = {
|
||||||
renderTrigger: PropTypes.bool,
|
renderTrigger: PropTypes.bool,
|
||||||
rightNode: PropTypes.node,
|
rightNode: PropTypes.node,
|
||||||
leftNode: PropTypes.node,
|
leftNode: PropTypes.node,
|
||||||
|
onClick: PropTypes.func,
|
||||||
|
hovered: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
validationErrors: [],
|
validationErrors: [],
|
||||||
renderTrigger: false,
|
renderTrigger: false,
|
||||||
|
hovered: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ControlHeader({
|
export default class ControlHeader extends React.Component {
|
||||||
label, description, validationErrors, renderTrigger, leftNode, rightNode }) {
|
renderOptionalIcons() {
|
||||||
const hasError = (validationErrors.length > 0);
|
if (this.props.hovered) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<span>
|
||||||
<div className="pull-left">
|
{this.props.description &&
|
||||||
<ControlLabel>
|
|
||||||
{hasError ?
|
|
||||||
<strong className="text-danger">{label}</strong> :
|
|
||||||
<span>{label}</span>
|
|
||||||
}
|
|
||||||
{' '}
|
|
||||||
{(validationErrors.length > 0) &&
|
|
||||||
<span>
|
<span>
|
||||||
<OverlayTrigger
|
<InfoTooltipWithTrigger
|
||||||
placement="right"
|
label="descr"
|
||||||
overlay={
|
tooltip={this.props.description}
|
||||||
<Tooltip id={'error-tooltip'}>
|
placement="top"
|
||||||
{validationErrors.join(' ')}
|
/>
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<i className="fa fa-exclamation-circle text-danger" />
|
|
||||||
</OverlayTrigger>
|
|
||||||
{' '}
|
{' '}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
{description &&
|
{this.props.renderTrigger &&
|
||||||
<span>
|
<span>
|
||||||
<InfoTooltipWithTrigger label={label} tooltip={description} />
|
<InfoTooltipWithTrigger
|
||||||
|
label="bolt"
|
||||||
|
tooltip={this.props.description}
|
||||||
|
placement="top"
|
||||||
|
icon="bolt"
|
||||||
|
/>
|
||||||
{' '}
|
{' '}
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
{renderTrigger &&
|
</span>);
|
||||||
<span>
|
}
|
||||||
<OverlayTrigger
|
return null;
|
||||||
placement="right"
|
}
|
||||||
overlay={
|
render() {
|
||||||
<Tooltip id={'rendertrigger-tooltip'}>
|
const labelClass = (this.props.validationErrors.length > 0) ? 'text-danger' : '';
|
||||||
Takes effect on chart immediatly
|
return (
|
||||||
</Tooltip>
|
<div
|
||||||
}
|
className="ControlHeader"
|
||||||
>
|
>
|
||||||
<i className="fa fa-bolt text-muted" />
|
<div className="pull-left">
|
||||||
</OverlayTrigger>
|
<ControlLabel>
|
||||||
{' '}
|
{this.props.leftNode &&
|
||||||
|
<span>{this.props.leftNode}</span>
|
||||||
|
}
|
||||||
|
<span
|
||||||
|
onClick={this.props.onClick}
|
||||||
|
className={labelClass}
|
||||||
|
style={{ cursor: this.props.onClick ? 'pointer' : '' }}
|
||||||
|
>
|
||||||
|
{this.props.label}
|
||||||
</span>
|
</span>
|
||||||
}
|
{' '}
|
||||||
{leftNode &&
|
{(this.props.validationErrors.length > 0) &&
|
||||||
<span>{leftNode}</span>
|
<span>
|
||||||
}
|
<OverlayTrigger
|
||||||
</ControlLabel>
|
placement="top"
|
||||||
</div>
|
overlay={
|
||||||
{rightNode &&
|
<Tooltip id={'error-tooltip'}>
|
||||||
<div className="pull-right">
|
{this.props.validationErrors.join(' ')}
|
||||||
{rightNode}
|
</Tooltip>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<i className="fa fa-exclamation-circle text-danger" />
|
||||||
|
</OverlayTrigger>
|
||||||
|
{' '}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
{this.renderOptionalIcons()}
|
||||||
|
</ControlLabel>
|
||||||
</div>
|
</div>
|
||||||
}
|
{this.props.rightNode &&
|
||||||
<div className="clearfix" />
|
<div className="pull-right">
|
||||||
</div>
|
{this.props.rightNode}
|
||||||
);
|
</div>
|
||||||
|
}
|
||||||
|
<div className="clearfix" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlHeader.propTypes = propTypes;
|
ControlHeader.propTypes = propTypes;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Checkbox } from 'react-bootstrap';
|
|
||||||
import ControlHeader from '../ControlHeader';
|
import ControlHeader from '../ControlHeader';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
|
@ -24,11 +23,16 @@ export default class CheckboxControl extends React.Component {
|
||||||
return (
|
return (
|
||||||
<ControlHeader
|
<ControlHeader
|
||||||
{...this.props}
|
{...this.props}
|
||||||
|
onClick={this.onToggle.bind(this)}
|
||||||
leftNode={
|
leftNode={
|
||||||
<Checkbox
|
<span>
|
||||||
checked={this.props.value}
|
<i
|
||||||
onChange={this.onToggle.bind(this)}
|
className={`fa fa-check ${this.props.value ? 'text-primary' : 'text-transparent'}`}
|
||||||
/>
|
onClick={this.onToggle.bind(this)}
|
||||||
|
style={{ border: '1px solid #aaa', borderRadius: '2px', cursor: 'pointer' }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</span>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -66,7 +66,7 @@
|
||||||
"react-addons-css-transition-group": "^15.6.0",
|
"react-addons-css-transition-group": "^15.6.0",
|
||||||
"react-addons-shallow-compare": "^15.4.2",
|
"react-addons-shallow-compare": "^15.4.2",
|
||||||
"react-alert": "^1.0.14",
|
"react-alert": "^1.0.14",
|
||||||
"react-bootstrap": "^0.31.0",
|
"react-bootstrap": "^0.31.2",
|
||||||
"react-bootstrap-table": "^3.1.7",
|
"react-bootstrap-table": "^3.1.7",
|
||||||
"react-dom": "^15.5.1",
|
"react-dom": "^15.5.1",
|
||||||
"react-gravatar": "^2.6.1",
|
"react-gravatar": "^2.6.1",
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* eslint-disable no-unused-expressions */
|
/* eslint-disable no-unused-expressions */
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Checkbox } from 'react-bootstrap';
|
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { describe, it, beforeEach } from 'mocha';
|
import { describe, it, beforeEach } from 'mocha';
|
||||||
|
@ -28,6 +27,6 @@ describe('CheckboxControl', () => {
|
||||||
expect(controlHeader).to.have.lengthOf(1);
|
expect(controlHeader).to.have.lengthOf(1);
|
||||||
|
|
||||||
const headerWrapper = controlHeader.shallow();
|
const headerWrapper = controlHeader.shallow();
|
||||||
expect(headerWrapper.find(Checkbox)).to.have.length(1);
|
expect(headerWrapper.find('i.fa-check')).to.have.length(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -329,3 +329,6 @@ iframe {
|
||||||
border: none;
|
border: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
.text-transparent {
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue