import React, { Component } from 'react';
import WordCloud from 'wordcloud';
class TermCloud extends Component {
WIDTH = 640;
HEIGHT = 480;
state = {
data: [],
hasPropsChanged: false,
width: this.WIDTH,
height: this.HEIGHT
};
wordCloudRef = React.createRef();
static getDerivedStateFromProps(props, state) {
let hasPropsChanged = false;
if (props.data !== state.data) {
hasPropsChanged = true;
}
return { data: props.data, hasPropsChanged };
}
componentDidMount() {
this.setCanvasDimensions();
window.addEventListener("resize", this.setCanvasDimensions);
}
setCanvasDimensions = () => {
const innerWidth = window.innerWidth;
const isBigScreen = innerWidth > this.WIDTH;
this.setState({
width: isBigScreen ? this.WIDTH : innerWidth / 1.25,
height: isBigScreen ? this.HEIGHT : innerWidth / 2
}, this.renderWordCloud);
}
renderWordCloud() {
const { data } = this.state;
if (data.length <= 0) return;
const min = data[data.length - 1].count;
const max = data[0].count;
const list = data.map(item => [item.term, item.count]);
WordCloud(this.wordCloudRef.current, {
list,
weightFactor: (count) => this.getWeight(min, max, count),
});
}
getWeight(min, max, count) {
const { width } = this.state;
const minWeight = width / 30;
const maxWeight = width / 8;
const diff = max - min;
const newDiff = maxWeight - minWeight;
return (count * newDiff - min * newDiff + minWeight * diff) / diff;
}
render() {
const { width, height } = this.state;
if (this.state.hasPropsChanged) {
this.renderWordCloud();
}
return (
<canvas id="wordCloud" width={width} height={height} ref={this.wordCloudRef}>
</canvas>
)
}
}
export default TermCloud;
import React from 'react'
import PropTypes from 'prop-types'
import { DirectionsRenderer } from 'react-google-maps'
import { Location } from './PropTypes'
import getRoute from './utils/getRoute'
import hasPropsChanged from './utils/hasPropsChanged'
const propTypes = {
/** Start point */
startPoint: Location,
/** End point */
endPoint: Location,
/** Function fired when direction routing is loaded */
onLoad: PropTypes.func,
/** Function fired when direction routing caused error */
onError: PropTypes.func,
/** Waypoints */
via: PropTypes.oneOfType([
PropTypes.arrayOf(Location),
Location
])
}
/**
* Component which represents Directions.
*
* @property {object} props
* @property {object|{ lat: number, lng: number }} props.startPoint
* @property {object|{ lat: number, lng: number }} props.endPoint
* @property {object[]|Array<{ lat: number, lng: number }>} [props.via]
*
* @property {object} state
* @property {object|null} state.directions
*
* @class
*/
class Directions extends React.PureComponent {
state = {
directions: null
}
/**
* Load directions when component is mounted
*/
componentDidMount () {
this.loadDirections(this.props)
}
/**
* Update route when points has changed
*
* @param {object} nextProps
* @param {object|{ lat: number, lng: number }} nextProps.startPoint
* @param {object|{ lat: number, lng: number }} nextProps.endPoint
* @param {object[]|Array<{ lat: number, lng: number }>} nextProps.via
*/
componentWillReceiveProps (nextProps) {
// When any point has changed, load directions again
if (hasPropsChanged(this.props, nextProps)) {
this.loadDirections(nextProps)
}
}
/**
* Load directions from Google
*
* @param {object} nextProps
* @param {object|{ lat: number, lng: number }} nextProps.startPoint
* @param {object|{ lat: number, lng: number }} nextProps.endPoint
*/
loadDirections (nextProps) {
if (nextProps.startPoint == null || nextProps.endPoint == null) {
return
}
return getRoute(nextProps.startPoint, nextProps.endPoint, nextProps.via).then(
this.handleRoute.bind(this, nextProps.startPoint, nextProps.endPoint, nextProps.via),
this.handleError
)
}
/**
* Handle found route
*
* @param {object|{ lat: number, lng: number }} startPoint
* @param {object|{ lat: number, lng: number }} endPoint
* @param {object[]|Array<{ lat: number, lng: number }>} via
* @param {*} directions
*/
handleRoute (startPoint, endPoint, via, directions) {
const { onLoad } = this.props
// Ignore route for old directions, otherwise we've got race condition
if (hasPropsChanged({ startPoint, endPoint, via }, this.props)) {
return
}
if (onLoad) {
onLoad()
}
// Update current directions
this.setState({ directions })
}
/**
* Handle error while searching for route
*
* @param {*} error
*/
handleError = (error) => {
const { onError } = this.props
if (onError) {
onError(error)
}
}
/**
* Build Google DirectionsRenderer
*
* @returns {React.Element|null}
*/
render () {
const { startPoint, endPoint, via, onError, onLoad, ...passedProps } = this.props
const { directions } = this.state
if (!directions) {
return null
}
return (
<DirectionsRenderer
directions={directions}
{...passedProps}
/>
)
}
}
Directions.displayName = 'Directions'
Directions.propTypes = propTypes
export default Directions
jest.dontMock('../StoreDependencyMixin.js');
describe('StoreDependencyMixin', () => {
var StoreDependencyMixin;
var mockComponent;
var mockMixin;
beforeEach(() => {
StoreDependencyMixin = require('../StoreDependencyMixin.js');
mockComponent = {
props: {},
state: {},
setState: jest.genMockFn()
};
mockMixin = StoreDependencyMixin({});
mockMixin.getInitialState.call(mockComponent);
});
it('applies dependencies in getInitialState', () => {
var {applyDependencies} = require('../StoreDependencyMixinInitialize.js');
var {getDependencyState} = require('../StoreDependencyMixinState.js');
expect(applyDependencies.mock.calls.length).toBe(1);
expect(getDependencyState.mock.calls.length).toBe(1);
});
it('sets handlers in componentWillMount', () => {
var {setupHandlers} = require('../StoreDependencyMixinHandlers.js');
mockMixin.componentWillMount.call(mockComponent);
expect(setupHandlers.mock.calls.length).toBe(1);
});
it('cleans up handlers in componentWillUnmount', () => {
var {cleanupHandlers} = require('../StoreDependencyMixinHandlers.js');
mockMixin.componentWillUnmount.call(mockComponent);
expect(cleanupHandlers.mock.calls.length).toBe(1);
});
it('sets state if appropriate in componentWillReceiveProps', () => {
var {hasPropsChanged} = require('../StoreDependencyMixinTransitions.js');
hasPropsChanged.mockReturnValueOnce(false);
mockMixin.componentWillReceiveProps.call(mockComponent);
expect(mockComponent.setState.mock.calls.length).toBe(0);
hasPropsChanged.mockReturnValueOnce(true);
mockMixin.componentWillReceiveProps.call(mockComponent);
expect(mockComponent.setState.mock.calls.length).toBe(1);
});
it('sets state if appropriate in componentDidUpdate', () => {
var {hasStateChanged} = require('../StoreDependencyMixinTransitions.js');
hasStateChanged.mockReturnValueOnce(false);
mockMixin.componentDidUpdate.call(mockComponent);
expect(mockComponent.setState.mock.calls.length).toBe(0);
hasStateChanged.mockReturnValueOnce(true);
mockMixin.componentDidUpdate.call(mockComponent);
expect(mockComponent.setState.mock.calls.length).toBe(1);
});
});