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 { useSelector } from 'react-redux';
import { Button, Input } from 'antd';
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Dispatch } from 'redux';
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 CheckBoxComponent from '../Common/Checkbox';
const { Search } = Input;
function CommonCheckBox(props: CommonCheckBoxProps): JSX.Element {
const { filter } = useSelector<AppState, TraceReducer>(
const { filter, filterDisplayValue } = useSelector<AppState, TraceReducer>(
(state) => state.traces,
);
@ -15,9 +22,40 @@ function CommonCheckBox(props: CommonCheckBoxProps): JSX.Element {
const status = filter.get(name) || {};
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 (
<>
{statusObj.length > 0 && (
<Search
value={searchFilter}
onChange={(e): void => setSearchFilter(e.target.value)}
style={{
padding: '0 3%',
}}
placeholder="Filter Values"
/>
)}
{statusObj
.sort((a, b) => {
const countA = +status[a];
@ -28,6 +66,15 @@ function CommonCheckBox(props: CommonCheckBoxProps): JSX.Element {
}
return countA - countB;
})
.filter((filter) => {
if (searchFilter.length === 0) {
return true;
}
return filter
.toLocaleLowerCase()
.includes(searchFilter.toLocaleLowerCase());
})
.filter((_, index) => index < numberOfFilters)
.map((e) => (
<CheckBoxComponent
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 { Input, Slider } from 'antd';
import { Slider } from 'antd';
import { SliderRangeProps } from 'antd/lib/slider';
import getFilters from 'api/trace/getFilters';
import dayjs from 'dayjs';
import durationPlugin from 'dayjs/plugin/duration';
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 { Dispatch } from 'redux';
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 { TraceReducer } from 'types/reducer/trace';
import { Container, InputContainer, Text } from './styles';
dayjs.extend(durationPlugin);
const getMs = (value: string): string => {
return parseFloat(
dayjs
.duration({
milliseconds: parseInt(value, 10) / 1000000,
})
.format('SSS'),
).toFixed(2);
};
import { Container, InputComponent, InputContainer, Text } from './styles';
import { getMs } from './util';
function Duration(): JSX.Element {
const {
@ -77,17 +69,18 @@ function Duration(): JSX.Element {
preLocalMinDuration.current = parseFloat(minDuration);
}
setPreMax(maxDuration);
setPreMin(minDuration);
setPreMax(getMs(maxDuration));
setPreMin(getMs(minDuration));
}, [getDuration]);
const defaultValue = [parseFloat(preMin), parseFloat(preMax)];
const updatedUrl = async (min: number, max: number): Promise<void> => {
const preSelectedFilter = new Map(selectedFilter);
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({
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;
setPreMin(min.toString());
setPreMax(max.toString());
setPreMin(min);
setPreMax(max);
};
const debouncedFunction = useDebouncedFn(
(min, max) => {
updatedUrl(min as number, max as number);
},
500,
1500,
undefined,
);
@ -156,8 +149,8 @@ function Duration(): JSX.Element {
event,
) => {
const { value } = event.target;
const min = parseFloat(preMin);
const max = parseFloat(value) * 1000000;
const min = preMin;
const max = value;
onRangeSliderHandler([min, max]);
debouncedFunction(min, max);
@ -167,8 +160,9 @@ function Duration(): JSX.Element {
event,
) => {
const { value } = event.target;
const min = parseFloat(value) * 1000000;
const max = parseFloat(preMax);
const min = value;
const max = preMax;
onRangeSliderHandler([min, max]);
debouncedFunction(min, max);
};
@ -177,45 +171,48 @@ function Duration(): JSX.Element {
updatedUrl(min, max);
};
const TipComponent = useCallback((value) => {
if (value === undefined) {
return <div />;
}
return <div>{`${getMs(value?.toString())}ms`}</div>;
}, []);
return (
<div>
<Container>
<InputContainer>
<Text>Min</Text>
</InputContainer>
<Input
<InputComponent
addonAfter="ms"
type="number"
onChange={onChangeMinHandler}
value={getMs(preMin)}
value={preMin}
/>
<InputContainer>
<Text>Max</Text>
</InputContainer>
<Input
<InputComponent
addonAfter="ms"
type="number"
onChange={onChangeMaxHandler}
value={getMs(preMax)}
value={preMax}
/>
</Container>
<Container>
<Slider
defaultValue={[defaultValue[0], defaultValue[1]]}
min={parseFloat((preLocalMinDuration.current || 0).toString())}
max={parseFloat((preLocalMaxDuration.current || 0).toString())}
min={Number(getMs(String(preLocalMinDuration.current || 0)))}
max={Number(getMs(String(preLocalMaxDuration.current || 0)))}
range
tipFormatter={(value): JSX.Element => {
if (value === undefined) {
return <div />;
}
return <div>{`${getMs(value?.toString())}ms`}</div>;
}}
tipFormatter={TipComponent}
onChange={([min, max]): void => {
onRangeSliderHandler([min, max]);
onRangeSliderHandler([String(min), String(max)]);
}}
onAfterChange={onRangeHandler}
value={[parseFloat(preMin), parseFloat(preMax)]}
value={[Number(preMin), Number(preMax)]}
/>
</Container>
</div>

View File

@ -1,4 +1,4 @@
import { Typography } from 'antd';
import { Input, Typography } from 'antd';
import styled from 'styled-components';
export const DurationText = styled.div`
@ -9,6 +9,19 @@ export const DurationText = styled.div`
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`
width: 100%;
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
dropdownClassName="certain-category-search-dropdown"
dropdownMatchSelectWidth={500}
style={{ width: 300 }}
options={options}
style={{ width: '100%' }}
value={selectedKey}
onChange={(value): void => {
if (options && options.find((option) => option.value === value)) {
allowClear
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);
setLocalSelectedTags((tags) => [
@ -89,8 +102,6 @@ function TagsKey(props: TagsKeysProps): JSX.Element {
},
...tags.slice(index + 1, tags.length),
]);
} else {
setSelectedKey('');
}
}}
>

View File

@ -1,13 +1,13 @@
import { Select } from 'antd';
import getTagValue from 'api/trace/getTagValue';
import React from 'react';
import React, { useState } from 'react';
import { useQuery } from 'react-query';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import { GlobalReducer } from 'types/reducer/globalTime';
import { TraceReducer } from 'types/reducer/trace';
import { SelectComponent } from './styles';
import { AutoCompleteComponent } from './styles';
function TagValue(props: TagValueProps): JSX.Element {
const { tag, setLocalSelectedTags, index, tagKey } = props;
@ -16,6 +16,7 @@ function TagValue(props: TagValueProps): JSX.Element {
Operator: selectedOperator,
Values: selectedValues,
} = tag;
const [localValue, setLocalValue] = useState<string>(selectedValues[0]);
const globalReducer = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
@ -34,22 +35,38 @@ function TagValue(props: TagValueProps): JSX.Element {
);
return (
<SelectComponent
value={selectedValues[0]}
<AutoCompleteComponent
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 => {
if (typeof value === 'string') {
setLocalValue(value);
setLocalSelectedTags((tags) => [
...tags.slice(0, index),
{
Key: selectedKey,
Operator: selectedOperator,
Values: [...selectedValues, value],
Values: [value],
},
...tags.slice(index + 1, tags.length),
]);
}
}}
loading={isLoading || false}
>
{data &&
data.payload &&
@ -58,7 +75,7 @@ function TagValue(props: TagValueProps): JSX.Element {
{suggestion.tagValues}
</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';
export const SpaceComponent = styled(Space)`
@ -9,18 +9,23 @@ export const SpaceComponent = styled(Space)`
export const SelectComponent = styled(Select)`
&&& {
min-width: 170px;
margin-right: 21.91px;
margin-left: 21.92px;
width: 100%;
}
`;
export const Container = styled.div`
export const Container = styled(Space)`
&&& {
display: flex;
margin-top: 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`
@ -31,3 +36,9 @@ export const IconContainer = styled.div`
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 AppActions from 'types/actions';

View File

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

View File

@ -31,6 +31,8 @@ export const UPDATE_SPANS_AGGREGATE_PAGE_NUMBER =
export const UPDATE_SPANS_AGGREGATE_PAGE_SIZE =
'UPDATE_SPANS_AGGREGATE_PAGE_SIZE';
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 {
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 =
| UpdateFilter
| GetTraceFilter
@ -208,4 +215,5 @@ export type TraceActions =
| UpdateSpanOrder
| UpdateSpansAggregatePageNumber
| UpdateSpanSize
| UpdateSpanParams;
| UpdateSpanParams
| UpdateTraceFilterDisplayValue;

View File

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