vmui: allow displaying the full error message on click (#4760)

https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4719
This commit is contained in:
Yury Molodov 2023-08-10 11:34:25 +02:00 committed by Aliaksandr Valialkin
parent 336744a93e
commit cf0077b552
No known key found for this signature in database
GPG key ID: A72BEC6CD3D0DED1
7 changed files with 86 additions and 26 deletions

View file

@ -36,7 +36,7 @@ const DateTimeInput: FC<DateTimeInputProps> = ({
const [maskedValue, setMaskedValue] = useState(formatStringDate(value)); const [maskedValue, setMaskedValue] = useState(formatStringDate(value));
const [focusToTime, setFocusToTime] = useState(false); const [focusToTime, setFocusToTime] = useState(false);
const [awaitChangeForEnter, setAwaitChangeForEnter] = useState(false); const [awaitChangeForEnter, setAwaitChangeForEnter] = useState(false);
const error = dayjs(maskedValue).isValid() ? "" : "Expected format: YYYY-MM-DD HH:mm:ss"; const error = dayjs(maskedValue).isValid() ? "" : "Invalid date format";
const handleMaskedChange = (e: ChangeEvent<HTMLInputElement>) => { const handleMaskedChange = (e: ChangeEvent<HTMLInputElement>) => {
setMaskedValue(e.currentTarget.value); setMaskedValue(e.currentTarget.value);

View file

@ -3,6 +3,7 @@ import classNames from "classnames";
import { useMemo } from "preact/compat"; import { useMemo } from "preact/compat";
import { useAppState } from "../../../state/common/StateContext"; import { useAppState } from "../../../state/common/StateContext";
import useDeviceDetect from "../../../hooks/useDeviceDetect"; import useDeviceDetect from "../../../hooks/useDeviceDetect";
import TextFieldError from "./TextFieldError";
import "./style.scss"; import "./style.scss";
interface TextFieldProps { interface TextFieldProps {
@ -132,12 +133,7 @@ const TextField: FC<TextFieldProps> = ({
) )
} }
{label && <span className="vm-text-field__label">{label}</span>} {label && <span className="vm-text-field__label">{label}</span>}
<span <TextFieldError error={error}/>
className="vm-text-field__error"
data-show={!!error}
>
{error}
</span>
{helperText && !error && ( {helperText && !error && (
<span className="vm-text-field__helper-text"> <span className="vm-text-field__helper-text">
{helperText} {helperText}

View file

@ -0,0 +1,56 @@
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;

View file

@ -40,14 +40,9 @@
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 2; /* number of lines to show */
line-clamp: 2;
-webkit-box-orient: vertical;
@media (max-width: 500px) {
-webkit-line-clamp: 1; /* number of lines to show */ -webkit-line-clamp: 1; /* number of lines to show */
line-clamp: 1; line-clamp: 1;
} -webkit-box-orient: vertical;
} }
&__label { &__label {
@ -56,10 +51,22 @@
} }
&__error { &__error {
top: calc((100% - ($font-size-small/2)) - 2px); position: relative;
top: calc($font-size-small/-2);
width: fit-content;
overflow-wrap: anywhere;
color: $color-error; color: $color-error;
pointer-events: auto; pointer-events: auto;
user-select: text; user-select: text;
&_full {
display: block;
overflow: visible;
}
&_overflowed {
cursor: pointer;
}
} }
&__helper-text { &__helper-text {
@ -117,9 +124,9 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
max-width: 15px; max-width: 15px;
top: auto; top: 0;
left: $padding-small; left: $padding-small;
height: 100%; height: 40px;
position: absolute; position: absolute;
color: $color-text-secondary; color: $color-text-secondary;
} }

View file

@ -41,7 +41,7 @@ const Tooltip: FC<TooltipProps> = ({
return () => { return () => {
window.removeEventListener("scroll", onScrollWindow); window.removeEventListener("scroll", onScrollWindow);
}; };
}, [isOpen]); }, [isOpen, title]);
const popperStyle = useMemo(() => { const popperStyle = useMemo(() => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment

View file

@ -52,6 +52,7 @@ input[type=number]::-webkit-outer-spin-button {
left: $padding-global; left: $padding-global;
bottom: $padding-global; bottom: $padding-global;
z-index: 999; z-index: 999;
animation: vm-slide-snackbar 150ms cubic-bezier(0.280, 0.840, 0.420, 1.1);
&-content { &-content {
display: grid; display: grid;
@ -71,7 +72,7 @@ input[type=number]::-webkit-outer-spin-button {
bottom: 0; bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
animation: vm-slide-snackbar 150ms cubic-bezier(0.280, 0.840, 0.420, 1.1); }
@keyframes vm-slide-snackbar { @keyframes vm-slide-snackbar {
0% { 0% {
@ -81,7 +82,6 @@ input[type=number]::-webkit-outer-spin-button {
transform: translateY(0); transform: translateY(0);
} }
} }
}
} }
svg { svg {

View file

@ -34,6 +34,7 @@ The following `tip` changes can be tested by building VictoriaMetrics components
* FEATURE: [vmctl](https://docs.victoriametrics.com/vmctl.html): add support of `week` step for [time-based chunking migration](https://docs.victoriametrics.com/vmctl.html#using-time-based-chunking-of-migration). See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4738). * FEATURE: [vmctl](https://docs.victoriametrics.com/vmctl.html): add support of `week` step for [time-based chunking migration](https://docs.victoriametrics.com/vmctl.html#using-time-based-chunking-of-migration). See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4738).
* FEATURE: [vmctl](https://docs.victoriametrics.com/vmctl.html): do not add `/api/v1/read` suffix to remote read storage address defined by `--remote-read-src-addr` if a `--remote-read-disable-path-append` command-line flag is set. It allows an overriding path for remote-read API via `--remote-read-src-addr`. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4655). * FEATURE: [vmctl](https://docs.victoriametrics.com/vmctl.html): do not add `/api/v1/read` suffix to remote read storage address defined by `--remote-read-src-addr` if a `--remote-read-disable-path-append` command-line flag is set. It allows an overriding path for remote-read API via `--remote-read-src-addr`. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4655).
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add warning in query field of vmui for partial data responses. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4721). * FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add warning in query field of vmui for partial data responses. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4721).
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): allow displaying the full error message on click for trimmed error messages in vmui. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4719).
* FEATURE: [Official Grafana dashboards for VictoriaMetrics](https://grafana.com/orgs/victoriametrics): add `Concurrent inserts` panel to vmagent's dasbhoard. The new panel supposed to show whether the number of concurrent inserts processed by vmagent isn't reaching the limit. * FEATURE: [Official Grafana dashboards for VictoriaMetrics](https://grafana.com/orgs/victoriametrics): add `Concurrent inserts` panel to vmagent's dasbhoard. The new panel supposed to show whether the number of concurrent inserts processed by vmagent isn't reaching the limit.
* FEATURE: [Official Grafana dashboards for VictoriaMetrics](https://grafana.com/orgs/victoriametrics): add panels for absolute Mem and CPU usage by vmalert. See related issue [here](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4627). * FEATURE: [Official Grafana dashboards for VictoriaMetrics](https://grafana.com/orgs/victoriametrics): add panels for absolute Mem and CPU usage by vmalert. See related issue [here](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4627).
* FEATURE: [Official Grafana dashboards for VictoriaMetrics](https://grafana.com/orgs/victoriametrics): correctly calculate `Bytes per point` value for single-server and cluster VM dashboards. Before, the calculation mistakenly accounted for the number of entries in indexdb in denominator, which could have shown lower values than expected. * FEATURE: [Official Grafana dashboards for VictoriaMetrics](https://grafana.com/orgs/victoriametrics): correctly calculate `Bytes per point` value for single-server and cluster VM dashboards. Before, the calculation mistakenly accounted for the number of entries in indexdb in denominator, which could have shown lower values than expected.