Merge branch 'develop' into trace-filter-toolip

This commit is contained in:
Palash 2022-06-24 11:33:05 +05:30 committed by GitHub
commit 31848c488d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 219 additions and 70 deletions

View File

@ -1,12 +1,19 @@
import React from 'react'; import { Button, Input } from 'antd';
import { useSelector } from 'react-redux'; import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Dispatch } from 'redux';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import { INITIAL_FILTER_VALUE } from 'store/reducers/trace';
import AppActions from 'types/actions';
import { UPDATE_SPAN_UPDATE_FILTER_DISPLAY_VALUE } from 'types/actions/trace';
import { TraceFilterEnum, TraceReducer } from 'types/reducer/trace'; import { TraceFilterEnum, TraceReducer } from 'types/reducer/trace';
import CheckBoxComponent from '../Common/Checkbox'; import CheckBoxComponent from '../Common/Checkbox';
const { Search } = Input;
function CommonCheckBox(props: CommonCheckBoxProps): JSX.Element { function CommonCheckBox(props: CommonCheckBoxProps): JSX.Element {
const { filter } = useSelector<AppState, TraceReducer>( const { filter, filterDisplayValue } = useSelector<AppState, TraceReducer>(
(state) => state.traces, (state) => state.traces,
); );
@ -15,9 +22,40 @@ function CommonCheckBox(props: CommonCheckBoxProps): JSX.Element {
const status = filter.get(name) || {}; const status = filter.get(name) || {};
const statusObj = Object.keys(status); const statusObj = Object.keys(status);
const numberOfFilters = filterDisplayValue.get(name) || 0;
const dispatch = useDispatch<Dispatch<AppActions>>();
const [searchFilter, setSearchFilter] = useState<string>('');
const onClickMoreHandler = (): void => {
const newFilterDisplayValue = new Map(filterDisplayValue);
const preValue =
(newFilterDisplayValue.get(name) || 0) + INITIAL_FILTER_VALUE;
newFilterDisplayValue.set(name, preValue);
dispatch({
type: UPDATE_SPAN_UPDATE_FILTER_DISPLAY_VALUE,
payload: newFilterDisplayValue,
});
};
const isMoreButtonAvilable = Boolean(
numberOfFilters && statusObj.length > numberOfFilters,
);
return ( return (
<> <>
{statusObj.length > 0 && (
<Search
value={searchFilter}
onChange={(e): void => setSearchFilter(e.target.value)}
style={{
padding: '0 3%',
}}
placeholder="Filter Values"
/>
)}
{statusObj {statusObj
.sort((a, b) => { .sort((a, b) => {
const countA = +status[a]; const countA = +status[a];
@ -28,6 +66,15 @@ function CommonCheckBox(props: CommonCheckBoxProps): JSX.Element {
} }
return countA - countB; return countA - countB;
}) })
.filter((filter) => {
if (searchFilter.length === 0) {
return true;
}
return filter
.toLocaleLowerCase()
.includes(searchFilter.toLocaleLowerCase());
})
.filter((_, index) => index < numberOfFilters)
.map((e) => ( .map((e) => (
<CheckBoxComponent <CheckBoxComponent
key={e} key={e}
@ -38,6 +85,12 @@ function CommonCheckBox(props: CommonCheckBoxProps): JSX.Element {
}} }}
/> />
))} ))}
{isMoreButtonAvilable && (
<Button onClick={onClickMoreHandler} type="link">
More
</Button>
)}
</> </>
); );
} }

View File

@ -1,11 +1,14 @@
/* eslint-disable react/no-unstable-nested-components */ import { Slider } from 'antd';
import { Input, Slider } from 'antd';
import { SliderRangeProps } from 'antd/lib/slider'; import { SliderRangeProps } from 'antd/lib/slider';
import getFilters from 'api/trace/getFilters'; import getFilters from 'api/trace/getFilters';
import dayjs from 'dayjs';
import durationPlugin from 'dayjs/plugin/duration';
import useDebouncedFn from 'hooks/useDebouncedFunction'; import useDebouncedFn from 'hooks/useDebouncedFunction';
import React, { useEffect, useMemo, useRef, useState } from 'react'; import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { Dispatch } from 'redux'; import { Dispatch } from 'redux';
import { getFilter, updateURL } from 'store/actions/trace/util'; import { getFilter, updateURL } from 'store/actions/trace/util';
@ -15,19 +18,8 @@ import { UPDATE_ALL_FILTERS } from 'types/actions/trace';
import { GlobalReducer } from 'types/reducer/globalTime'; import { GlobalReducer } from 'types/reducer/globalTime';
import { TraceReducer } from 'types/reducer/trace'; import { TraceReducer } from 'types/reducer/trace';
import { Container, InputContainer, Text } from './styles'; import { Container, InputComponent, InputContainer, Text } from './styles';
import { getMs } from './util';
dayjs.extend(durationPlugin);
const getMs = (value: string): string => {
return parseFloat(
dayjs
.duration({
milliseconds: parseInt(value, 10) / 1000000,
})
.format('SSS'),
).toFixed(2);
};
function Duration(): JSX.Element { function Duration(): JSX.Element {
const { const {
@ -77,17 +69,18 @@ function Duration(): JSX.Element {
preLocalMinDuration.current = parseFloat(minDuration); preLocalMinDuration.current = parseFloat(minDuration);
} }
setPreMax(maxDuration); setPreMax(getMs(maxDuration));
setPreMin(minDuration); setPreMin(getMs(minDuration));
}, [getDuration]); }, [getDuration]);
const defaultValue = [parseFloat(preMin), parseFloat(preMax)];
const updatedUrl = async (min: number, max: number): Promise<void> => { const updatedUrl = async (min: number, max: number): Promise<void> => {
const preSelectedFilter = new Map(selectedFilter); const preSelectedFilter = new Map(selectedFilter);
const preUserSelected = new Map(userSelectedFilter); const preUserSelected = new Map(userSelectedFilter);
preSelectedFilter.set('duration', [String(max), String(min)]); preSelectedFilter.set('duration', [
String(max * 1000000),
String(min * 1000000),
]);
const response = await getFilters({ const response = await getFilters({
end: String(globalTime.maxTime), end: String(globalTime.maxTime),
@ -137,18 +130,18 @@ function Duration(): JSX.Element {
} }
}; };
const onRangeSliderHandler = (number: [number, number]): void => { const onRangeSliderHandler = (number: [string, string]): void => {
const [min, max] = number; const [min, max] = number;
setPreMin(min.toString()); setPreMin(min);
setPreMax(max.toString()); setPreMax(max);
}; };
const debouncedFunction = useDebouncedFn( const debouncedFunction = useDebouncedFn(
(min, max) => { (min, max) => {
updatedUrl(min as number, max as number); updatedUrl(min as number, max as number);
}, },
500, 1500,
undefined, undefined,
); );
@ -156,8 +149,8 @@ function Duration(): JSX.Element {
event, event,
) => { ) => {
const { value } = event.target; const { value } = event.target;
const min = parseFloat(preMin); const min = preMin;
const max = parseFloat(value) * 1000000; const max = value;
onRangeSliderHandler([min, max]); onRangeSliderHandler([min, max]);
debouncedFunction(min, max); debouncedFunction(min, max);
@ -167,8 +160,9 @@ function Duration(): JSX.Element {
event, event,
) => { ) => {
const { value } = event.target; const { value } = event.target;
const min = parseFloat(value) * 1000000; const min = value;
const max = parseFloat(preMax); const max = preMax;
onRangeSliderHandler([min, max]); onRangeSliderHandler([min, max]);
debouncedFunction(min, max); debouncedFunction(min, max);
}; };
@ -177,45 +171,48 @@ function Duration(): JSX.Element {
updatedUrl(min, max); updatedUrl(min, max);
}; };
const TipComponent = useCallback((value) => {
if (value === undefined) {
return <div />;
}
return <div>{`${getMs(value?.toString())}ms`}</div>;
}, []);
return ( return (
<div> <div>
<Container> <Container>
<InputContainer> <InputContainer>
<Text>Min</Text> <Text>Min</Text>
</InputContainer> </InputContainer>
<Input <InputComponent
addonAfter="ms" addonAfter="ms"
type="number"
onChange={onChangeMinHandler} onChange={onChangeMinHandler}
value={getMs(preMin)} value={preMin}
/> />
<InputContainer> <InputContainer>
<Text>Max</Text> <Text>Max</Text>
</InputContainer> </InputContainer>
<Input <InputComponent
addonAfter="ms" addonAfter="ms"
type="number"
onChange={onChangeMaxHandler} onChange={onChangeMaxHandler}
value={getMs(preMax)} value={preMax}
/> />
</Container> </Container>
<Container> <Container>
<Slider <Slider
defaultValue={[defaultValue[0], defaultValue[1]]} min={Number(getMs(String(preLocalMinDuration.current || 0)))}
min={parseFloat((preLocalMinDuration.current || 0).toString())} max={Number(getMs(String(preLocalMaxDuration.current || 0)))}
max={parseFloat((preLocalMaxDuration.current || 0).toString())}
range range
tipFormatter={(value): JSX.Element => { tipFormatter={TipComponent}
if (value === undefined) {
return <div />;
}
return <div>{`${getMs(value?.toString())}ms`}</div>;
}}
onChange={([min, max]): void => { onChange={([min, max]): void => {
onRangeSliderHandler([min, max]); onRangeSliderHandler([String(min), String(max)]);
}} }}
onAfterChange={onRangeHandler} onAfterChange={onRangeHandler}
value={[parseFloat(preMin), parseFloat(preMax)]} value={[Number(preMin), Number(preMax)]}
/> />
</Container> </Container>
</div> </div>

View File

@ -1,4 +1,4 @@
import { Typography } from 'antd'; import { Input, Typography } from 'antd';
import styled from 'styled-components'; import styled from 'styled-components';
export const DurationText = styled.div` export const DurationText = styled.div`
@ -9,6 +9,19 @@ export const DurationText = styled.div`
flex-direction: column; flex-direction: column;
`; `;
export const InputComponent = styled(Input)`
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input[type='number'] {
-moz-appearance: textfield;
}
`;
export const InputContainer = styled.div` export const InputContainer = styled.div`
width: 100%; width: 100%;
margin-top: 0.5rem; margin-top: 0.5rem;

View File

@ -0,0 +1,13 @@
import dayjs from 'dayjs';
import durationPlugin from 'dayjs/plugin/duration';
dayjs.extend(durationPlugin);
export const getMs = (value: string): string =>
parseFloat(
dayjs
.duration({
milliseconds: parseInt(value, 10) / 1000000,
})
.format('SSS'),
).toFixed(2);

View File

@ -73,11 +73,24 @@ function TagsKey(props: TagsKeysProps): JSX.Element {
<AutoComplete <AutoComplete
dropdownClassName="certain-category-search-dropdown" dropdownClassName="certain-category-search-dropdown"
dropdownMatchSelectWidth={500} dropdownMatchSelectWidth={500}
style={{ width: 300 }} style={{ width: '100%' }}
options={options}
value={selectedKey} value={selectedKey}
onChange={(value): void => { allowClear
if (options && options.find((option) => option.value === value)) { showSearch
options={options?.map((e) => ({
label: e.label?.toString(),
value: e.value,
}))}
filterOption={(inputValue, option): boolean =>
option?.label?.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1
}
onChange={(e): void => setSelectedKey(e)}
onSelect={(value: unknown): void => {
if (
typeof value === 'string' &&
options &&
options.find((option) => option.value === value)
) {
setSelectedKey(value); setSelectedKey(value);
setLocalSelectedTags((tags) => [ setLocalSelectedTags((tags) => [
@ -89,8 +102,6 @@ function TagsKey(props: TagsKeysProps): JSX.Element {
}, },
...tags.slice(index + 1, tags.length), ...tags.slice(index + 1, tags.length),
]); ]);
} else {
setSelectedKey('');
} }
}} }}
> >

View File

@ -1,13 +1,13 @@
import { Select } from 'antd'; import { Select } from 'antd';
import getTagValue from 'api/trace/getTagValue'; import getTagValue from 'api/trace/getTagValue';
import React from 'react'; import React, { useState } from 'react';
import { useQuery } from 'react-query'; import { useQuery } from 'react-query';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import { GlobalReducer } from 'types/reducer/globalTime'; import { GlobalReducer } from 'types/reducer/globalTime';
import { TraceReducer } from 'types/reducer/trace'; import { TraceReducer } from 'types/reducer/trace';
import { SelectComponent } from './styles'; import { AutoCompleteComponent } from './styles';
function TagValue(props: TagValueProps): JSX.Element { function TagValue(props: TagValueProps): JSX.Element {
const { tag, setLocalSelectedTags, index, tagKey } = props; const { tag, setLocalSelectedTags, index, tagKey } = props;
@ -16,6 +16,7 @@ function TagValue(props: TagValueProps): JSX.Element {
Operator: selectedOperator, Operator: selectedOperator,
Values: selectedValues, Values: selectedValues,
} = tag; } = tag;
const [localValue, setLocalValue] = useState<string>(selectedValues[0]);
const globalReducer = useSelector<AppState, GlobalReducer>( const globalReducer = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime, (state) => state.globalTime,
@ -34,22 +35,38 @@ function TagValue(props: TagValueProps): JSX.Element {
); );
return ( return (
<SelectComponent <AutoCompleteComponent
value={selectedValues[0]} options={data?.payload?.map((e) => ({
label: e.tagValues,
value: e.tagValues,
}))}
allowClear
defaultOpen
showSearch
filterOption={(inputValue, option): boolean =>
option?.label.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1
}
disabled={isLoading}
value={localValue}
onChange={(values): void => {
if (typeof values === 'string') {
setLocalValue(values);
}
}}
onSelect={(value: unknown): void => { onSelect={(value: unknown): void => {
if (typeof value === 'string') { if (typeof value === 'string') {
setLocalValue(value);
setLocalSelectedTags((tags) => [ setLocalSelectedTags((tags) => [
...tags.slice(0, index), ...tags.slice(0, index),
{ {
Key: selectedKey, Key: selectedKey,
Operator: selectedOperator, Operator: selectedOperator,
Values: [...selectedValues, value], Values: [value],
}, },
...tags.slice(index + 1, tags.length), ...tags.slice(index + 1, tags.length),
]); ]);
} }
}} }}
loading={isLoading || false}
> >
{data && {data &&
data.payload && data.payload &&
@ -58,7 +75,7 @@ function TagValue(props: TagValueProps): JSX.Element {
{suggestion.tagValues} {suggestion.tagValues}
</Select.Option> </Select.Option>
))} ))}
</SelectComponent> </AutoCompleteComponent>
); );
} }

View File

@ -1,4 +1,4 @@
import { Select, Space } from 'antd'; import { AutoComplete, Select, Space } from 'antd';
import styled from 'styled-components'; import styled from 'styled-components';
export const SpaceComponent = styled(Space)` export const SpaceComponent = styled(Space)`
@ -9,18 +9,23 @@ export const SpaceComponent = styled(Space)`
export const SelectComponent = styled(Select)` export const SelectComponent = styled(Select)`
&&& { &&& {
min-width: 170px; width: 100%;
margin-right: 21.91px;
margin-left: 21.92px;
} }
`; `;
export const Container = styled.div` export const Container = styled(Space)`
&&& { &&& {
display: flex; display: flex;
margin-top: 1rem; margin-top: 1rem;
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.ant-space-item:not(:last-child, :nth-child(2)) {
width: 100%;
}
.ant-space-item:nth-child(2) {
width: 50%;
}
`; `;
export const IconContainer = styled.div` export const IconContainer = styled.div`
@ -31,3 +36,9 @@ export const IconContainer = styled.div`
margin-left: 1.125rem; margin-left: 1.125rem;
`; `;
export const AutoCompleteComponent = styled(AutoComplete)`
&&& {
width: 100%;
}
`;

View File

@ -1,4 +1,8 @@
import { applyMiddleware, compose, createStore } from 'redux'; import {
applyMiddleware,
compose,
legacy_createStore as createStore,
} from 'redux';
import thunk, { ThunkMiddleware } from 'redux-thunk'; import thunk, { ThunkMiddleware } from 'redux-thunk';
import AppActions from 'types/actions'; import AppActions from 'types/actions';

View File

@ -11,6 +11,7 @@ import {
UPDATE_SELECTED_TAGS, UPDATE_SELECTED_TAGS,
UPDATE_SPAN_ORDER, UPDATE_SPAN_ORDER,
UPDATE_SPAN_ORDER_PARAMS, UPDATE_SPAN_ORDER_PARAMS,
UPDATE_SPAN_UPDATE_FILTER_DISPLAY_VALUE,
UPDATE_SPANS_AGGREGATE, UPDATE_SPANS_AGGREGATE,
UPDATE_SPANS_AGGREGATE_PAGE_NUMBER, UPDATE_SPANS_AGGREGATE_PAGE_NUMBER,
UPDATE_SPANS_AGGREGATE_PAGE_SIZE, UPDATE_SPANS_AGGREGATE_PAGE_SIZE,
@ -23,6 +24,8 @@ import {
} from 'types/actions/trace'; } from 'types/actions/trace';
import { TraceFilterEnum, TraceReducer } from 'types/reducer/trace'; import { TraceFilterEnum, TraceReducer } from 'types/reducer/trace';
export const INITIAL_FILTER_VALUE = 8;
const initialValue: TraceReducer = { const initialValue: TraceReducer = {
filter: new Map(), filter: new Map(),
filterToFetchData: ['duration', 'status', 'serviceName'], filterToFetchData: ['duration', 'status', 'serviceName'],
@ -53,6 +56,17 @@ const initialValue: TraceReducer = {
loading: true, loading: true,
payload: { items: {} }, payload: { items: {} },
}, },
filterDisplayValue: new Map<TraceFilterEnum, number>([
['component', INITIAL_FILTER_VALUE],
['duration', INITIAL_FILTER_VALUE],
['httpCode', INITIAL_FILTER_VALUE],
['httpHost', INITIAL_FILTER_VALUE],
['httpMethod', INITIAL_FILTER_VALUE],
['httpUrl', INITIAL_FILTER_VALUE],
['operation', INITIAL_FILTER_VALUE],
['serviceName', INITIAL_FILTER_VALUE],
['status', INITIAL_FILTER_VALUE],
]),
}; };
const traceReducer = ( const traceReducer = (
@ -251,6 +265,13 @@ const traceReducer = (
}; };
} }
case UPDATE_SPAN_UPDATE_FILTER_DISPLAY_VALUE: {
return {
...state,
filterDisplayValue: action.payload,
};
}
default: default:
return state; return state;
} }

View File

@ -31,6 +31,8 @@ export const UPDATE_SPANS_AGGREGATE_PAGE_NUMBER =
export const UPDATE_SPANS_AGGREGATE_PAGE_SIZE = export const UPDATE_SPANS_AGGREGATE_PAGE_SIZE =
'UPDATE_SPANS_AGGREGATE_PAGE_SIZE'; 'UPDATE_SPANS_AGGREGATE_PAGE_SIZE';
export const UPDATE_SPAN_ORDER_PARAMS = 'UPDATE_SPAN_ORDER_PARAMS'; export const UPDATE_SPAN_ORDER_PARAMS = 'UPDATE_SPAN_ORDER_PARAMS';
export const UPDATE_SPAN_UPDATE_FILTER_DISPLAY_VALUE =
'UPDATE_SPAN_UPDATE_FILTER_DISPLAY_VALUE';
export interface UpdateFilter { export interface UpdateFilter {
type: typeof UPDATE_TRACE_FILTER; type: typeof UPDATE_TRACE_FILTER;
@ -187,6 +189,11 @@ export interface UpdateSpanParams {
}; };
} }
export interface UpdateTraceFilterDisplayValue {
type: typeof UPDATE_SPAN_UPDATE_FILTER_DISPLAY_VALUE;
payload: TraceReducer['filterDisplayValue'];
}
export type TraceActions = export type TraceActions =
| UpdateFilter | UpdateFilter
| GetTraceFilter | GetTraceFilter
@ -208,4 +215,5 @@ export type TraceActions =
| UpdateSpanOrder | UpdateSpanOrder
| UpdateSpansAggregatePageNumber | UpdateSpansAggregatePageNumber
| UpdateSpanSize | UpdateSpanSize
| UpdateSpanParams; | UpdateSpanParams
| UpdateTraceFilterDisplayValue;

View File

@ -32,6 +32,7 @@ export interface TraceReducer {
payload: PayloadProps; payload: PayloadProps;
}; };
yAxisUnit: string | undefined; yAxisUnit: string | undefined;
filterDisplayValue: Map<TraceFilterEnum, number>;
} }
interface SpansAggregateData { interface SpansAggregateData {