Skip to content

Commit

Permalink
fixed (#470)
Browse files Browse the repository at this point in the history
  • Loading branch information
EduardZaydler authored Dec 13, 2023
1 parent 5f9a52d commit 2fc5e20
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 132 deletions.
2 changes: 1 addition & 1 deletion src/Components/TriggerEditForm/TriggerEditForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import { defaultNumberEditFormat, defaultNumberViewFormat } from "../../helpers/Formats";
import FormattedNumberInput from "../FormattedNumberInput/FormattedNumberInput";
import ScheduleEdit from "../ScheduleEdit/ScheduleEdit";
import TriggerModeEditor, { ValueType } from "../TriggerModeEditor/TriggerModeEditor";
import { ValueType, TriggerModeEditor } from "../TriggerModeEditor/TriggerModeEditor";
import StatusSelect from "../StatusSelect/StatusSelect";
import TagDropdownSelect from "../TagDropdownSelect/TagDropdownSelect";
import { Status, StatusesList } from "../../Domain/Status";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from "react";
import { Link } from "@skbkontur/react-ui/components/Link";
import CodeRef from "../../../CodeRef/CodeRef";
import classNames from "classnames/bind";

import styles from "../../TriggerModeEditor.less";

const cn = classNames.bind(styles);

export const TooltipExpressionHelp = (): React.ReactNode => (
<div className={cn("expression-help")}>
<div className={cn("main-description")}>
Expression uses{" "}
<Link target="_blank" href="https://github.com/Knetic/govaluate/blob/master/MANUAL.md">
govaluate
</Link>{" "}
with predefined constants:
</div>
<div>
<CodeRef>t1</CodeRef>, <CodeRef>t2</CodeRef>, ... are values from your targets.
</div>
<div>
<CodeRef>OK</CodeRef>, <CodeRef>WARN</CodeRef>, <CodeRef>ERROR</CodeRef>,{" "}
<CodeRef>NODATA</CodeRef> are states that must be the result of evaluation.
</div>
<div>
<CodeRef>PREV_STATE</CodeRef> is equal to previously set state, and allows you to
prevent frequent state changes.
</div>

<div className={cn("note")}>
NOTE: Only T1 target can resolve into multiple metrics in Advanced Mode. T2, T3, ...
must resolve to single metrics.
</div>
</div>
);
205 changes: 75 additions & 130 deletions src/Components/TriggerModeEditor/TriggerModeEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import * as React from "react";
import React, { FC, useState } from "react";
import { ValidationWrapperV1, tooltip, ValidationInfo } from "@skbkontur/react-ui-validations";
import { Tabs } from "@skbkontur/react-ui/components/Tabs";
import { Input } from "@skbkontur/react-ui/components/Input";
import { Link } from "@skbkontur/react-ui/components/Link";
import { Trigger, TriggerType } from "../../Domain/Trigger";
import TriggerSimpleModeEditor from "../TriggerSimpleModeEditor/TriggerSimpleModeEditor";
import { RowStack, Fit, Fill } from "../ItemsStack/ItemsStack";
import CodeRef from "../CodeRef/CodeRef";
import HelpTooltip from "../HelpTooltip/HelpTooltip";
import { TooltipExpressionHelp } from "./Components/TooltipExpressionHelp/TooltipExpressionHelp";
import classNames from "classnames/bind";

import styles from "./TriggerModeEditor.less";
Expand All @@ -21,7 +20,7 @@ export type ValueType = {
error_value: number | null;
};

type Props = {
type IProps = {
disableSimpleMode?: boolean;
triggerType: TriggerType;
value: ValueType;
Expand All @@ -30,146 +29,92 @@ type Props = {
onChange: (update: Partial<Trigger>) => void;
};

type State = {
mode: string;
watchFor: WatchForType;
risingValues: ValueType;
fallingValues: ValueType;
};

export default class TriggerModeEditor extends React.Component<Props, State> {
static getDerivedStateFromProps(props: Props): State {
const modeType = TriggerModeEditor.getModeType(props.triggerType);
const watchForType = TriggerModeEditor.getWatchForType(props.triggerType);

return {
mode: modeType,
watchFor: watchForType,
risingValues:
watchForType === "rising" ? props.value : { warn_value: null, error_value: null },
fallingValues:
watchForType === "falling" ? props.value : { warn_value: null, error_value: null },
};
}

static getWatchForType(type: string): WatchForType {
export const TriggerModeEditor: FC<IProps> = ({
disableSimpleMode,
triggerType,
value,
expression,
validateExpression,
onChange,
}) => {
const getWatchForType = (type: string): WatchForType => {
return type === "falling" ? type : "rising";
}
};

static getModeType(type: string): string {
const getModeType = (type: string): string => {
return type === "expression" ? "advanced" : "simple";
}

render(): React.ReactElement {
const { mode, watchFor, risingValues, fallingValues } = this.state;
const { expression, disableSimpleMode, validateExpression, onChange } = this.props;
};

return (
<>
<div className={cn("tabs")}>
<Tabs value={mode} onValueChange={this.handleTabChange}>
<Tabs.Tab id="simple" style={{ color: disableSimpleMode ? "#888888" : "" }}>
Simple mode
</Tabs.Tab>
<Tabs.Tab id="advanced">Advanced mode</Tabs.Tab>
</Tabs>
</div>
{mode === "simple" && (
<TriggerSimpleModeEditor
watchFor={watchFor}
risingValues={risingValues}
fallingValues={fallingValues}
onChange={this.handleInputChange}
onSwitch={this.handleRadioChange}
/>
)}
{mode === "advanced" && (
<div className={cn("advanced")}>
<RowStack verticalAlign="baseline" block>
<Fill>
<ValidationWrapperV1
validationInfo={validateExpression(
expression,
"Expression can't be empty"
)}
renderMessage={tooltip("right middle")}
>
<Input
width="100%"
value={expression}
onValueChange={(value) => onChange({ expression: value })}
placeholder="t1 >= 10 ? ERROR : (t1 >= 1 ? WARN : OK)"
/>
</ValidationWrapperV1>
</Fill>
<Fit>
&nbsp;
<HelpTooltip>{this.tooltipExpressionHelp()}</HelpTooltip>
</Fit>
</RowStack>
</div>
)}
</>
);
}
const [mode, setMode] = useState<string>(getModeType(triggerType));
const [watchForField, setWatchForField] = useState<WatchForType>(getWatchForType(triggerType));
const [values, setValues] = useState<ValueType>(value);

handleTabChange = (value: string): void => {
const { watchFor } = this.state;
const { disableSimpleMode, onChange } = this.props;
const handleTabChange = (value: string): void => {
if (!disableSimpleMode) {
const triggerType = value === "advanced" ? "expression" : watchFor;
const triggerType = value === "advanced" ? "expression" : watchForField;
onChange({ trigger_type: triggerType });
setMode(value);
}
};

handleRadioChange = (type: WatchForType): void => {
const { risingValues, fallingValues } = this.state;
const { onChange } = this.props;
const value = type === "falling" ? { ...fallingValues } : { ...risingValues };
this.setState({ watchFor: type });
onChange({ trigger_type: type, ...value });
const handleRadioChange = (type: WatchForType): void => {
setWatchForField(type);
onChange({ trigger_type: type, ...values });
};

handleInputChange = (value: number | null, valueType: string): void => {
const { watchFor, risingValues, fallingValues } = this.state;
const { onChange } = this.props;
if (watchFor === "rising") {
this.setState({ risingValues: { ...risingValues, [valueType]: value } });
}
if (watchFor === "falling") {
this.setState({ fallingValues: { ...fallingValues, [valueType]: value } });
}
const handleInputChange = (value: number | null, valueType: string): void => {
setValues((prev) => ({ ...prev, [valueType]: value }));
onChange({ [valueType]: value });
};

tooltipExpressionHelp = (): React.ReactNode => (
<div className={cn("expression-help")}>
<div className={cn("main-description")}>
Expression uses{" "}
<Link
target="_blank"
href="https://github.com/Knetic/govaluate/blob/master/MANUAL.md"
>
govaluate
</Link>{" "}
with predefined constants:
</div>
<div>
<CodeRef>t1</CodeRef>, <CodeRef>t2</CodeRef>, ... are values from your targets.
</div>
<div>
<CodeRef>OK</CodeRef>, <CodeRef>WARN</CodeRef>, <CodeRef>ERROR</CodeRef>,{" "}
<CodeRef>NODATA</CodeRef> are states that must be the result of evaluation.
</div>
<div>
<CodeRef>PREV_STATE</CodeRef> is equal to previously set state, and allows you to
prevent frequent state changes.
</div>

<div className={cn("note")}>
NOTE: Only T1 target can resolve into multiple metrics in Advanced Mode. T2, T3, ...
must resolve to single metrics.
const configureValues = (watchForType: WatchForType) =>
watchForField === `${watchForType}` ? values : { warn_value: null, error_value: null };

return (
<>
<div className={cn("tabs")}>
<Tabs value={mode} onValueChange={handleTabChange}>
<Tabs.Tab id="simple" style={{ color: disableSimpleMode ? "#888888" : "" }}>
Simple mode
</Tabs.Tab>
<Tabs.Tab id="advanced">Advanced mode</Tabs.Tab>
</Tabs>
</div>
</div>
{mode === "simple" && (
<TriggerSimpleModeEditor
watchFor={watchForField}
risingValues={configureValues("rising")}
fallingValues={configureValues("falling")}
onChange={handleInputChange}
onSwitch={handleRadioChange}
/>
)}
{mode === "advanced" && (
<div className={cn("advanced")}>
<RowStack verticalAlign="baseline" block>
<Fill>
<ValidationWrapperV1
validationInfo={validateExpression(
expression,
"Expression can't be empty"
)}
renderMessage={tooltip("right middle")}
>
<Input
width="100%"
value={expression}
onValueChange={(value) => onChange({ expression: value })}
placeholder="t1 >= 10 ? ERROR : (t1 >= 1 ? WARN : OK)"
/>
</ValidationWrapperV1>
</Fill>
<Fit>
&nbsp;
<HelpTooltip>{TooltipExpressionHelp()}</HelpTooltip>
</Fit>
</RowStack>
</div>
)}
</>
);
}
};
2 changes: 1 addition & 1 deletion src/Stories/TriggerModeEditor.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from "react";
import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import { ValidationContainer } from "@skbkontur/react-ui-validations";
import TriggerModeEditor from "../Components/TriggerModeEditor/TriggerModeEditor";
import { TriggerModeEditor } from "../Components/TriggerModeEditor/TriggerModeEditor";

storiesOf("TriggerModeEditor", module)
.addDecorator((story) => (
Expand Down

0 comments on commit 2fc5e20

Please sign in to comment.