Convert MeasurementList to TypeScript and add tests
parent
e2ceba3560
commit
f4eca0a228
|
@ -23,10 +23,6 @@ interface DatabaseListState {
|
||||||
namespaces: Namespace[]
|
namespaces: Namespace[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DatabaseListContext {
|
|
||||||
source: Source
|
|
||||||
}
|
|
||||||
|
|
||||||
const {shape, string} = PropTypes
|
const {shape, string} = PropTypes
|
||||||
|
|
||||||
class DatabaseList extends PureComponent<DatabaseListProps, DatabaseListState> {
|
class DatabaseList extends PureComponent<DatabaseListProps, DatabaseListState> {
|
||||||
|
|
|
@ -1,52 +1,56 @@
|
||||||
import React, {PropTypes} from 'react'
|
import React, {PureComponent} from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
|
||||||
import {showMeasurements} from 'shared/apis/metaQuery'
|
import {showMeasurements} from 'src/shared/apis/metaQuery'
|
||||||
import showMeasurementsParser from 'shared/parsing/showMeasurements'
|
import showMeasurementsParser from 'src/shared/parsing/showMeasurements'
|
||||||
|
|
||||||
|
import {Query, Source} from 'src/types'
|
||||||
|
|
||||||
import TagList from 'src/data_explorer/components/TagList'
|
import TagList from 'src/data_explorer/components/TagList'
|
||||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||||
|
|
||||||
const {func, shape, string} = PropTypes
|
interface Props {
|
||||||
|
query: Query
|
||||||
|
querySource: Source
|
||||||
|
onChooseTag: () => void
|
||||||
|
onGroupByTag: () => void
|
||||||
|
onToggleTagAcceptance: () => void
|
||||||
|
onChooseMeasurement: (measurement: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
const MeasurementList = React.createClass({
|
interface State {
|
||||||
propTypes: {
|
measurements: string[]
|
||||||
query: shape({
|
filterText: string
|
||||||
database: string,
|
}
|
||||||
measurement: string,
|
|
||||||
}).isRequired,
|
|
||||||
onChooseMeasurement: func.isRequired,
|
|
||||||
onChooseTag: func.isRequired,
|
|
||||||
onToggleTagAcceptance: func.isRequired,
|
|
||||||
onGroupByTag: func.isRequired,
|
|
||||||
querySource: shape({
|
|
||||||
links: shape({
|
|
||||||
proxy: string.isRequired,
|
|
||||||
}).isRequired,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
|
|
||||||
contextTypes: {
|
const {shape, string} = PropTypes
|
||||||
|
|
||||||
|
class MeasurementList extends PureComponent<Props, State> {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {
|
||||||
|
measurements: [],
|
||||||
|
filterText: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
this.handleEscape = this.handleEscape.bind(this)
|
||||||
|
this.handleFilterText = this.handleFilterText.bind(this)
|
||||||
|
this.handleAcceptReject = this.handleAcceptReject.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static defaultProps: Partial<Props> = {
|
||||||
|
querySource: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
public static contextTypes = {
|
||||||
source: shape({
|
source: shape({
|
||||||
links: shape({
|
links: shape({
|
||||||
proxy: string.isRequired,
|
proxy: string.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
},
|
}
|
||||||
|
|
||||||
getInitialState() {
|
|
||||||
return {
|
|
||||||
measurements: [],
|
|
||||||
filterText: '',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps() {
|
|
||||||
return {
|
|
||||||
querySource: null,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if (!this.props.query.database) {
|
if (!this.props.query.database) {
|
||||||
|
@ -54,7 +58,7 @@ const MeasurementList = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
this._getMeasurements()
|
this._getMeasurements()
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
const {query, querySource} = this.props
|
const {query, querySource} = this.props
|
||||||
|
@ -71,14 +75,14 @@ const MeasurementList = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
this._getMeasurements()
|
this._getMeasurements()
|
||||||
},
|
}
|
||||||
|
|
||||||
handleFilterText(e) {
|
handleFilterText(e) {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
this.setState({
|
this.setState({
|
||||||
filterText: this.refs.filterText.value,
|
filterText: e.target.value,
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
handleEscape(e) {
|
handleEscape(e) {
|
||||||
if (e.key !== 'Escape') {
|
if (e.key !== 'Escape') {
|
||||||
|
@ -89,12 +93,11 @@ const MeasurementList = React.createClass({
|
||||||
this.setState({
|
this.setState({
|
||||||
filterText: '',
|
filterText: '',
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
|
|
||||||
handleAcceptReject(e) {
|
handleAcceptReject() {
|
||||||
e.stopPropagation()
|
|
||||||
this.props.onToggleTagAcceptance()
|
this.props.onToggleTagAcceptance()
|
||||||
},
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
|
@ -105,14 +108,13 @@ const MeasurementList = React.createClass({
|
||||||
? <div className="query-builder--filter">
|
? <div className="query-builder--filter">
|
||||||
<input
|
<input
|
||||||
className="form-control input-sm"
|
className="form-control input-sm"
|
||||||
ref="filterText"
|
|
||||||
placeholder="Filter"
|
placeholder="Filter"
|
||||||
type="text"
|
type="text"
|
||||||
value={this.state.filterText}
|
value={this.state.filterText}
|
||||||
onChange={this.handleFilterText}
|
onChange={this.handleFilterText}
|
||||||
onKeyUp={this.handleEscape}
|
onKeyUp={this.handleEscape}
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
autoComplete={false}
|
autoComplete="false"
|
||||||
/>
|
/>
|
||||||
<span className="icon search" />
|
<span className="icon search" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -121,7 +123,7 @@ const MeasurementList = React.createClass({
|
||||||
{this.renderList()}
|
{this.renderList()}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
|
|
||||||
renderList() {
|
renderList() {
|
||||||
if (!this.props.query.database) {
|
if (!this.props.query.database) {
|
||||||
|
@ -193,7 +195,7 @@ const MeasurementList = React.createClass({
|
||||||
</FancyScrollbar>
|
</FancyScrollbar>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
|
|
||||||
_getMeasurements() {
|
_getMeasurements() {
|
||||||
const {source} = this.context
|
const {source} = this.context
|
||||||
|
@ -206,14 +208,14 @@ const MeasurementList = React.createClass({
|
||||||
const {errors, measurementSets} = showMeasurementsParser(resp.data)
|
const {errors, measurementSets} = showMeasurementsParser(resp.data)
|
||||||
if (errors.length) {
|
if (errors.length) {
|
||||||
// TODO: display errors in the UI.
|
// TODO: display errors in the UI.
|
||||||
return console.error('InfluxDB returned error(s): ', errors) // eslint-disable-line no-console
|
return console.error('InfluxDB returned error(s): ', errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
measurements: measurementSets[0].measurements,
|
measurements: measurementSets[0].measurements,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
export default MeasurementList
|
export default MeasurementList
|
|
@ -0,0 +1,159 @@
|
||||||
|
import React from 'react'
|
||||||
|
import MeasurementList from 'src/shared/components/MeasurementList'
|
||||||
|
import {shallow} from 'enzyme'
|
||||||
|
import {query, source} from 'test/resources'
|
||||||
|
|
||||||
|
const setup = (override = {}) => {
|
||||||
|
const props = {
|
||||||
|
query,
|
||||||
|
querySource: source,
|
||||||
|
onChooseTag: () => {},
|
||||||
|
onGroupByTag: () => {},
|
||||||
|
onToggleTagAcceptance: () => {},
|
||||||
|
onChooseMeasurement: () => {},
|
||||||
|
...override,
|
||||||
|
}
|
||||||
|
|
||||||
|
MeasurementList.prototype._getMeasurements = jest.fn(() => Promise.resolve())
|
||||||
|
|
||||||
|
const wrapper = shallow(<MeasurementList {...props} />, {
|
||||||
|
context: {source},
|
||||||
|
})
|
||||||
|
|
||||||
|
const instance = wrapper.instance() as MeasurementList
|
||||||
|
|
||||||
|
return {
|
||||||
|
props,
|
||||||
|
wrapper,
|
||||||
|
instance,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Shared.Components.MeasurementList', () => {
|
||||||
|
describe('rendering', () => {
|
||||||
|
it('renders to the page', () => {
|
||||||
|
const {wrapper} = setup()
|
||||||
|
|
||||||
|
expect(wrapper.exists()).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('lifecycle methods', () => {
|
||||||
|
describe('componentDidMount', () => {
|
||||||
|
it('does not fire getMeasurements if there is no database', () => {
|
||||||
|
const _getMeasurements = jest.fn()
|
||||||
|
const {instance} = setup({query: {...query, database: ''}})
|
||||||
|
instance._getMeasurements = _getMeasurements
|
||||||
|
instance.componentDidMount()
|
||||||
|
expect(_getMeasurements).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does fire _getMeasurements if there is a database', () => {
|
||||||
|
const _getMeasurements = jest.fn()
|
||||||
|
const {instance} = setup()
|
||||||
|
instance._getMeasurements = _getMeasurements
|
||||||
|
instance.componentDidMount()
|
||||||
|
expect(_getMeasurements).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('componentDidUpdate', () => {
|
||||||
|
it('does not fire getMeasurements if there is no database', () => {
|
||||||
|
const _getMeasurements = jest.fn()
|
||||||
|
const {instance, props} = setup({query: {...query, database: ''}})
|
||||||
|
|
||||||
|
instance._getMeasurements = _getMeasurements
|
||||||
|
instance.componentDidUpdate(props)
|
||||||
|
|
||||||
|
expect(_getMeasurements).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not fire _getMeasurements if the database does not change and the sources are equal', () => {
|
||||||
|
const _getMeasurements = jest.fn()
|
||||||
|
const {instance, props} = setup()
|
||||||
|
|
||||||
|
instance._getMeasurements = _getMeasurements
|
||||||
|
instance.componentDidUpdate(props)
|
||||||
|
|
||||||
|
expect(_getMeasurements).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('it fires _getMeasurements if there is a change in database', () => {
|
||||||
|
const _getMeasurements = jest.fn()
|
||||||
|
const {instance, props} = setup()
|
||||||
|
|
||||||
|
instance._getMeasurements = _getMeasurements
|
||||||
|
instance.componentDidUpdate({
|
||||||
|
...props,
|
||||||
|
query: {...query, database: 'diffDb'},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(_getMeasurements).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('it calls _getMeasurements if there is a change in source', () => {
|
||||||
|
const _getMeasurements = jest.fn()
|
||||||
|
const {instance, props} = setup()
|
||||||
|
|
||||||
|
instance._getMeasurements = _getMeasurements
|
||||||
|
instance.componentDidUpdate({
|
||||||
|
...props,
|
||||||
|
querySource: {...source, id: 'newSource'},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(_getMeasurements).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('instance methods', () => {
|
||||||
|
describe('handleFilterText', () => {
|
||||||
|
it('sets the filterText state to the event targets value', () => {
|
||||||
|
const {instance} = setup()
|
||||||
|
const value = 'spectacs'
|
||||||
|
const stopPropagation = () => {}
|
||||||
|
const event = {target: {value}, stopPropagation}
|
||||||
|
|
||||||
|
instance.handleFilterText(event)
|
||||||
|
|
||||||
|
expect(instance.state.filterText).toBe(value)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('handleEscape', () => {
|
||||||
|
it('resets fiterText when escape is pressed', () => {
|
||||||
|
const {instance} = setup()
|
||||||
|
const key = 'Escape'
|
||||||
|
const stopPropagation = () => {}
|
||||||
|
const event = {key, stopPropagation}
|
||||||
|
|
||||||
|
instance.setState({filterText: 'foo'})
|
||||||
|
expect(instance.state.filterText).toBe('foo')
|
||||||
|
|
||||||
|
instance.handleEscape(event)
|
||||||
|
expect(instance.state.filterText).toBe('')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not reset fiterText when escape is pressed', () => {
|
||||||
|
const {instance} = setup()
|
||||||
|
const key = 'Enter'
|
||||||
|
const stopPropagation = () => {}
|
||||||
|
const event = {key, stopPropagation}
|
||||||
|
|
||||||
|
instance.setState({filterText: 'foo'})
|
||||||
|
instance.handleEscape(event)
|
||||||
|
expect(instance.state.filterText).toBe('foo')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('handleAcceptReject', () => {
|
||||||
|
it('it calls onToggleTagAcceptance', () => {
|
||||||
|
const onToggleTagAcceptance = jest.fn()
|
||||||
|
const {instance} = setup({onToggleTagAcceptance})
|
||||||
|
|
||||||
|
instance.handleAcceptReject()
|
||||||
|
expect(onToggleTagAcceptance).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in New Issue