Convert MeasurementList to TypeScript and add tests
parent
e2ceba3560
commit
f4eca0a228
|
@ -23,10 +23,6 @@ interface DatabaseListState {
|
|||
namespaces: Namespace[]
|
||||
}
|
||||
|
||||
export interface DatabaseListContext {
|
||||
source: Source
|
||||
}
|
||||
|
||||
const {shape, string} = PropTypes
|
||||
|
||||
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 _ from 'lodash'
|
||||
|
||||
import {showMeasurements} from 'shared/apis/metaQuery'
|
||||
import showMeasurementsParser from 'shared/parsing/showMeasurements'
|
||||
import {showMeasurements} from 'src/shared/apis/metaQuery'
|
||||
import showMeasurementsParser from 'src/shared/parsing/showMeasurements'
|
||||
|
||||
import {Query, Source} from 'src/types'
|
||||
|
||||
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({
|
||||
propTypes: {
|
||||
query: shape({
|
||||
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,
|
||||
}),
|
||||
},
|
||||
interface State {
|
||||
measurements: string[]
|
||||
filterText: string
|
||||
}
|
||||
|
||||
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({
|
||||
links: shape({
|
||||
proxy: string.isRequired,
|
||||
}).isRequired,
|
||||
}).isRequired,
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
measurements: [],
|
||||
filterText: '',
|
||||
}
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
return {
|
||||
querySource: null,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (!this.props.query.database) {
|
||||
|
@ -54,7 +58,7 @@ const MeasurementList = React.createClass({
|
|||
}
|
||||
|
||||
this._getMeasurements()
|
||||
},
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const {query, querySource} = this.props
|
||||
|
@ -71,14 +75,14 @@ const MeasurementList = React.createClass({
|
|||
}
|
||||
|
||||
this._getMeasurements()
|
||||
},
|
||||
}
|
||||
|
||||
handleFilterText(e) {
|
||||
e.stopPropagation()
|
||||
this.setState({
|
||||
filterText: this.refs.filterText.value,
|
||||
filterText: e.target.value,
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
handleEscape(e) {
|
||||
if (e.key !== 'Escape') {
|
||||
|
@ -89,12 +93,11 @@ const MeasurementList = React.createClass({
|
|||
this.setState({
|
||||
filterText: '',
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
handleAcceptReject(e) {
|
||||
e.stopPropagation()
|
||||
handleAcceptReject() {
|
||||
this.props.onToggleTagAcceptance()
|
||||
},
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
|
@ -105,14 +108,13 @@ const MeasurementList = React.createClass({
|
|||
? <div className="query-builder--filter">
|
||||
<input
|
||||
className="form-control input-sm"
|
||||
ref="filterText"
|
||||
placeholder="Filter"
|
||||
type="text"
|
||||
value={this.state.filterText}
|
||||
onChange={this.handleFilterText}
|
||||
onKeyUp={this.handleEscape}
|
||||
spellCheck={false}
|
||||
autoComplete={false}
|
||||
autoComplete="false"
|
||||
/>
|
||||
<span className="icon search" />
|
||||
</div>
|
||||
|
@ -121,7 +123,7 @@ const MeasurementList = React.createClass({
|
|||
{this.renderList()}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
renderList() {
|
||||
if (!this.props.query.database) {
|
||||
|
@ -193,7 +195,7 @@ const MeasurementList = React.createClass({
|
|||
</FancyScrollbar>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
_getMeasurements() {
|
||||
const {source} = this.context
|
||||
|
@ -206,14 +208,14 @@ const MeasurementList = React.createClass({
|
|||
const {errors, measurementSets} = showMeasurementsParser(resp.data)
|
||||
if (errors.length) {
|
||||
// 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({
|
||||
measurements: measurementSets[0].measurements,
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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