[SIP-6] Add reactify function and convert world map to new directory structure. (#5893)

* reactify world map

* add createAdaptor

* fix typo

* add schema

* move directory

* remove unnecessary lines

* make setRef a function

* convert keys to camelcase

* add unit test

* update formatting

* add check for displayName

* pass width and height as separate inputs
This commit is contained in:
Krist Wongsuphasawat 2018-09-18 23:35:36 -07:00 committed by Chris Williams
parent 75bc501146
commit 8fff0d9e8f
13 changed files with 150 additions and 19 deletions

View File

@ -79,6 +79,7 @@
"jed": "^1.1.1",
"jquery": "3.1.1",
"json-bigint": "^0.3.0",
"lodash": "^4.17.11",
"lodash.throttle": "^4.1.1",
"mapbox-gl": "^0.45.0",
"mathjs": "^3.20.2",

View File

@ -0,0 +1,29 @@
import { it, describe } from 'mocha';
import { expect } from 'chai';
import convertKeysToCamelCase from '../../../src/utils/convertKeysToCamelCase';
describe.only('convertKeysToCamelCase(object)', () => {
it('returns undefined for undefined input', () => {
expect(convertKeysToCamelCase(undefined)).to.equal(undefined);
});
it('returns null for null input', () => {
expect(convertKeysToCamelCase(null)).to.equal(null);
});
it('returns a new object that has all keys in camelCase', () => {
const input = {
is_happy: true,
'is-angry': false,
isHungry: false,
};
expect(convertKeysToCamelCase(input)).to.deep.equal({
isHappy: true,
isAngry: false,
isHungry: false,
});
});
it('throws error if input is not a plain object', () => {
expect(() => { convertKeysToCamelCase({}); }).to.not.throw();
expect(() => { convertKeysToCamelCase(''); }).to.throw();
expect(() => { convertKeysToCamelCase(new Map()); }).to.throw();
});
});

View File

@ -0,0 +1,11 @@
import { mapKeys, camelCase, isPlainObject } from 'lodash/fp';
export default function convertKeysToCamelCase(object) {
if (object === null || object === undefined) {
return object;
}
if (isPlainObject(object)) {
return mapKeys(k => camelCase(k), object);
}
throw new Error(`Cannot convert input that is not a plain object: ${object}`);
}

View File

@ -0,0 +1,19 @@
import React from 'react';
import ReactDOM from 'react-dom';
import BasicChartInput from '../visualizations/models/BasicChartInput';
const IDENTITY = x => x;
export default function createAdaptor(Component, transformProps = IDENTITY) {
return function adaptor(slice, payload, setControlValue) {
const basicChartInput = new BasicChartInput(slice, payload, setControlValue);
ReactDOM.render(
<Component
width={slice.width()}
height={slice.height()}
{...transformProps(basicChartInput)}
/>,
document.querySelector(slice.selector),
);
};
}

View File

@ -0,0 +1,54 @@
import React from 'react';
export default function reactify(renderFn) {
class ReactifiedComponent extends React.Component {
constructor(props) {
super(props);
this.setContainerRef = this.setContainerRef.bind(this);
}
componentDidMount() {
this.execute();
}
componentDidUpdate() {
this.execute();
}
componentWillUnmount() {
this.container = null;
}
setContainerRef(c) {
this.container = c;
}
execute() {
if (this.container) {
renderFn(this.container, this.props);
}
}
render() {
const { id, className } = this.props;
return (
<div
id={id}
className={className}
ref={this.setContainerRef}
/>
);
}
}
if (renderFn.displayName) {
ReactifiedComponent.displayName = renderFn.displayName;
}
if (renderFn.propTypes) {
ReactifiedComponent.propTypes = renderFn.propTypes;
}
if (renderFn.defaultProps) {
ReactifiedComponent.defaultProps = renderFn.defaultProps;
}
return ReactifiedComponent;
}

View File

@ -0,0 +1,4 @@
import reactify from '../../utils/reactify';
import WorldMap from './WorldMap';
export default reactify(WorldMap);

View File

@ -1,7 +1,7 @@
import d3 from 'd3';
import PropTypes from 'prop-types';
import Datamap from 'datamaps';
import './world_map.css';
import './WorldMap.css';
const propTypes = {
data: PropTypes.arrayOf(PropTypes.shape({
@ -109,20 +109,4 @@ function WorldMap(element, props) {
WorldMap.propTypes = propTypes;
function adaptor(slice, payload) {
const { selector, formData } = slice;
const {
max_bubble_size: maxBubbleSize,
show_bubbles: showBubbles,
} = formData;
const element = document.querySelector(selector);
return WorldMap(element, {
data: payload.data,
height: slice.height(),
maxBubbleSize: parseInt(maxBubbleSize, 10),
showBubbles,
});
}
export default adaptor;
export default WorldMap;

View File

@ -0,0 +1,5 @@
import createAdaptor from '../../utils/createAdaptor';
import WorldMap from './ReactWorldMap';
import transformProps from './transformProps';
export default createAdaptor(WorldMap, transformProps);

View File

@ -0,0 +1,10 @@
export default function transformProps(basicChartInput) {
const { formData, payload } = basicChartInput;
const { maxBubbleSize, showBubbles } = formData;
return {
data: payload.data,
maxBubbleSize: parseInt(maxBubbleSize, 10),
showBubbles,
};
}

View File

@ -108,7 +108,7 @@ const vizMap = {
[VIZ_TYPES.word_cloud]: () =>
loadVis(import(/* webpackChunkName: "word_cloud" */ './wordcloud/WordCloud.js')),
[VIZ_TYPES.world_map]: () =>
loadVis(import(/* webpackChunkName: "world_map" */ './world_map.js')),
loadVis(import(/* webpackChunkName: "world_map" */ './WorldMap/adaptor.jsx')),
[VIZ_TYPES.dual_line]: loadNvd3,
[VIZ_TYPES.event_flow]: () =>
loadVis(import(/* webpackChunkName: "EventFlow" */ './EventFlow.jsx')),

View File

@ -0,0 +1,10 @@
import convertKeysToCamelCase from '../../utils/convertKeysToCamelCase';
export default class BasicChartInput {
constructor(slice, payload, setControlValue) {
this.annotationData = slice.annotationData;
this.formData = convertKeysToCamelCase(slice.formData);
this.payload = payload;
this.setControlValue = setControlValue;
}
}

View File

@ -7487,6 +7487,10 @@ lodash@4.17.10, lodash@^4.0.1, lodash@^4.0.8, lodash@^4.13.1, lodash@^4.14.0, lo
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
lodash@^4.17.11:
version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
log-symbols@2.2.0, log-symbols@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"