Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions web/locales/en/plugin__distributed-tracing-console-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
"Last 12 hours": "Last 12 hours",
"Last 1 day": "Last 1 day",
"Last 7 days": "Last 7 days",
"Time Range": "Time Range",
"Select a Time Range": "Select a Time Range",
"Time range": "Time range",
"Select a time range": "Select a time range",
"No Tempo instance selected": "No Tempo instance selected",
"To explore data, select a Tempo instance and run a query.": "To explore data, select a Tempo instance and run a query.",
"Tempo Instance": "Tempo Instance",
"Tempo instance": "Tempo instance",
"Select a Tempo instance": "Select a Tempo instance",
"TempoStack and TempoMonolithic instances with multi-tenancy are supported. Instances without multi-tenancy are not supported.": "TempoStack and TempoMonolithic instances with multi-tenancy are supported. Instances without multi-tenancy are not supported.",
"Tenant": "Tenant",
Expand All @@ -20,14 +20,26 @@
"Tracing": "Tracing",
"Traces": "Traces",
"Trace details": "Trace details",
"Query": "Query",
"Max Traces": "Max Traces",
"The number of search results must be between 1 and {{max}}": "The number of search results must be between 1 and {{max}}",
"Run query": "Run query",
"TraceQL Query": "TraceQL Query",
"Limit traces": "Limit traces",
"No datapoints found.": "No datapoints found.",
"Hide graph": "Hide graph",
"Show graph": "Show graph",
"Filter": "Filter",
"between {{min}} and {{max}}": "between {{min}} and {{max}}",
"greater than {{min}}": "greater than {{min}}",
"less than {{max}}": "less than {{max}}",
"Min duration": "Min duration",
"e.g. 100ms": "e.g. 100ms",
"Max duration": "Max duration",
"Invalid format. Accepted format e.g. 100ms, accepted units: ns, ms, s, m, h": "Invalid format. Accepted format e.g. 100ms, accepted units: ns, ms, s, m, h",
"Custom attributes": "Custom attributes",
"Filter by attributes": "Filter by attributes",
"Attributes are written in the form key=value and are combined via AND. Multiple attributes can be separated via space. String values must be quoted. Example:": "Attributes are written in the form key=value and are combined via AND. Multiple attributes can be separated via space. String values must be quoted. Example:",
"Show query": "Show query",
"Hide query": "Hide query",
"TraceQL Query": "TraceQL Query",
"Query": "Query",
"Run query": "Run query",
"Tempo operator isn't installed yet": "Tempo operator isn't installed yet",
"To get started, install the Tempo operator and create a TempoStack or TempoMonolithic instance with multi-tenancy enabled.": "To get started, install the Tempo operator and create a TempoStack or TempoMonolithic instance with multi-tenancy enabled.",
"Install Tempo operator": "Install Tempo operator",
Expand Down
96 changes: 21 additions & 75 deletions web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"dependencies": {
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.0",
"@grafana/lezer-traceql": "^0.0.22",
"@hookform/resolvers": "^3.3.4",
"@mui/material": "^5.15.14",
"@patternfly/react-charts": "6.92.0",
Expand All @@ -87,13 +88,13 @@
"@perses-dev/dashboards": "0.49.0-rc.1",
"@perses-dev/panels-plugin": "0.49.0-rc.1",
"@perses-dev/plugin-system": "0.49.0-rc.1",
"@perses-dev/prometheus-plugin": "0.49.0-rc.1",
"@perses-dev/tempo-plugin": "0.49.0-rc.1",
"@tanstack/react-query": "^4.7.1",
"copy-webpack-plugin": "^12.0.2",
"i18next": "^23.10.0",
"i18next-http-backend": "^2.5.0",
"i18next-parser": "^8.13.0",
"lodash": "^4.17.21",
"pluralize": "^8.0.0",
"use-query-params": "^2.2.1"
}
Expand Down
88 changes: 88 additions & 0 deletions web/src/components/DebouncedTextInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import {
FormHelperText,
HelperText,
HelperTextItem,
TextInput,
TextInputProps,
} from '@patternfly/react-core';
import { debounce } from 'lodash';
import { ExclamationCircleIcon } from '@patternfly/react-icons';

export interface DebouncedTextInputProps extends TextInputProps {
waitTime: number;
validationRegex?: RegExp;
validationFailedMessage?: string;
value: string;
setValue: (value: string) => void;
}

/**
* <DebouncedTextInput> is a <TextInput> which validates the input (format e.g. 1s) and
* debounces the onChange events until the user stopped typing for waitTime milliseconds, or the cursor left the input field.
* Without debouncing, every keypress would trigger an API request and re-render.
*/
export function DebouncedTextInput({
waitTime,
validationRegex,
validationFailedMessage,
value,
setValue,
...otherProps
}: DebouncedTextInputProps) {
const [draftValue, setDraftValue] = useState(value);

const isValidInput = useCallback(
(x: string) => {
return x == '' || validationRegex === undefined || validationRegex.test(x);
},
[validationRegex],
);

const debouncedSetValue = useMemo(() => {
return debounce((newValue: string) => {
if (isValidInput(newValue)) {
setValue(newValue);
}
}, waitTime);
}, [setValue, isValidInput, waitTime]);

const handleChange = useCallback(
(_event: React.FormEvent<HTMLInputElement>, newValue: string) => {
setDraftValue(newValue);
debouncedSetValue(newValue);
},
[debouncedSetValue],
);

const handleBlur = useCallback(() => {
debouncedSetValue.flush();
}, [debouncedSetValue]);

useEffect(() => {
setDraftValue(value);
}, [value, setDraftValue]);

const validInput = isValidInput(draftValue);
return (
<>
<TextInput
{...otherProps}
type="text"
validated={validInput ? 'default' : 'error'}
value={draftValue}
onChange={handleChange}
onBlur={handleBlur}
/>
{!validInput && (
<FormHelperText>
<HelperText>
<HelperTextItem icon={<ExclamationCircleIcon />} variant="error">
{validationFailedMessage}
</HelperTextItem>
</HelperText>
</FormHelperText>
)}
</>
);
}
27 changes: 14 additions & 13 deletions web/src/components/DurationDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';
import { Stack } from '@patternfly/react-core';
import { Form, FormGroup } from '@patternfly/react-core';
import { useTranslation } from 'react-i18next';
import { DurationString } from '@perses-dev/prometheus-plugin';
import { DurationString } from '@perses-dev/core';
import { ControlledSimpleSelect } from './ControlledSelects';

type DurationDropDownProps = {
Expand Down Expand Up @@ -56,16 +56,17 @@ export const DurationDropdown = ({ duration, setDuration }: DurationDropDownProp
];

return (
<Stack>
<label htmlFor="duration-dropdown">{t('Time Range')}</label>
<ControlledSimpleSelect
id="duration-dropdown"
toggleWidth="12em"
placeholder={t('Select a Time Range')}
options={timeRangeSelectOptions}
value={duration}
setValue={(value) => setDuration(value as DurationString)}
/>
</Stack>
<Form>
<FormGroup fieldId="duration-dropdown" label={t('Time range')}>
<ControlledSimpleSelect
id="duration-dropdown"
toggleWidth="12em"
placeholder={t('Select a time range')}
options={timeRangeSelectOptions}
value={duration}
setValue={(value) => setDuration(value as DurationString)}
/>
</FormGroup>
</Form>
);
};
Loading