diff --git a/web/ui/react-app/src/pages/graph/MetricsExplorer.test.tsx b/web/ui/react-app/src/pages/graph/MetricsExplorer.test.tsx new file mode 100644 index 000000000..d864fb349 --- /dev/null +++ b/web/ui/react-app/src/pages/graph/MetricsExplorer.test.tsx @@ -0,0 +1,46 @@ +import * as React from 'react'; +import { mount, ReactWrapper } from 'enzyme'; +import MetricsExplorer from './MetricsExplorer'; +import { Input } from 'reactstrap'; + +describe('MetricsExplorer', () => { + const spyInsertAtCursor = jest.fn().mockImplementation((value: string) => { + value = value; + }); + const metricsExplorerProps = { + show: true, + updateShow: (show: boolean): void => { + show = show; + }, + metrics: ['go_test_1', 'prometheus_test_1'], + insertAtCursor: spyInsertAtCursor, + }; + + let metricsExplorer: ReactWrapper; + beforeEach(() => { + metricsExplorer = mount(); + }); + + it('renders an Input[type=text]', () => { + const input = metricsExplorer.find(Input); + expect(input.prop('type')).toEqual('text'); + }); + + it('lists all metrics in props', () => { + const metrics = metricsExplorer.find('.metric'); + expect(metrics).toHaveLength(metricsExplorerProps.metrics.length); + }); + + it('filters metrics with search', () => { + const input = metricsExplorer.find(Input); + input.simulate('change', { target: { value: 'go' } }); + const metrics = metricsExplorer.find('.metric'); + expect(metrics).toHaveLength(1); + }); + + it('handles click on metric', () => { + const metric = metricsExplorer.find('.metric').at(0); + metric.simulate('click'); + expect(metricsExplorerProps.insertAtCursor).toHaveBeenCalled(); + }); +}); diff --git a/web/ui/react-app/src/pages/graph/MetricsExplorer.tsx b/web/ui/react-app/src/pages/graph/MetricsExplorer.tsx index 8c01ba757..1959fa826 100644 --- a/web/ui/react-app/src/pages/graph/MetricsExplorer.tsx +++ b/web/ui/react-app/src/pages/graph/MetricsExplorer.tsx @@ -1,33 +1,60 @@ -import React, { Component } from 'react'; -import { Modal, ModalBody, ModalHeader } from 'reactstrap'; +import React, { Component, ChangeEvent } from 'react'; +import { Modal, ModalBody, ModalHeader, Input } from 'reactstrap'; +import { Fuzzy, FuzzyResult } from '@nexucis/fuzzy'; -interface Props { +const fuz = new Fuzzy({ pre: '', post: '', shouldSort: true }); + +interface MetricsExplorerProps { show: boolean; updateShow(show: boolean): void; metrics: string[]; insertAtCursor(value: string): void; } -class MetricsExplorer extends Component { +type MetricsExplorerState = { + searchTerm: string; +}; + +class MetricsExplorer extends Component { + constructor(props: MetricsExplorerProps) { + super(props); + this.state = { searchTerm: '' }; + } + handleSearchTerm = (event: ChangeEvent): void => { + this.setState({ searchTerm: event.target.value }); + }; handleMetricClick = (query: string): void => { this.props.insertAtCursor(query); this.props.updateShow(false); + this.setState({ searchTerm: '' }); }; toggle = (): void => { this.props.updateShow(!this.props.show); }; - render(): JSX.Element { return ( - + Metrics Explorer - {this.props.metrics.map((metric) => ( -

- {metric} -

- ))} + + {this.state.searchTerm.length > 0 && + fuz + .filter(this.state.searchTerm, this.props.metrics) + .map((result: FuzzyResult) => ( +

+ ))} + {this.state.searchTerm.length === 0 && + this.props.metrics.map((metric) => ( +

+ {metric} +

+ ))}
);