mirror of
https://github.com/prometheus/prometheus.git
synced 2024-11-09 23:24:05 -08:00
Feat UI metrics search (#9629)
* feat: add search to metrics explorer Signed-off-by: mtfoley <mtfoley.mae@gmail.com> * fix: ui-lint and ui-build errors Signed-off-by: mtfoley <mtfoley.mae@gmail.com> * feat: use @nexucis/fuzzy Signed-off-by: mtfoley <mtfoley.mae@gmail.com> * chore: code style and delete commented test Signed-off-by: mtfoley <mtfoley.mae@gmail.com> * rename Props to MetricsExplorerProps Signed-off-by: mtfoley <mtfoley.mae@gmail.com>
This commit is contained in:
parent
4caae4e4a6
commit
628211c25a
46
web/ui/react-app/src/pages/graph/MetricsExplorer.test.tsx
Normal file
46
web/ui/react-app/src/pages/graph/MetricsExplorer.test.tsx
Normal file
|
@ -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(<MetricsExplorer {...metricsExplorerProps} />);
|
||||||
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,33 +1,60 @@
|
||||||
import React, { Component } from 'react';
|
import React, { Component, ChangeEvent } from 'react';
|
||||||
import { Modal, ModalBody, ModalHeader } from 'reactstrap';
|
import { Modal, ModalBody, ModalHeader, Input } from 'reactstrap';
|
||||||
|
import { Fuzzy, FuzzyResult } from '@nexucis/fuzzy';
|
||||||
|
|
||||||
interface Props {
|
const fuz = new Fuzzy({ pre: '<strong>', post: '</strong>', shouldSort: true });
|
||||||
|
|
||||||
|
interface MetricsExplorerProps {
|
||||||
show: boolean;
|
show: boolean;
|
||||||
updateShow(show: boolean): void;
|
updateShow(show: boolean): void;
|
||||||
metrics: string[];
|
metrics: string[];
|
||||||
insertAtCursor(value: string): void;
|
insertAtCursor(value: string): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MetricsExplorer extends Component<Props> {
|
type MetricsExplorerState = {
|
||||||
|
searchTerm: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MetricsExplorer extends Component<MetricsExplorerProps, MetricsExplorerState> {
|
||||||
|
constructor(props: MetricsExplorerProps) {
|
||||||
|
super(props);
|
||||||
|
this.state = { searchTerm: '' };
|
||||||
|
}
|
||||||
|
handleSearchTerm = (event: ChangeEvent<HTMLInputElement>): void => {
|
||||||
|
this.setState({ searchTerm: event.target.value });
|
||||||
|
};
|
||||||
handleMetricClick = (query: string): void => {
|
handleMetricClick = (query: string): void => {
|
||||||
this.props.insertAtCursor(query);
|
this.props.insertAtCursor(query);
|
||||||
this.props.updateShow(false);
|
this.props.updateShow(false);
|
||||||
|
this.setState({ searchTerm: '' });
|
||||||
};
|
};
|
||||||
|
|
||||||
toggle = (): void => {
|
toggle = (): void => {
|
||||||
this.props.updateShow(!this.props.show);
|
this.props.updateShow(!this.props.show);
|
||||||
};
|
};
|
||||||
|
|
||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={this.props.show} toggle={this.toggle} className="metrics-explorer">
|
<Modal isOpen={this.props.show} toggle={this.toggle} className="metrics-explorer" scrollable>
|
||||||
<ModalHeader toggle={this.toggle}>Metrics Explorer</ModalHeader>
|
<ModalHeader toggle={this.toggle}>Metrics Explorer</ModalHeader>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
{this.props.metrics.map((metric) => (
|
<Input placeholder="Search" value={this.state.searchTerm} type="text" onChange={this.handleSearchTerm} />
|
||||||
<p key={metric} className="metric" onClick={this.handleMetricClick.bind(this, metric)}>
|
{this.state.searchTerm.length > 0 &&
|
||||||
{metric}
|
fuz
|
||||||
</p>
|
.filter(this.state.searchTerm, this.props.metrics)
|
||||||
))}
|
.map((result: FuzzyResult) => (
|
||||||
|
<p
|
||||||
|
key={result.original}
|
||||||
|
className="metric"
|
||||||
|
onClick={this.handleMetricClick.bind(this, result.original)}
|
||||||
|
dangerouslySetInnerHTML={{ __html: result.rendered }}
|
||||||
|
></p>
|
||||||
|
))}
|
||||||
|
{this.state.searchTerm.length === 0 &&
|
||||||
|
this.props.metrics.map((metric) => (
|
||||||
|
<p key={metric} className="metric" onClick={this.handleMetricClick.bind(this, metric)}>
|
||||||
|
{metric}
|
||||||
|
</p>
|
||||||
|
))}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue