Make VizTypeControl use metadata from plugin (#6235)
* Render vis type item using information from registry * adjust style * fix unit test * Remove large thumbnails * Update addSlice
Before Width: | Height: | Size: 103 KiB |
Before Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 132 KiB |
Before Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 398 KiB |
Before Width: | Height: | Size: 253 KiB |
Before Width: | Height: | Size: 296 KiB |
Before Width: | Height: | Size: 225 KiB |
Before Width: | Height: | Size: 177 KiB |
Before Width: | Height: | Size: 2.0 MiB |
Before Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 968 KiB |
Before Width: | Height: | Size: 511 KiB |
Before Width: | Height: | Size: 433 KiB |
Before Width: | Height: | Size: 777 KiB |
Before Width: | Height: | Size: 578 KiB |
Before Width: | Height: | Size: 242 KiB |
Before Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 162 KiB |
Before Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 425 KiB |
Before Width: | Height: | Size: 63 KiB |
Before Width: | Height: | Size: 161 KiB |
Before Width: | Height: | Size: 738 KiB |
Before Width: | Height: | Size: 314 KiB |
Before Width: | Height: | Size: 113 KiB |
Before Width: | Height: | Size: 220 KiB |
Before Width: | Height: | Size: 222 KiB |
Before Width: | Height: | Size: 743 KiB |
Before Width: | Height: | Size: 230 KiB |
Before Width: | Height: | Size: 460 KiB |
Before Width: | Height: | Size: 194 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 270 KiB |
Before Width: | Height: | Size: 494 KiB |
Before Width: | Height: | Size: 200 KiB |
Before Width: | Height: | Size: 99 KiB |
Before Width: | Height: | Size: 170 KiB |
Before Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 115 KiB |
Before Width: | Height: | Size: 133 KiB |
|
@ -3,17 +3,30 @@ import sinon from 'sinon';
|
|||
import { shallow } from 'enzyme';
|
||||
import { Modal } from 'react-bootstrap';
|
||||
import VizTypeControl from '../../../../src/explore/components/controls/VizTypeControl';
|
||||
import getChartMetadataRegistry from '../../../../src/visualizations/core/registries/ChartMetadataRegistrySingleton';
|
||||
import ChartMetadata from '../../../../src/visualizations/core/models/ChartMetadata';
|
||||
|
||||
const defaultProps = {
|
||||
name: 'viz_type',
|
||||
label: 'Visualization Type',
|
||||
value: 'table',
|
||||
value: 'vis1',
|
||||
onChange: sinon.spy(),
|
||||
};
|
||||
|
||||
describe('VizTypeControl', () => {
|
||||
let wrapper;
|
||||
|
||||
const registry = getChartMetadataRegistry();
|
||||
registry
|
||||
.registerValue('vis1', new ChartMetadata({
|
||||
name: 'vis1',
|
||||
thumbnail: '',
|
||||
}))
|
||||
.registerValue('vis2', new ChartMetadata({
|
||||
name: 'vis2',
|
||||
thumbnail: '',
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(<VizTypeControl {...defaultProps} />);
|
||||
});
|
||||
|
@ -28,8 +41,8 @@ describe('VizTypeControl', () => {
|
|||
expect(defaultProps.onChange.called).toBe(true);
|
||||
});
|
||||
it('filters images based on text input', () => {
|
||||
expect(wrapper.find('img').length).toBeGreaterThan(20);
|
||||
wrapper.setState({ filter: 'time' });
|
||||
expect(wrapper.find('img').length).toBeLessThan(10);
|
||||
expect(wrapper.find('img')).toHaveLength(2);
|
||||
wrapper.setState({ filter: 'vis2' });
|
||||
expect(wrapper.find('img')).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|||
import { Button, Panel } from 'react-bootstrap';
|
||||
import Select from 'react-virtualized-select';
|
||||
import { t } from '@superset-ui/translation';
|
||||
import visTypes from '../explore/visTypes';
|
||||
import getChartMetadataRegistry from '../visualizations/core/registries/ChartMetadataRegistrySingleton';
|
||||
|
||||
const propTypes = {
|
||||
datasources: PropTypes.arrayOf(PropTypes.shape({
|
||||
|
@ -17,11 +17,13 @@ const styleSelectWidth = { width: 300 };
|
|||
export default class AddSliceContainer extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const visTypeKeys = Object.keys(visTypes);
|
||||
this.vizTypeOptions = visTypeKeys.map(vt => ({ label: visTypes[vt].label, value: vt }));
|
||||
this.state = {
|
||||
visType: 'table',
|
||||
};
|
||||
|
||||
this.changeDatasource = this.changeDatasource.bind(this);
|
||||
this.changeVisType = this.changeVisType.bind(this);
|
||||
this.gotoSlice = this.gotoSlice.bind(this);
|
||||
}
|
||||
|
||||
exploreUrl() {
|
||||
|
@ -54,6 +56,12 @@ export default class AddSliceContainer extends React.PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const types = getChartMetadataRegistry().entries()
|
||||
.map(({ key, value }) => ({
|
||||
value: key,
|
||||
label: value.name,
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<Panel header={<h3>{t('Create a new chart')}</h3>}>
|
||||
|
@ -64,7 +72,7 @@ export default class AddSliceContainer extends React.PureComponent {
|
|||
clearable={false}
|
||||
style={styleSelectWidth}
|
||||
name="select-datasource"
|
||||
onChange={this.changeDatasource.bind(this)}
|
||||
onChange={this.changeDatasource}
|
||||
options={this.props.datasources}
|
||||
placeholder={t('Choose a datasource')}
|
||||
value={this.state.datasourceValue}
|
||||
|
@ -86,8 +94,8 @@ export default class AddSliceContainer extends React.PureComponent {
|
|||
clearable={false}
|
||||
name="select-vis-type"
|
||||
style={styleSelectWidth}
|
||||
onChange={this.changeVisType.bind(this)}
|
||||
options={this.vizTypeOptions}
|
||||
onChange={this.changeVisType}
|
||||
options={types}
|
||||
placeholder={t('Choose a visualization type')}
|
||||
value={this.state.visType}
|
||||
/>
|
||||
|
@ -96,7 +104,7 @@ export default class AddSliceContainer extends React.PureComponent {
|
|||
<Button
|
||||
bsStyle="primary"
|
||||
disabled={this.isBtnDisabled()}
|
||||
onClick={this.gotoSlice.bind(this)}
|
||||
onClick={this.gotoSlice}
|
||||
>
|
||||
{t('Create new chart')}
|
||||
</Button>
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import React from 'react';
|
||||
import { hot } from 'react-hot-loader';
|
||||
import setupApp from '../setup/setupApp';
|
||||
import setupPlugins from '../setup/setupPlugins';
|
||||
import AddSliceContainer from './AddSliceContainer';
|
||||
|
||||
setupApp();
|
||||
setupPlugins();
|
||||
|
||||
const addSliceContainer = document.getElementById('js-add-slice-container');
|
||||
const bootstrapData = JSON.parse(addSliceContainer.getAttribute('data-bootstrap'));
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
.viztype-label {
|
||||
margin-top: 10px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.viztype-selector-container {
|
||||
cursor: pointer;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.viztype-selector-container:hover img {
|
||||
border: 1px solid #aaa;
|
||||
}
|
||||
|
||||
.viztype-selector-container.selected {
|
||||
cursor: not-allowed;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.viztype-selector-container.selected img {
|
||||
border: 1px solid #333;
|
||||
}
|
||||
|
||||
.viztype-selector-container img {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
transition: border-color .2s;
|
||||
}
|
||||
|
||||
.viztype-control-search-box {
|
||||
margin-bottom: 10px;
|
||||
}
|
|
@ -5,8 +5,9 @@ import {
|
|||
Tooltip } from 'react-bootstrap';
|
||||
import { t } from '@superset-ui/translation';
|
||||
|
||||
import visTypes from '../../visTypes';
|
||||
import getChartMetadataRegistry from '../../../visualizations/core/registries/ChartMetadataRegistrySingleton';
|
||||
import ControlHeader from '../ControlHeader';
|
||||
import './VizTypeControl.css';
|
||||
|
||||
const propTypes = {
|
||||
description: PropTypes.string,
|
||||
|
@ -50,37 +51,47 @@ export default class VizTypeControl extends React.PureComponent {
|
|||
this.searchRef.focus();
|
||||
}
|
||||
}
|
||||
renderVizType(vizType) {
|
||||
const vt = vizType;
|
||||
renderItem(entry) {
|
||||
const { value } = this.props;
|
||||
const { key, value: type } = entry;
|
||||
const isSelected = key === value;
|
||||
return (
|
||||
<div
|
||||
className={`viztype-selector-container ${vt === this.props.value ? 'selected' : ''}`}
|
||||
onClick={this.onChange.bind(this, vt)}
|
||||
className={`viztype-selector-container ${isSelected ? 'selected' : ''}`}
|
||||
onClick={this.onChange.bind(this, key)}
|
||||
>
|
||||
<img
|
||||
alt={`viz-type-${vt}`}
|
||||
alt={type.name}
|
||||
width="100%"
|
||||
className={`viztype-selector ${this.props.value === vt ? 'selected' : ''}`}
|
||||
src={`/static/assets/images/viz_thumbnails/${vt}.png`}
|
||||
className={`viztype-selector ${isSelected ? 'selected' : ''}`}
|
||||
src={type.thumbnail}
|
||||
/>
|
||||
<div className="viztype-label">
|
||||
{visTypes[vt].label}
|
||||
{type.name}
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
render() {
|
||||
const filter = this.state.filter;
|
||||
const filteredVizTypes = Object.keys(visTypes)
|
||||
.filter(vt => filter.length === 0 || visTypes[vt].label.toLowerCase().includes(filter));
|
||||
const { filter, showModal } = this.state;
|
||||
const { value } = this.props;
|
||||
|
||||
const registry = getChartMetadataRegistry();
|
||||
|
||||
const types = registry.entries();
|
||||
const filteredTypes = filter.length > 0
|
||||
? types.filter(type => type.value.name.toLowerCase().includes(filter))
|
||||
: types;
|
||||
|
||||
const selectedType = registry.get(value);
|
||||
|
||||
const imgPerRow = 6;
|
||||
const rows = [];
|
||||
for (let i = 0; i <= filteredVizTypes.length; i += imgPerRow) {
|
||||
for (let i = 0; i <= filteredTypes.length; i += imgPerRow) {
|
||||
rows.push(
|
||||
<Row key={`row-${i}`}>
|
||||
{filteredVizTypes.slice(i, i + imgPerRow).map(vt => (
|
||||
<Col md={12 / imgPerRow} key={`grid-col-${vt}`}>
|
||||
{this.renderVizType(vt)}
|
||||
{filteredTypes.slice(i, i + imgPerRow).map(entry => (
|
||||
<Col md={12 / imgPerRow} key={`grid-col-${entry.key}`}>
|
||||
{this.renderItem(entry)}
|
||||
</Col>
|
||||
))}
|
||||
</Row>);
|
||||
|
@ -97,11 +108,11 @@ export default class VizTypeControl extends React.PureComponent {
|
|||
}
|
||||
>
|
||||
<Label onClick={this.toggleModal} style={{ cursor: 'pointer' }}>
|
||||
{visTypes[this.props.value].label}
|
||||
{selectedType.name}
|
||||
</Label>
|
||||
</OverlayTrigger>
|
||||
<Modal
|
||||
show={this.state.showModal}
|
||||
show={showModal}
|
||||
onHide={this.toggleModal}
|
||||
onEnter={this.focusSearch}
|
||||
onExit={this.setSearchRef}
|
||||
|
@ -111,21 +122,21 @@ export default class VizTypeControl extends React.PureComponent {
|
|||
<Modal.Title>{t('Select a visualization type')}</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<div>
|
||||
<div className="viztype-control-search-box">
|
||||
<FormControl
|
||||
id="formControlsText"
|
||||
inputRef={(ref) => { this.setSearchRef(ref); }}
|
||||
inputRef={this.setSearchRef}
|
||||
type="text"
|
||||
bsSize="sm"
|
||||
value={this.state.filter}
|
||||
placeholder={t('Search / Filter')}
|
||||
bsSize="md"
|
||||
value={filter}
|
||||
placeholder={t('Search')}
|
||||
onChange={this.changeSearch}
|
||||
/>
|
||||
</div>
|
||||
{rows}
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
</div>);
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,32 +47,6 @@
|
|||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.viztype-label {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.viztype-selector-container {
|
||||
cursor: pointer;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 5px;
|
||||
border: 1px solid #aaa;
|
||||
}
|
||||
|
||||
.viztype-selector-container:hover {
|
||||
border: 1px solid #000;
|
||||
}
|
||||
|
||||
.viztype-selector-container.selected {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.viztype-selector-container.selected {
|
||||
cursor: not-allowed;
|
||||
border: 1px solid #aaa;
|
||||
}
|
||||
|
||||
.color-scheme-container {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
|
|