mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-01-10 15:14:09 +00:00
https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4848
This commit is contained in:
parent
8287749c05
commit
ca44b8da1f
7 changed files with 112 additions and 112 deletions
|
@ -5,8 +5,6 @@ import TextField from "../../Main/TextField/TextField";
|
|||
import Autocomplete from "../../Main/Autocomplete/Autocomplete";
|
||||
import "./style.scss";
|
||||
import { QueryStats } from "../../../api/types";
|
||||
import Tooltip from "../../Main/Tooltip/Tooltip";
|
||||
import { WarningIcon } from "../../Main/Icons";
|
||||
import { partialWarning, seriesFetchedWarning } from "./warningText";
|
||||
|
||||
export interface QueryEditorProps {
|
||||
|
@ -41,7 +39,7 @@ const QueryEditor: FC<QueryEditorProps> = ({
|
|||
const [openAutocomplete, setOpenAutocomplete] = useState(false);
|
||||
const autocompleteAnchorEl = useRef<HTMLDivElement>(null);
|
||||
|
||||
const warnings = [
|
||||
const warning = [
|
||||
{
|
||||
show: stats?.seriesFetched === "0" && !stats.resultLength,
|
||||
text: seriesFetchedWarning
|
||||
|
@ -50,7 +48,7 @@ const QueryEditor: FC<QueryEditorProps> = ({
|
|||
show: stats?.isPartial,
|
||||
text: partialWarning
|
||||
}
|
||||
].filter((warning) => warning.show);
|
||||
].filter((w) => w.show).map(w => w.text).join("");
|
||||
|
||||
const handleSelect = (val: string) => {
|
||||
onChange(val);
|
||||
|
@ -104,6 +102,7 @@ const QueryEditor: FC<QueryEditorProps> = ({
|
|||
type={"textarea"}
|
||||
autofocus={!!value}
|
||||
error={error}
|
||||
warning={warning}
|
||||
onKeyDown={handleKeyDown}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
|
@ -119,20 +118,6 @@ const QueryEditor: FC<QueryEditorProps> = ({
|
|||
onFoundOptions={handleChangeFoundOptions}
|
||||
/>
|
||||
)}
|
||||
{!!warnings.length && (
|
||||
<div className="vm-query-editor-warning">
|
||||
<Tooltip
|
||||
placement="bottom-right"
|
||||
title={(
|
||||
<div className="vm-query-editor-warning__tooltip">
|
||||
{warnings.map((warning, index) => <p key={index}>{warning.text}</p>)}
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<WarningIcon/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</div>;
|
||||
};
|
||||
|
||||
|
|
|
@ -7,29 +7,4 @@
|
|||
max-height: 300px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
&-warning {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: $padding-global;
|
||||
transform: translateY(-50%);
|
||||
display: grid;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: $color-warning;
|
||||
|
||||
&__tooltip {
|
||||
white-space: pre-line;
|
||||
|
||||
p {
|
||||
margin-bottom: $padding-small;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import classNames from "classnames";
|
|||
import { useMemo } from "preact/compat";
|
||||
import { useAppState } from "../../../state/common/StateContext";
|
||||
import useDeviceDetect from "../../../hooks/useDeviceDetect";
|
||||
import TextFieldError from "./TextFieldError";
|
||||
import TextFieldMessage from "./TextFieldMessage";
|
||||
import "./style.scss";
|
||||
|
||||
interface TextFieldProps {
|
||||
|
@ -11,6 +11,7 @@ interface TextFieldProps {
|
|||
value?: string | number
|
||||
type?: HTMLInputTypeAttribute | "textarea"
|
||||
error?: string
|
||||
warning?: string
|
||||
placeholder?: string
|
||||
endIcon?: ReactNode
|
||||
startIcon?: ReactNode
|
||||
|
@ -30,12 +31,13 @@ const TextField: FC<TextFieldProps> = ({
|
|||
value,
|
||||
type = "text",
|
||||
error = "",
|
||||
warning = "",
|
||||
helperText = "",
|
||||
placeholder,
|
||||
endIcon,
|
||||
startIcon,
|
||||
disabled = false,
|
||||
autofocus = false,
|
||||
helperText,
|
||||
inputmode = "text",
|
||||
onChange,
|
||||
onEnter,
|
||||
|
@ -53,6 +55,7 @@ const TextField: FC<TextFieldProps> = ({
|
|||
const inputClasses = classNames({
|
||||
"vm-text-field__input": true,
|
||||
"vm-text-field__input_error": error,
|
||||
"vm-text-field__input_warning": !error && warning,
|
||||
"vm-text-field__input_icon-start": startIcon,
|
||||
"vm-text-field__input_disabled": disabled,
|
||||
"vm-text-field__input_textarea": type === "textarea",
|
||||
|
@ -133,12 +136,11 @@ const TextField: FC<TextFieldProps> = ({
|
|||
)
|
||||
}
|
||||
{label && <span className="vm-text-field__label">{label}</span>}
|
||||
<TextFieldError error={error}/>
|
||||
{helperText && !error && (
|
||||
<span className="vm-text-field__helper-text">
|
||||
{helperText}
|
||||
</span>
|
||||
)}
|
||||
<TextFieldMessage
|
||||
error={error}
|
||||
warning={warning}
|
||||
info={helperText}
|
||||
/>
|
||||
</label>
|
||||
;
|
||||
};
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
import React, { FC, useEffect, useRef, useState } from "react";
|
||||
import useEventListener from "../../../hooks/useEventListener";
|
||||
import classNames from "classnames";
|
||||
import "./style.scss";
|
||||
|
||||
interface TextFieldErrorProps {
|
||||
error: string;
|
||||
}
|
||||
|
||||
const TextFieldError: FC<TextFieldErrorProps> = ({ error }) => {
|
||||
const errorRef = useRef<HTMLSpanElement>(null);
|
||||
const [isErrorTruncated, setIsErrorTruncated] = useState(false);
|
||||
const [showFull, setShowFull] = useState(false);
|
||||
|
||||
const checkIfTextTruncated = () => {
|
||||
const el = errorRef.current;
|
||||
if (el) {
|
||||
const { offsetWidth, scrollWidth, offsetHeight, scrollHeight } = el;
|
||||
// The "+1" is for the scrollbar in Firefox
|
||||
const overflowed = (offsetWidth + 1) < scrollWidth || (offsetHeight + 1) < scrollHeight;
|
||||
setIsErrorTruncated(overflowed);
|
||||
} else {
|
||||
setIsErrorTruncated(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleClickError = () => {
|
||||
if (!isErrorTruncated) return;
|
||||
setShowFull(true);
|
||||
setIsErrorTruncated(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setShowFull(false);
|
||||
checkIfTextTruncated();
|
||||
}, [errorRef, error]);
|
||||
|
||||
useEventListener("resize", checkIfTextTruncated);
|
||||
|
||||
return (
|
||||
<span
|
||||
className={classNames({
|
||||
"vm-text-field__error": true,
|
||||
"vm-text-field__error_overflowed": isErrorTruncated,
|
||||
"vm-text-field__error_full": showFull,
|
||||
})}
|
||||
data-show={!!error}
|
||||
ref={errorRef}
|
||||
onClick={handleClickError}
|
||||
>
|
||||
{error}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default TextFieldError;
|
|
@ -0,0 +1,72 @@
|
|||
import React, { FC, useEffect, useRef, useState } from "react";
|
||||
import useEventListener from "../../../hooks/useEventListener";
|
||||
import classNames from "classnames";
|
||||
import "./style.scss";
|
||||
import { useMemo } from "preact/compat";
|
||||
|
||||
interface TextFieldErrorProps {
|
||||
error: string;
|
||||
warning: string;
|
||||
info: string;
|
||||
}
|
||||
|
||||
const TextFieldMessage: FC<TextFieldErrorProps> = ({ error, warning, info }) => {
|
||||
console.log(warning);
|
||||
const messageRef = useRef<HTMLSpanElement>(null);
|
||||
const [isMessageTruncated, setIsMessageTruncated] = useState(false);
|
||||
const [showFull, setShowFull] = useState(false);
|
||||
|
||||
const prefix = useMemo(() => {
|
||||
if (error) return "ERROR: ";
|
||||
if (warning) return "WARNING: ";
|
||||
return "";
|
||||
}, [error, warning]);
|
||||
|
||||
const message = `${prefix}${error || warning || info}`;
|
||||
|
||||
const checkIfTextTruncated = () => {
|
||||
const el = messageRef.current;
|
||||
if (el) {
|
||||
const { offsetWidth, scrollWidth, offsetHeight, scrollHeight } = el;
|
||||
// The "+1" is for the scrollbar in Firefox
|
||||
const overflowed = (offsetWidth + 1) < scrollWidth || (offsetHeight + 1) < scrollHeight;
|
||||
setIsMessageTruncated(overflowed);
|
||||
} else {
|
||||
setIsMessageTruncated(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleClickError = () => {
|
||||
if (!isMessageTruncated) return;
|
||||
setShowFull(true);
|
||||
setIsMessageTruncated(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setShowFull(false);
|
||||
checkIfTextTruncated();
|
||||
}, [messageRef, message]);
|
||||
|
||||
useEventListener("resize", checkIfTextTruncated);
|
||||
|
||||
if (!error && !warning && !info) return null;
|
||||
|
||||
return (
|
||||
<span
|
||||
className={classNames({
|
||||
"vm-text-field__error": true,
|
||||
"vm-text-field__warning": warning && !error,
|
||||
"vm-text-field__helper-text": !warning && !error,
|
||||
"vm-text-field__error_overflowed": isMessageTruncated,
|
||||
"vm-text-field__error_full": showFull,
|
||||
})}
|
||||
data-show={!!message}
|
||||
ref={messageRef}
|
||||
onClick={handleClickError}
|
||||
>
|
||||
{message}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default TextFieldMessage;
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
&__label,
|
||||
&__error,
|
||||
&__warning,
|
||||
&__helper-text, {
|
||||
position: absolute;
|
||||
left: calc($padding-global/2);
|
||||
|
@ -50,12 +51,13 @@
|
|||
color: $color-text-secondary;
|
||||
}
|
||||
|
||||
&__helper-text,
|
||||
&__warning,
|
||||
&__error {
|
||||
position: relative;
|
||||
top: calc($font-size-small/-2);
|
||||
width: fit-content;
|
||||
overflow-wrap: anywhere;
|
||||
color: $color-error;
|
||||
pointer-events: auto;
|
||||
user-select: text;
|
||||
|
||||
|
@ -69,8 +71,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
&__error {
|
||||
color: $color-error;
|
||||
}
|
||||
|
||||
&__warning {
|
||||
color: $color-warning;
|
||||
}
|
||||
|
||||
&__helper-text {
|
||||
bottom: calc($font-size-small/-2);
|
||||
color: $color-text-secondary;
|
||||
}
|
||||
|
||||
|
@ -93,14 +102,26 @@
|
|||
}
|
||||
|
||||
&_error {
|
||||
border: 1px solid $color-error;
|
||||
border-color: $color-error;
|
||||
|
||||
&:hover {
|
||||
border: 1px solid $color-error;
|
||||
border-color: $color-error;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border: 1px solid $color-error;
|
||||
border-color: $color-error;
|
||||
}
|
||||
}
|
||||
|
||||
&_warning {
|
||||
border-color: $color-warning;
|
||||
|
||||
&:hover {
|
||||
border-color: $color-warning;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: $color-warning;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ The following `tip` changes can be tested by building VictoriaMetrics components
|
|||
|
||||
* FEATURE: [vmbackup](https://docs.victoriametrics.com/vmbackup.html): add support for server-side copy of existing backups. See [these docs](https://docs.victoriametrics.com/vmbackup.html#server-side-copy-of-the-existing-backup) for details.
|
||||
* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): properly handle `unexpected EOF` error when parsing metrics in Prometheus exposition format. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4817).
|
||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): make the warning message more noticeable for text fields. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4848).
|
||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add button for auto-formatting PromQL/MetricsQL queries. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4681). Thanks to @aramattamara for the [pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/4694).
|
||||
|
||||
* BUGFIX: do not allow starting VictoriaMetrics components with improperly set boolean command-line flags in the form `-boolFlagName value`, since this leads to silent incomplete flags' parsing. This form should be replaced with `-boolFlagName=value`. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4845).
|
||||
|
|
Loading…
Reference in a new issue