Compare commits

..

1 Commits

Author SHA1 Message Date
Abhi Kumar
68e4a2c5de chore: broke down rightcontainer component into sub-components 2026-03-13 02:27:22 +05:30
23 changed files with 1074 additions and 704 deletions

View File

@@ -1,9 +1,9 @@
.right-container {
display: flex;
flex-direction: column;
font-family: 'Space Mono';
.section-heading {
font-family: 'Space Mono';
color: var(--bg-vanilla-400);
font-size: 13px;
font-style: normal;
@@ -41,50 +41,6 @@
gap: 8px;
}
.name-description {
padding: 0 0 4px 0;
.name-input {
display: flex;
padding: 6px 6px 6px 8px;
align-items: center;
gap: 4px;
flex: 1 0 0;
align-self: stretch;
border-radius: 2px;
border: 1px solid var(--bg-slate-400);
background: var(--bg-ink-300);
color: var(--bg-vanilla-100);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 18px; /* 128.571% */
letter-spacing: -0.07px;
}
.description-input {
border-style: unset;
.ant-input {
display: flex;
height: 80px;
padding: 6px 6px 6px 8px;
align-items: flex-start;
gap: 4px;
border-radius: 2px;
border: 1px solid var(--bg-slate-400);
background: var(--bg-ink-300);
color: var(--bg-vanilla-100);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 18px; /* 128.571% */
letter-spacing: -0.07px;
}
}
}
.panel-config {
display: flex;
flex-direction: column;
@@ -218,87 +174,6 @@
flex-direction: row;
justify-content: space-between;
}
.bucket-config {
.bucket-size-label {
margin-top: 8px;
}
.bucket-input {
display: flex;
width: 100%;
height: 32px;
padding: 6px 6px 6px 8px;
align-items: center;
gap: 4px;
align-self: stretch;
border-radius: 2px;
border: 1px solid var(--bg-slate-400);
background: var(--bg-ink-300);
.ant-input {
background: var(--bg-ink-300);
}
}
.combine-hist {
display: flex;
justify-content: space-between;
margin-top: 8px;
.label {
color: var(--bg-vanilla-400);
font-size: 13px;
font-style: normal;
font-weight: 400;
line-height: 18px; /* 138.462% */
letter-spacing: 0.52px;
text-transform: uppercase;
}
}
}
}
.alerts {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 12px 32px 12px;
min-height: 44px;
border-top: 1px solid var(--bg-slate-500);
cursor: pointer;
.left-section {
display: flex;
align-items: center;
gap: 8px;
.bell-icon {
color: var(--bg-vanilla-400);
}
.alerts-text {
color: var(--bg-vanilla-400);
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 142.857% */
letter-spacing: 0.14px;
}
}
.plus-icon {
color: var(--bg-vanilla-400);
}
}
.context-links {
padding: 12px 12px 16px 12px;
border-bottom: 1px solid var(--bg-slate-500);
}
.thresholds-section {
padding: 12px 12px 16px 12px;
border-top: 1px solid var(--bg-slate-500);
}
}
@@ -333,25 +208,6 @@
}
}
.name-description {
border-top: 1px solid var(--bg-vanilla-300);
border-bottom: 1px solid var(--bg-vanilla-300);
.name-input {
border: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-300);
color: var(--bg-ink-300);
}
.description-input {
.ant-input {
border: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-300);
color: var(--bg-ink-300);
}
}
}
.panel-config {
.panel-type-select {
.ant-select-selector {
@@ -389,21 +245,6 @@
}
}
.bucket-config {
.label {
color: var(--bg-ink-400);
}
.bucket-input {
border: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-300);
.ant-input {
background: var(--bg-vanilla-300);
}
}
}
.panel-time-text {
color: var(--bg-ink-400);
}
@@ -437,31 +278,6 @@
}
}
}
.alerts {
border-top: 1px solid var(--bg-vanilla-300);
.left-section {
.bell-icon {
color: var(--bg-ink-300);
}
.alerts-text {
color: var(--bg-ink-300);
}
}
.plus-icon {
color: var(--bg-ink-300);
}
}
.context-links {
border-bottom: 1px solid var(--bg-vanilla-300);
}
.thresholds-section {
border-top: 1px solid var(--bg-vanilla-300);
}
}
.select-option {

View File

@@ -0,0 +1,50 @@
.alerts-section {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 12px 32px 12px;
min-height: 44px;
border-top: 1px solid var(--bg-slate-500);
cursor: pointer;
.alerts-section__left {
display: flex;
align-items: center;
gap: 8px;
.alerts-section__bell-icon {
color: var(--bg-vanilla-400);
}
.alerts-section__text {
color: var(--bg-vanilla-400);
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 142.857% */
letter-spacing: 0.14px;
}
}
.alerts-section__plus-icon {
color: var(--bg-vanilla-400);
}
}
.lightMode {
.alerts-section {
border-top: 1px solid var(--bg-vanilla-300);
.alerts-section__left {
.alerts-section__bell-icon {
color: var(--bg-ink-300);
}
.alerts-section__text {
color: var(--bg-ink-300);
}
}
.alerts-section__plus-icon {
color: var(--bg-ink-300);
}
}
}

View File

@@ -0,0 +1,23 @@
import { Typography } from 'antd';
import { ConciergeBell, Plus, SquareArrowOutUpRight } from 'lucide-react';
import './AlertsSection.styles.scss';
interface AlertsSectionProps {
onCreateAlertsHandler: () => void;
}
export function AlertsSection({
onCreateAlertsHandler,
}: AlertsSectionProps): JSX.Element {
return (
<section className="alerts-section" onClick={onCreateAlertsHandler}>
<div className="alerts-section__left">
<ConciergeBell size={14} className="alerts-section__bell-icon" />
<Typography.Text className="alerts-section__text">Alerts</Typography.Text>
<SquareArrowOutUpRight size={10} className="info-icon" />
</div>
<Plus size={14} className="alerts-section__plus-icon" />
</section>
);
}

View File

@@ -0,0 +1,98 @@
import { Dispatch, SetStateAction } from 'react';
import { InputNumber, Select, Typography } from 'antd';
import SettingsSection from 'components/SettingsSection/SettingsSection';
import { Axis3D, LineChart, Spline } from 'lucide-react';
enum LogScale {
LINEAR = 'linear',
LOGARITHMIC = 'logarithmic',
}
const { Option } = Select;
interface AxesSectionProps {
allowSoftMinMax: boolean;
allowLogScale: boolean;
softMin: number | null;
softMax: number | null;
setSoftMin: Dispatch<SetStateAction<number | null>>;
setSoftMax: Dispatch<SetStateAction<number | null>>;
isLogScale: boolean;
setIsLogScale: Dispatch<SetStateAction<boolean>>;
}
export function AxesSection({
allowSoftMinMax,
allowLogScale,
softMin,
softMax,
setSoftMin,
setSoftMax,
isLogScale,
setIsLogScale,
}: AxesSectionProps): JSX.Element {
const softMinHandler = (value: number | null): void => {
setSoftMin(value);
};
const softMaxHandler = (value: number | null): void => {
setSoftMax(value);
};
return (
<SettingsSection title="Axes" icon={<Axis3D size={14} />}>
{allowSoftMinMax && (
<section className="soft-min-max">
<section className="container">
<Typography.Text className="text">Soft Min</Typography.Text>
<InputNumber
type="number"
value={softMin}
onChange={softMinHandler}
rootClassName="input"
/>
</section>
<section className="container">
<Typography.Text className="text">Soft Max</Typography.Text>
<InputNumber
value={softMax}
type="number"
rootClassName="input"
onChange={softMaxHandler}
/>
</section>
</section>
)}
{allowLogScale && (
<section className="log-scale control-container">
<Typography.Text className="section-heading">Y Axis Scale</Typography.Text>
<Select
onChange={(value): void => setIsLogScale(value === LogScale.LOGARITHMIC)}
value={isLogScale ? LogScale.LOGARITHMIC : LogScale.LINEAR}
style={{ width: '100%' }}
className="panel-type-select"
defaultValue={LogScale.LINEAR}
>
<Option value={LogScale.LINEAR}>
<div className="select-option">
<div className="icon">
<LineChart size={16} />
</div>
<Typography.Text className="display">Linear</Typography.Text>
</div>
</Option>
<Option value={LogScale.LOGARITHMIC}>
<div className="select-option">
<div className="icon">
<Spline size={16} />
</div>
<Typography.Text className="display">Logarithmic</Typography.Text>
</div>
</Option>
</Select>
</section>
)}
</SettingsSection>
);
}

View File

@@ -0,0 +1,71 @@
import { Dispatch, SetStateAction } from 'react';
import { Switch, Typography } from 'antd';
import SettingsSection from 'components/SettingsSection/SettingsSection';
import {
FillMode,
LineInterpolation,
LineStyle,
} from 'lib/uPlotV2/config/types';
import { Paintbrush } from 'lucide-react';
import { FillModeSelector } from '../../components/FillModeSelector/FillModeSelector';
import { LineInterpolationSelector } from '../../components/LineInterpolationSelector/LineInterpolationSelector';
import { LineStyleSelector } from '../../components/LineStyleSelector/LineStyleSelector';
interface ChartAppearanceSectionProps {
fillMode: FillMode;
setFillMode: Dispatch<SetStateAction<FillMode>>;
lineStyle: LineStyle;
setLineStyle: Dispatch<SetStateAction<LineStyle>>;
lineInterpolation: LineInterpolation;
setLineInterpolation: Dispatch<SetStateAction<LineInterpolation>>;
showPoints: boolean;
setShowPoints: Dispatch<SetStateAction<boolean>>;
allowFillMode: boolean;
allowLineStyle: boolean;
allowLineInterpolation: boolean;
allowShowPoints: boolean;
}
export function ChartAppearanceSection({
fillMode,
setFillMode,
lineStyle,
setLineStyle,
lineInterpolation,
setLineInterpolation,
showPoints,
setShowPoints,
allowFillMode,
allowLineStyle,
allowLineInterpolation,
allowShowPoints,
}: ChartAppearanceSectionProps): JSX.Element {
return (
<SettingsSection title="Chart Appearance" icon={<Paintbrush size={14} />}>
{allowFillMode && (
<FillModeSelector value={fillMode} onChange={setFillMode} />
)}
{allowLineStyle && (
<LineStyleSelector value={lineStyle} onChange={setLineStyle} />
)}
{allowLineInterpolation && (
<LineInterpolationSelector
value={lineInterpolation}
onChange={setLineInterpolation}
/>
)}
{allowShowPoints && (
<section className="show-points toggle-card">
<div className="toggle-card-text-container">
<Typography.Text className="section-heading">Show points</Typography.Text>
<Typography.Text className="toggle-card-description">
Display individual data points on the chart
</Typography.Text>
</div>
<Switch size="small" checked={showPoints} onChange={setShowPoints} />
</section>
)}
</SettingsSection>
);
}

View File

@@ -0,0 +1,10 @@
.context-links-section {
padding: 12px 12px 16px 12px;
border-bottom: 1px solid var(--bg-slate-500);
}
.lightMode {
.context-links-section {
border-bottom: 1px solid var(--bg-vanilla-300);
}
}

View File

@@ -0,0 +1,36 @@
import { Dispatch, SetStateAction } from 'react';
import SettingsSection from 'components/SettingsSection/SettingsSection';
import { Link as LinkIcon } from 'lucide-react';
import { ContextLinksData, Widgets } from 'types/api/dashboard/getAll';
import ContextLinks from '../../ContextLinks';
import './ContextLinksSection.styles.scss';
interface ContextLinksSectionProps {
contextLinks: ContextLinksData;
setContextLinks: Dispatch<SetStateAction<ContextLinksData>>;
selectedWidget?: Widgets;
}
export function ContextLinksSection({
contextLinks,
setContextLinks,
selectedWidget,
}: ContextLinksSectionProps): JSX.Element {
return (
<SettingsSection
title="Context Links"
icon={<LinkIcon size={14} />}
defaultOpen={!!contextLinks.linksData.length}
>
<div className="context-links-section">
<ContextLinks
contextLinks={contextLinks}
setContextLinks={setContextLinks}
selectedWidget={selectedWidget}
/>
</div>
</SettingsSection>
);
}

View File

@@ -0,0 +1,92 @@
import { Dispatch, SetStateAction, useMemo } from 'react';
import { Select, Typography } from 'antd';
import { PrecisionOption, PrecisionOptionsEnum } from 'components/Graph/types';
import SettingsSection from 'components/SettingsSection/SettingsSection';
import { PanelDisplay } from 'constants/queryBuilder';
import { SlidersHorizontal } from 'lucide-react';
import { ColumnUnit } from 'types/api/dashboard/getAll';
import { ColumnUnitSelector } from '../../ColumnUnitSelector/ColumnUnitSelector';
import DashboardYAxisUnitSelectorWrapper from '../../DashboardYAxisUnitSelectorWrapper';
interface FormattingUnitsSectionProps {
selectedGraphType: PanelDisplay | '';
yAxisUnit: string;
setYAxisUnit: Dispatch<SetStateAction<string>>;
isNewDashboard: boolean;
decimalPrecision: PrecisionOption;
setDecimalPrecision: Dispatch<SetStateAction<PrecisionOption>>;
columnUnits: ColumnUnit;
setColumnUnits: Dispatch<SetStateAction<ColumnUnit>>;
allowYAxisUnit: boolean;
allowDecimalPrecision: boolean;
allowPanelColumnPreference: boolean;
}
export function FormattingUnitsSection({
selectedGraphType,
yAxisUnit,
setYAxisUnit,
isNewDashboard,
decimalPrecision,
setDecimalPrecision,
columnUnits,
setColumnUnits,
allowYAxisUnit,
allowDecimalPrecision,
allowPanelColumnPreference,
}: FormattingUnitsSectionProps): JSX.Element {
const decimapPrecisionOptions = useMemo(
() => [
{ label: '0 decimals', value: PrecisionOptionsEnum.ZERO },
{ label: '1 decimal', value: PrecisionOptionsEnum.ONE },
{ label: '2 decimals', value: PrecisionOptionsEnum.TWO },
{ label: '3 decimals', value: PrecisionOptionsEnum.THREE },
],
[],
);
return (
<SettingsSection
title="Formatting & Units"
icon={<SlidersHorizontal size={14} />}
>
{allowYAxisUnit && (
<DashboardYAxisUnitSelectorWrapper
onSelect={setYAxisUnit}
value={yAxisUnit || ''}
fieldLabel={
selectedGraphType === PanelDisplay.VALUE ||
selectedGraphType === PanelDisplay.PIE
? 'Unit'
: 'Y Axis Unit'
}
shouldUpdateYAxisUnit={isNewDashboard}
/>
)}
{allowDecimalPrecision && (
<section className="decimal-precision-selector control-container">
<Typography.Text className="section-heading">
Decimal Precision
</Typography.Text>
<Select
options={decimapPrecisionOptions}
value={decimalPrecision}
style={{ width: '100%' }}
className="panel-type-select"
defaultValue={decimapPrecisionOptions[0]?.value}
onChange={(val: PrecisionOption): void => setDecimalPrecision(val)}
/>
</section>
)}
{allowPanelColumnPreference && (
<ColumnUnitSelector
columnUnits={columnUnits}
setColumnUnits={setColumnUnits}
isNewDashboard={isNewDashboard}
/>
)}
</SettingsSection>
);
}

View File

@@ -0,0 +1,64 @@
.general-settings__name-description {
padding: 0 0 4px 0;
.general-settings__name-input {
display: flex;
padding: 6px 6px 6px 8px;
align-items: center;
gap: 4px;
flex: 1 0 0;
align-self: stretch;
border-radius: 2px;
border: 1px solid var(--bg-slate-400);
background: var(--bg-ink-300);
color: var(--bg-vanilla-100);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 18px; /* 128.571% */
letter-spacing: -0.07px;
}
.general-settings__description-input {
border-style: unset;
.ant-input {
display: flex;
height: 80px;
padding: 6px 6px 6px 8px;
align-items: flex-start;
gap: 4px;
border-radius: 2px;
border: 1px solid var(--bg-slate-400);
background: var(--bg-ink-300);
color: var(--bg-vanilla-100);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 18px; /* 128.571% */
letter-spacing: -0.07px;
}
}
}
.lightMode {
.general-settings__name-description {
border-top: 1px solid var(--bg-vanilla-300);
border-bottom: 1px solid var(--bg-vanilla-300);
.general-settings__name-input {
border: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-300);
color: var(--bg-ink-300);
}
.general-settings__description-input {
.ant-input {
border: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-300);
color: var(--bg-ink-300);
}
}
}
}

View File

@@ -0,0 +1,147 @@
import {
Dispatch,
SetStateAction,
useCallback,
useMemo,
useRef,
useState,
} from 'react';
import type { InputRef } from 'antd';
import { AutoComplete, Input, Typography } from 'antd';
import SettingsSection from 'components/SettingsSection/SettingsSection';
import { popupContainer } from 'utils/selectPopupContainer';
import './GeneralSettingsSection.styles.scss';
const { TextArea } = Input;
interface VariableOption {
value: string;
label: string;
}
interface GeneralSettingsSectionProps {
title: string;
setTitle: Dispatch<SetStateAction<string>>;
description: string;
setDescription: Dispatch<SetStateAction<string>>;
dashboardVariables: Record<string, { name?: string }>;
}
export function GeneralSettingsSection({
title,
setTitle,
description,
setDescription,
dashboardVariables,
}: GeneralSettingsSectionProps): JSX.Element {
const [inputValue, setInputValue] = useState(title);
const [autoCompleteOpen, setAutoCompleteOpen] = useState(false);
const [cursorPos, setCursorPos] = useState(0);
const inputRef = useRef<InputRef>(null);
const onChangeHandler = useCallback(
(setFunc: Dispatch<SetStateAction<string>>, value: string) => {
setFunc(value);
},
[],
);
const dashboardVariableOptions = useMemo<VariableOption[]>(() => {
return Object.entries(dashboardVariables).map(([, value]) => ({
value: value.name || '',
label: value.name || '',
}));
}, [dashboardVariables]);
const updateCursorAndDropdown = (value: string, pos: number): void => {
setCursorPos(pos);
const lastDollar = value.lastIndexOf('$', pos - 1);
setAutoCompleteOpen(lastDollar !== -1 && pos >= lastDollar + 1);
};
const onInputChange = (value: string): void => {
setInputValue(value);
onChangeHandler(setTitle, value);
setTimeout(() => {
const pos = inputRef.current?.input?.selectionStart ?? 0;
updateCursorAndDropdown(value, pos);
}, 0);
};
const onSelect = (selectedValue: string): void => {
const pos = cursorPos;
const value = inputValue;
const lastDollar = value.lastIndexOf('$', pos - 1);
const textBeforeDollar = value.substring(0, lastDollar);
const textAfterDollar = value.substring(lastDollar + 1);
const match = textAfterDollar.match(/^([a-zA-Z0-9_.]*)/);
const rest = textAfterDollar.substring(match ? match[1].length : 0);
const newValue = `${textBeforeDollar}$${selectedValue}${rest}`;
setInputValue(newValue);
onChangeHandler(setTitle, newValue);
setAutoCompleteOpen(false);
setTimeout(() => {
const newCursor = `${textBeforeDollar}$${selectedValue}`.length;
inputRef.current?.input?.setSelectionRange(newCursor, newCursor);
setCursorPos(newCursor);
}, 0);
};
const filterOption = (
currentInputValue: string,
option?: VariableOption,
): boolean => {
const pos = cursorPos;
const value = currentInputValue;
const lastDollar = value.lastIndexOf('$', pos - 1);
if (lastDollar === -1) {
return false;
}
const afterDollar = value.substring(lastDollar + 1, pos).toLowerCase();
return option?.value.toLowerCase().startsWith(afterDollar) || false;
};
const handleInputCursor = (): void => {
const pos = inputRef.current?.input?.selectionStart ?? 0;
updateCursorAndDropdown(inputValue, pos);
};
return (
<SettingsSection title="General" defaultOpen icon={null}>
<section className="general-settings__name-description control-container">
<Typography.Text className="section-heading">Name</Typography.Text>
<AutoComplete
options={dashboardVariableOptions}
value={inputValue}
onChange={onInputChange}
onSelect={onSelect}
filterOption={filterOption}
style={{ width: '100%' }}
getPopupContainer={popupContainer}
placeholder="Enter the panel name here..."
open={autoCompleteOpen}
>
<Input
rootClassName="general-settings__name-input"
ref={inputRef}
onSelect={handleInputCursor}
onClick={handleInputCursor}
onBlur={(): void => setAutoCompleteOpen(false)}
/>
</AutoComplete>
<Typography.Text className="section-heading">Description</Typography.Text>
<TextArea
placeholder="Enter the panel description here..."
bordered
allowClear
value={description}
onChange={(event): void =>
onChangeHandler(setDescription, event.target.value)
}
rootClassName="general-settings__description-input"
/>
</section>
</SettingsSection>
);
}

View File

@@ -0,0 +1,55 @@
.histogram-settings__bucket-config {
.histogram-settings__bucket-size-label {
margin-top: 8px;
}
.histogram-settings__bucket-input {
display: flex;
width: 100%;
height: 32px;
padding: 6px 6px 6px 8px;
align-items: center;
gap: 4px;
align-self: stretch;
border-radius: 2px;
border: 1px solid var(--bg-slate-400);
background: var(--bg-ink-300);
.ant-input {
background: var(--bg-ink-300);
}
}
.histogram-settings__combine-hist {
display: flex;
justify-content: space-between;
margin-top: 8px;
.histogram-settings__merge-label {
color: var(--bg-vanilla-400);
font-size: 13px;
font-style: normal;
font-weight: 400;
line-height: 18px; /* 138.462% */
letter-spacing: 0.52px;
text-transform: uppercase;
}
}
}
.lightMode {
.histogram-settings__bucket-config {
.histogram-settings__merge-label {
color: var(--bg-ink-400);
}
.histogram-settings__bucket-input {
border: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-300);
.ant-input {
background: var(--bg-vanilla-300);
}
}
}
}

View File

@@ -0,0 +1,70 @@
import { Dispatch, SetStateAction } from 'react';
import { InputNumber, Switch, Typography } from 'antd';
import SettingsSection from 'components/SettingsSection/SettingsSection';
import './HistogramBucketsSection.styles.scss';
interface HistogramBucketsSectionProps {
bucketCount: number;
setBucketCount: Dispatch<SetStateAction<number>>;
bucketWidth: number;
setBucketWidth: Dispatch<SetStateAction<number>>;
combineHistogram: boolean;
setCombineHistogram: Dispatch<SetStateAction<boolean>>;
}
export function HistogramBucketsSection({
bucketCount,
setBucketCount,
bucketWidth,
setBucketWidth,
combineHistogram,
setCombineHistogram,
}: HistogramBucketsSectionProps): JSX.Element {
return (
<SettingsSection title="Histogram / Buckets">
<section className="histogram-settings__bucket-config control-container">
<Typography.Text className="section-heading">
Number of buckets
</Typography.Text>
<InputNumber
value={bucketCount || null}
type="number"
min={0}
rootClassName="bucket-input"
placeholder="Default: 30"
onChange={(val): void => {
setBucketCount(val || 0);
}}
/>
<Typography.Text className="section-heading histogram-settings__bucket-size-label">
Bucket width
</Typography.Text>
<InputNumber
value={bucketWidth || null}
type="number"
precision={2}
placeholder="Default: Auto"
step={0.1}
min={0.0}
rootClassName="histogram-settings__bucket-input"
onChange={(val): void => {
setBucketWidth(val || 0);
}}
/>
<section className="histogram-settings__combine-hist">
<Typography.Text className="section-heading">
<span className="histogram-settings__merge-label">
Merge all series into one
</span>
</Typography.Text>
<Switch
checked={combineHistogram}
size="small"
onChange={(checked): void => setCombineHistogram(checked)}
/>
</section>
</section>
</SettingsSection>
);
}

View File

@@ -0,0 +1,73 @@
import { Dispatch, SetStateAction } from 'react';
import type { UseQueryResult } from 'react-query';
import { Select, Typography } from 'antd';
import SettingsSection from 'components/SettingsSection/SettingsSection';
import { Layers } from 'lucide-react';
import { SuccessResponse } from 'types/api';
import { LegendPosition } from 'types/api/dashboard/getAll';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import LegendColors from '../../LegendColors/LegendColors';
const { Option } = Select;
interface LegendSectionProps {
allowLegendPosition: boolean;
allowLegendColors: boolean;
legendPosition: LegendPosition;
setLegendPosition: Dispatch<SetStateAction<LegendPosition>>;
customLegendColors: Record<string, string>;
setCustomLegendColors: Dispatch<SetStateAction<Record<string, string>>>;
queryResponse?: UseQueryResult<
SuccessResponse<MetricRangePayloadProps, unknown>,
Error
>;
}
export function LegendSection({
allowLegendPosition,
allowLegendColors,
legendPosition,
setLegendPosition,
customLegendColors,
setCustomLegendColors,
queryResponse,
}: LegendSectionProps): JSX.Element {
return (
<SettingsSection title="Legend" icon={<Layers size={14} />}>
{allowLegendPosition && (
<section className="legend-position control-container">
<Typography.Text className="section-heading">Position</Typography.Text>
<Select
onChange={(value: LegendPosition): void => setLegendPosition(value)}
value={legendPosition}
style={{ width: '100%' }}
className="panel-type-select"
defaultValue={LegendPosition.BOTTOM}
>
<Option value={LegendPosition.BOTTOM}>
<div className="select-option">
<Typography.Text className="display">Bottom</Typography.Text>
</div>
</Option>
<Option value={LegendPosition.RIGHT}>
<div className="select-option">
<Typography.Text className="display">Right</Typography.Text>
</div>
</Option>
</Select>
</section>
)}
{allowLegendColors && (
<section className="legend-colors">
<LegendColors
customLegendColors={customLegendColors}
setCustomLegendColors={setCustomLegendColors}
queryResponse={queryResponse}
/>
</section>
)}
</SettingsSection>
);
}

View File

@@ -0,0 +1,10 @@
.thresholds-section {
padding: 12px 12px 16px 12px;
border-top: 1px solid var(--bg-slate-500);
}
.lightMode {
.thresholds-section {
border-top: 1px solid var(--bg-vanilla-300);
}
}

View File

@@ -0,0 +1,42 @@
import { Dispatch, SetStateAction } from 'react';
import SettingsSection from 'components/SettingsSection/SettingsSection';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { Antenna } from 'lucide-react';
import { ColumnUnit } from 'types/api/dashboard/getAll';
import ThresholdSelector from '../../Threshold/ThresholdSelector';
import { ThresholdProps } from '../../Threshold/types';
import './ThresholdsSection.styles.scss';
interface ThresholdsSectionProps {
thresholds: ThresholdProps[];
setThresholds: Dispatch<SetStateAction<ThresholdProps[]>>;
yAxisUnit: string;
selectedGraph: PANEL_TYPES;
columnUnits: ColumnUnit;
}
export function ThresholdsSection({
thresholds,
setThresholds,
yAxisUnit,
selectedGraph,
columnUnits,
}: ThresholdsSectionProps): JSX.Element {
return (
<SettingsSection
title="Thresholds"
icon={<Antenna size={14} />}
defaultOpen={!!thresholds.length}
>
<ThresholdSelector
thresholds={thresholds}
setThresholds={setThresholds}
yAxisUnit={yAxisUnit}
selectedGraph={selectedGraph}
columnUnits={columnUnits}
/>
</SettingsSection>
);
}

View File

@@ -0,0 +1,129 @@
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
import { Select, Switch, Typography } from 'antd';
import SettingsSection from 'components/SettingsSection/SettingsSection';
import TimePreference from 'components/TimePreferenceDropDown';
import { PANEL_TYPES } from 'constants/queryBuilder';
import GraphTypes, {
ItemsProps,
} from 'container/DashboardContainer/ComponentsSlider/menuItems';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { LayoutDashboard } from 'lucide-react';
import { DataSource } from 'types/common/queryBuilder';
import { timePreferance } from '../../timeItems';
const { Option } = Select;
interface VisualizationSettingsSectionProps {
selectedGraph: PANEL_TYPES;
setGraphHandler: (type: PANEL_TYPES) => void;
selectedTime: timePreferance;
setSelectedTime: Dispatch<SetStateAction<timePreferance>>;
stackedBarChart: boolean;
setStackedBarChart: Dispatch<SetStateAction<boolean>>;
isFillSpans: boolean;
setIsFillSpans: Dispatch<SetStateAction<boolean>>;
allowPanelTimePreference: boolean;
allowStackingBarChart: boolean;
allowFillSpans: boolean;
}
export function VisualizationSettingsSection({
selectedGraph,
setGraphHandler,
selectedTime,
setSelectedTime,
stackedBarChart,
setStackedBarChart,
isFillSpans,
setIsFillSpans,
allowPanelTimePreference,
allowStackingBarChart,
allowFillSpans,
}: VisualizationSettingsSectionProps): JSX.Element {
const { currentQuery } = useQueryBuilder();
const [graphTypes, setGraphTypes] = useState<ItemsProps[]>(GraphTypes);
useEffect(() => {
const queryContainsMetricsDataSource = currentQuery.builder.queryData.some(
(query) => query.dataSource === DataSource.METRICS,
);
if (queryContainsMetricsDataSource) {
setGraphTypes((prev) =>
prev.filter((graph) => graph.name !== PANEL_TYPES.LIST),
);
} else {
setGraphTypes(GraphTypes);
}
}, [currentQuery]);
return (
<SettingsSection
title="Visualization"
defaultOpen
icon={<LayoutDashboard size={14} />}
>
<section className="panel-type control-container">
<Typography.Text className="section-heading">Panel Type</Typography.Text>
<Select
onChange={setGraphHandler}
value={selectedGraph}
className="panel-type-select"
data-testid="panel-change-select"
data-stacking-state={stackedBarChart ? 'true' : 'false'}
>
{graphTypes.map((item) => (
<Option key={item.name} value={item.name}>
<div className="select-option">
<div className="icon">{item.icon}</div>
<Typography.Text className="display">{item.display}</Typography.Text>
</div>
</Option>
))}
</Select>
</section>
{allowPanelTimePreference && (
<section className="panel-time-preference control-container">
<Typography.Text className="section-heading">
Panel Time Preference
</Typography.Text>
<TimePreference
{...{
selectedTime,
setSelectedTime,
}}
/>
</section>
)}
{allowStackingBarChart && (
<section className="stack-chart control-container">
<Typography.Text className="section-heading">Stack series</Typography.Text>
<Switch
checked={stackedBarChart}
size="small"
onChange={(checked): void => setStackedBarChart(checked)}
/>
</section>
)}
{allowFillSpans && (
<section className="fill-gaps toggle-card">
<div className="toggle-card-text-container">
<Typography className="section-heading">Fill gaps</Typography>
<Typography.Text className="toggle-card-description">
Fill gaps in data with 0 for continuity
</Typography.Text>
</div>
<Switch
checked={isFillSpans}
size="small"
onChange={(checked): void => setIsFillSpans(checked)}
/>
</section>
)}
</SettingsSection>
);
}

View File

@@ -1,52 +1,16 @@
import {
Dispatch,
SetStateAction,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import { Dispatch, SetStateAction, useMemo } from 'react';
import { UseQueryResult } from 'react-query';
import type { InputRef } from 'antd';
import {
AutoComplete,
Input,
InputNumber,
Select,
Switch,
Typography,
} from 'antd';
import { PrecisionOption, PrecisionOptionsEnum } from 'components/Graph/types';
import SettingsSection from 'components/SettingsSection/SettingsSection';
import TimePreference from 'components/TimePreferenceDropDown';
import { Typography } from 'antd';
import { PrecisionOption } from 'components/Graph/types';
import { PANEL_TYPES, PanelDisplay } from 'constants/queryBuilder';
import GraphTypes, {
ItemsProps,
} from 'container/DashboardContainer/ComponentsSlider/menuItems';
import GraphTypes from 'container/DashboardContainer/ComponentsSlider/menuItems';
import { useDashboardVariables } from 'hooks/dashboard/useDashboardVariables';
import useCreateAlerts from 'hooks/queryBuilder/useCreateAlerts';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import {
FillMode,
LineInterpolation,
LineStyle,
} from 'lib/uPlotV2/config/types';
import {
Antenna,
Axis3D,
ConciergeBell,
Layers,
LayoutDashboard,
LineChart,
Link,
Paintbrush,
Pencil,
Plus,
SlidersHorizontal,
Spline,
SquareArrowOutUpRight,
} from 'lucide-react';
import { SuccessResponse } from 'types/api';
import {
ColumnUnit,
@@ -55,10 +19,7 @@ import {
Widgets,
} from 'types/api/dashboard/getAll';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { DataSource } from 'types/common/queryBuilder';
import { popupContainer } from 'utils/selectPopupContainer';
import { ColumnUnitSelector } from './ColumnUnitSelector/ColumnUnitSelector';
import {
panelTypeVsBucketConfig,
panelTypeVsColumnUnitPreferences,
@@ -79,32 +40,20 @@ import {
panelTypeVsThreshold,
panelTypeVsYAxisUnit,
} from './constants';
import ContextLinks from './ContextLinks';
import DashboardYAxisUnitSelectorWrapper from './DashboardYAxisUnitSelectorWrapper';
import { FillModeSelector } from './FillModeSelector';
import LegendColors from './LegendColors/LegendColors';
import { LineInterpolationSelector } from './LineInterpolationSelector';
import { LineStyleSelector } from './LineStyleSelector';
import ThresholdSelector from './Threshold/ThresholdSelector';
import { AlertsSection } from './SettingSections/AlertsSection/AlertsSection';
import { AxesSection } from './SettingSections/AxesSection/AxesSection';
import { ChartAppearanceSection } from './SettingSections/ChartAppearanceSection/ChartAppearanceSection';
import { ContextLinksSection } from './SettingSections/ContextLinksSection/ContextLinksSection';
import { FormattingUnitsSection } from './SettingSections/FormattingUnitsSection/FormattingUnitsSection';
import { GeneralSettingsSection } from './SettingSections/GeneralSettingsSection/GeneralSettingsSection';
import { HistogramBucketsSection } from './SettingSections/HistogramBucketsSection/HistogramBucketsSection';
import { LegendSection } from './SettingSections/LegendSection/LegendSection';
import { ThresholdsSection } from './SettingSections/ThresholdsSection/ThresholdsSection';
import { VisualizationSettingsSection } from './SettingSections/VisualizationSettingsSection/VisualizationSettingsSection';
import { ThresholdProps } from './Threshold/types';
import { timePreferance } from './timeItems';
import './RightContainer.styles.scss';
const { TextArea } = Input;
const { Option } = Select;
enum LogScale {
LINEAR = 'linear',
LOGARITHMIC = 'logarithmic',
}
interface VariableOption {
value: string;
label: string;
}
// eslint-disable-next-line sonarjs/cognitive-complexity
function RightContainer({
description,
setDescription,
@@ -158,20 +107,9 @@ function RightContainer({
isNewDashboard,
}: RightContainerProps): JSX.Element {
const { dashboardVariables } = useDashboardVariables();
const [inputValue, setInputValue] = useState(title);
const [autoCompleteOpen, setAutoCompleteOpen] = useState(false);
const [cursorPos, setCursorPos] = useState(0);
const inputRef = useRef<InputRef>(null);
const onChangeHandler = useCallback(
(setFunc: Dispatch<SetStateAction<string>>, value: string) => {
setFunc(value);
},
[],
);
const selectedGraphType =
GraphTypes.find((e) => e.name === selectedGraph)?.display || '';
const selectedGraphType = GraphTypes.find((e) => e.name === selectedGraph)
?.display as PanelDisplay;
const onCreateAlertsHandler = useCreateAlerts(selectedWidget, 'panelView');
@@ -200,17 +138,6 @@ function RightContainer({
const allowFillMode = panelTypeVsFillMode[selectedGraph];
const allowShowPoints = panelTypeVsShowPoints[selectedGraph];
const { currentQuery } = useQueryBuilder();
const [graphTypes, setGraphTypes] = useState<ItemsProps[]>(GraphTypes);
const dashboardVariableOptions = useMemo<VariableOption[]>(() => {
return Object.entries(dashboardVariables).map(([, value]) => ({
value: value.name || '',
label: value.name || '',
}));
}, [dashboardVariables]);
const isAxisSectionVisible = useMemo(() => allowSoftMinMax || allowLogScale, [
allowSoftMinMax,
allowLogScale,
@@ -227,96 +154,6 @@ function RightContainer({
[allowFillMode, allowLineStyle, allowLineInterpolation, allowShowPoints],
);
const updateCursorAndDropdown = (value: string, pos: number): void => {
setCursorPos(pos);
const lastDollar = value.lastIndexOf('$', pos - 1);
setAutoCompleteOpen(lastDollar !== -1 && pos >= lastDollar + 1);
};
const onInputChange = (value: string): void => {
setInputValue(value);
onChangeHandler(setTitle, value);
setTimeout(() => {
const pos = inputRef.current?.input?.selectionStart ?? 0;
updateCursorAndDropdown(value, pos);
}, 0);
};
const decimapPrecisionOptions = useMemo(() => {
return [
{ label: '0 decimals', value: PrecisionOptionsEnum.ZERO },
{ label: '1 decimal', value: PrecisionOptionsEnum.ONE },
{ label: '2 decimals', value: PrecisionOptionsEnum.TWO },
{ label: '3 decimals', value: PrecisionOptionsEnum.THREE },
];
}, []);
const handleInputCursor = (): void => {
const pos = inputRef.current?.input?.selectionStart ?? 0;
updateCursorAndDropdown(inputValue, pos);
};
const onSelect = (selectedValue: string): void => {
const pos = cursorPos;
const value = inputValue;
const lastDollar = value.lastIndexOf('$', pos - 1);
const textBeforeDollar = value.substring(0, lastDollar);
const textAfterDollar = value.substring(lastDollar + 1);
const match = textAfterDollar.match(/^([a-zA-Z0-9_.]*)/);
const rest = textAfterDollar.substring(match ? match[1].length : 0);
const newValue = `${textBeforeDollar}$${selectedValue}${rest}`;
setInputValue(newValue);
onChangeHandler(setTitle, newValue);
setAutoCompleteOpen(false);
setTimeout(() => {
const newCursor = `${textBeforeDollar}$${selectedValue}`.length;
inputRef.current?.input?.setSelectionRange(newCursor, newCursor);
setCursorPos(newCursor);
}, 0);
};
const filterOption = (
inputValue: string,
option?: VariableOption,
): boolean => {
const pos = cursorPos;
const value = inputValue;
const lastDollar = value.lastIndexOf('$', pos - 1);
if (lastDollar === -1) {
return false;
}
const afterDollar = value.substring(lastDollar + 1, pos).toLowerCase();
return option?.value.toLowerCase().startsWith(afterDollar) || false;
};
useEffect(() => {
const queryContainsMetricsDataSource = currentQuery.builder.queryData.some(
(query) => query.dataSource === DataSource.METRICS,
);
if (queryContainsMetricsDataSource) {
setGraphTypes((prev) =>
prev.filter((graph) => graph.name !== PANEL_TYPES.LIST),
);
} else {
setGraphTypes(GraphTypes);
}
}, [currentQuery]);
const softMinHandler = useCallback(
(value: number | null) => {
setSoftMin(value);
},
[setSoftMin],
);
const softMaxHandler = useCallback(
(value: number | null) => {
setSoftMax(value);
},
[setSoftMax],
);
return (
<div className="right-container">
<section className="header">
@@ -324,370 +161,117 @@ function RightContainer({
<Typography.Text className="header-text">Panel Settings</Typography.Text>
</section>
<SettingsSection title="General" defaultOpen icon={<Pencil size={14} />}>
<section className="name-description control-container">
<Typography.Text className="section-heading">Name</Typography.Text>
<AutoComplete
options={dashboardVariableOptions}
value={inputValue}
onChange={onInputChange}
onSelect={onSelect}
filterOption={filterOption}
style={{ width: '100%' }}
getPopupContainer={popupContainer}
placeholder="Enter the panel name here..."
open={autoCompleteOpen}
>
<Input
rootClassName="name-input"
ref={inputRef}
onSelect={handleInputCursor}
onClick={handleInputCursor}
onBlur={(): void => setAutoCompleteOpen(false)}
/>
</AutoComplete>
<Typography.Text className="section-heading">Description</Typography.Text>
<TextArea
placeholder="Enter the panel description here..."
bordered
allowClear
value={description}
onChange={(event): void =>
onChangeHandler(setDescription, event.target.value)
}
rootClassName="description-input"
/>
</section>
</SettingsSection>
<GeneralSettingsSection
title={title}
setTitle={setTitle}
description={description}
setDescription={setDescription}
dashboardVariables={dashboardVariables}
/>
<section className="panel-config">
<SettingsSection
title="Visualization"
defaultOpen
icon={<LayoutDashboard size={14} />}
>
<section className="panel-type control-container">
<Typography.Text className="section-heading">Panel Type</Typography.Text>
<Select
onChange={setGraphHandler}
value={selectedGraph}
className="panel-type-select"
data-testid="panel-change-select"
data-stacking-state={stackedBarChart ? 'true' : 'false'}
>
{graphTypes.map((item) => (
<Option key={item.name} value={item.name}>
<div className="select-option">
<div className="icon">{item.icon}</div>
<Typography.Text className="display">{item.display}</Typography.Text>
</div>
</Option>
))}
</Select>
</section>
<VisualizationSettingsSection
selectedGraph={selectedGraph}
setGraphHandler={setGraphHandler}
selectedTime={selectedTime}
setSelectedTime={setSelectedTime}
stackedBarChart={stackedBarChart}
setStackedBarChart={setStackedBarChart}
isFillSpans={isFillSpans}
setIsFillSpans={setIsFillSpans}
allowPanelTimePreference={allowPanelTimePreference}
allowStackingBarChart={allowStackingBarChart}
allowFillSpans={allowFillSpans}
/>
{allowPanelTimePreference && (
<section className="panel-time-preference control-container">
<Typography.Text className="section-heading">
Panel Time Preference
</Typography.Text>
<TimePreference
{...{
selectedTime,
setSelectedTime,
}}
/>
</section>
)}
{allowStackingBarChart && (
<section className="stack-chart control-container">
<Typography.Text className="section-heading">
Stack series
</Typography.Text>
<Switch
checked={stackedBarChart}
size="small"
onChange={(checked): void => setStackedBarChart(checked)}
/>
</section>
)}
{allowFillSpans && (
<section className="fill-gaps toggle-card">
<div className="toggle-card-text-container">
<Typography className="section-heading">Fill gaps</Typography>
<Typography.Text className="toggle-card-description">
Fill gaps in data with 0 for continuity
</Typography.Text>
</div>
<Switch
checked={isFillSpans}
size="small"
onChange={(checked): void => setIsFillSpans(checked)}
/>
</section>
)}
</SettingsSection>
<SettingsSection
title="Formatting & Units"
icon={<SlidersHorizontal size={14} />}
>
{allowYAxisUnit && (
<DashboardYAxisUnitSelectorWrapper
onSelect={setYAxisUnit}
value={yAxisUnit || ''}
fieldLabel={
selectedGraphType === PanelDisplay.VALUE ||
selectedGraphType === PanelDisplay.PIE
? 'Unit'
: 'Y Axis Unit'
}
// Only update the y-axis unit value automatically in create mode
shouldUpdateYAxisUnit={isNewDashboard}
/>
)}
{allowDecimalPrecision && (
<section className="decimal-precision-selector control-container">
<Typography.Text className="section-heading">
Decimal Precision
</Typography.Text>
<Select
options={decimapPrecisionOptions}
value={decimalPrecision}
style={{ width: '100%' }}
className="panel-type-select"
defaultValue={PrecisionOptionsEnum.TWO}
onChange={(val: PrecisionOption): void => setDecimalPrecision(val)}
/>
</section>
)}
{allowPanelColumnPreference && (
<ColumnUnitSelector
columnUnits={columnUnits}
setColumnUnits={setColumnUnits}
isNewDashboard={isNewDashboard}
/>
)}
</SettingsSection>
<FormattingUnitsSection
selectedGraphType={selectedGraphType}
yAxisUnit={yAxisUnit}
setYAxisUnit={setYAxisUnit}
isNewDashboard={isNewDashboard}
decimalPrecision={decimalPrecision}
setDecimalPrecision={setDecimalPrecision}
columnUnits={columnUnits}
setColumnUnits={setColumnUnits}
allowYAxisUnit={allowYAxisUnit}
allowDecimalPrecision={allowDecimalPrecision}
allowPanelColumnPreference={allowPanelColumnPreference}
/>
{isChartAppearanceSectionVisible && (
<SettingsSection title="Chart Appearance" icon={<Paintbrush size={14} />}>
{allowFillMode && (
<FillModeSelector value={fillMode} onChange={setFillMode} />
)}
{allowLineStyle && (
<LineStyleSelector value={lineStyle} onChange={setLineStyle} />
)}
{allowLineInterpolation && (
<LineInterpolationSelector
value={lineInterpolation}
onChange={setLineInterpolation}
/>
)}
{allowShowPoints && (
<section className="show-points toggle-card">
<div className="toggle-card-text-container">
<Typography.Text className="section-heading">
Show points
</Typography.Text>
<Typography.Text className="toggle-card-description">
Display individual data points on the chart
</Typography.Text>
</div>
<Switch size="small" checked={showPoints} onChange={setShowPoints} />
</section>
)}
</SettingsSection>
<ChartAppearanceSection
fillMode={fillMode}
setFillMode={setFillMode}
lineStyle={lineStyle}
setLineStyle={setLineStyle}
lineInterpolation={lineInterpolation}
setLineInterpolation={setLineInterpolation}
showPoints={showPoints}
setShowPoints={setShowPoints}
allowFillMode={allowFillMode}
allowLineStyle={allowLineStyle}
allowLineInterpolation={allowLineInterpolation}
allowShowPoints={allowShowPoints}
/>
)}
{isAxisSectionVisible && (
<SettingsSection title="Axes" icon={<Axis3D size={14} />}>
{allowSoftMinMax && (
<section className="soft-min-max">
<section className="container">
<Typography.Text className="text">Soft Min</Typography.Text>
<InputNumber
type="number"
value={softMin}
onChange={softMinHandler}
rootClassName="input"
/>
</section>
<section className="container">
<Typography.Text className="text">Soft Max</Typography.Text>
<InputNumber
value={softMax}
type="number"
rootClassName="input"
onChange={softMaxHandler}
/>
</section>
</section>
)}
{allowLogScale && (
<section className="log-scale control-container">
<Typography.Text className="section-heading">
Y Axis Scale
</Typography.Text>
<Select
onChange={(value): void =>
setIsLogScale(value === LogScale.LOGARITHMIC)
}
value={isLogScale ? LogScale.LOGARITHMIC : LogScale.LINEAR}
style={{ width: '100%' }}
className="panel-type-select"
defaultValue={LogScale.LINEAR}
>
<Option value={LogScale.LINEAR}>
<div className="select-option">
<div className="icon">
<LineChart size={16} />
</div>
<Typography.Text className="display">Linear</Typography.Text>
</div>
</Option>
<Option value={LogScale.LOGARITHMIC}>
<div className="select-option">
<div className="icon">
<Spline size={16} />
</div>
<Typography.Text className="display">Logarithmic</Typography.Text>
</div>
</Option>
</Select>
</section>
)}
</SettingsSection>
<AxesSection
allowSoftMinMax={allowSoftMinMax}
allowLogScale={allowLogScale}
softMin={softMin}
softMax={softMax}
setSoftMin={setSoftMin}
setSoftMax={setSoftMax}
isLogScale={isLogScale}
setIsLogScale={setIsLogScale}
/>
)}
{isLegendSectionVisible && (
<SettingsSection title="Legend" icon={<Layers size={14} />}>
{allowLegendPosition && (
<section className="legend-position control-container">
<Typography.Text className="section-heading">Position</Typography.Text>
<Select
onChange={(value: LegendPosition): void => setLegendPosition(value)}
value={legendPosition}
style={{ width: '100%' }}
className="panel-type-select"
defaultValue={LegendPosition.BOTTOM}
>
<Option value={LegendPosition.BOTTOM}>
<div className="select-option">
<Typography.Text className="display">Bottom</Typography.Text>
</div>
</Option>
<Option value={LegendPosition.RIGHT}>
<div className="select-option">
<Typography.Text className="display">Right</Typography.Text>
</div>
</Option>
</Select>
</section>
)}
{allowLegendColors && (
<section className="legend-colors">
<LegendColors
customLegendColors={customLegendColors}
setCustomLegendColors={setCustomLegendColors}
queryResponse={queryResponse}
/>
</section>
)}
</SettingsSection>
<LegendSection
allowLegendPosition={allowLegendPosition}
allowLegendColors={allowLegendColors}
legendPosition={legendPosition}
setLegendPosition={setLegendPosition}
customLegendColors={customLegendColors}
setCustomLegendColors={setCustomLegendColors}
queryResponse={queryResponse}
/>
)}
{allowBucketConfig && (
<SettingsSection title="Histogram / Buckets">
<section className="bucket-config control-container">
<Typography.Text className="section-heading">
Number of buckets
</Typography.Text>
<InputNumber
value={bucketCount || null}
type="number"
min={0}
rootClassName="bucket-input"
placeholder="Default: 30"
onChange={(val): void => {
setBucketCount(val || 0);
}}
/>
<Typography.Text className="section-heading bucket-size-label">
Bucket width
</Typography.Text>
<InputNumber
value={bucketWidth || null}
type="number"
precision={2}
placeholder="Default: Auto"
step={0.1}
min={0.0}
rootClassName="bucket-input"
onChange={(val): void => {
setBucketWidth(val || 0);
}}
/>
<section className="combine-hist">
<Typography.Text className="section-heading">
Merge all series into one
</Typography.Text>
<Switch
checked={combineHistogram}
size="small"
onChange={(checked): void => setCombineHistogram(checked)}
/>
</section>
</section>
</SettingsSection>
<HistogramBucketsSection
bucketCount={bucketCount}
setBucketCount={setBucketCount}
bucketWidth={bucketWidth}
setBucketWidth={setBucketWidth}
combineHistogram={combineHistogram}
setCombineHistogram={setCombineHistogram}
/>
)}
</section>
{allowCreateAlerts && (
<section className="alerts" onClick={onCreateAlertsHandler}>
<div className="left-section">
<ConciergeBell size={14} className="bell-icon" />
<Typography.Text className="alerts-text">Alerts</Typography.Text>
<SquareArrowOutUpRight size={10} className="info-icon" />
</div>
<Plus size={14} className="plus-icon" />
</section>
<AlertsSection onCreateAlertsHandler={onCreateAlertsHandler} />
)}
{allowContextLinks && (
<SettingsSection
title="Context Links"
icon={<Link size={14} />}
defaultOpen={!!contextLinks.linksData.length}
>
<ContextLinks
contextLinks={contextLinks}
setContextLinks={setContextLinks}
selectedWidget={selectedWidget}
/>
</SettingsSection>
<ContextLinksSection
contextLinks={contextLinks}
setContextLinks={setContextLinks}
selectedWidget={selectedWidget}
/>
)}
{allowThreshold && (
<SettingsSection
title="Thresholds"
icon={<Antenna size={14} />}
defaultOpen={!!thresholds.length}
>
<ThresholdSelector
thresholds={thresholds}
setThresholds={setThresholds}
yAxisUnit={yAxisUnit}
selectedGraph={selectedGraph}
columnUnits={columnUnits}
/>
</SettingsSection>
<ThresholdsSection
thresholds={thresholds}
setThresholds={setThresholds}
yAxisUnit={yAxisUnit}
selectedGraph={selectedGraph}
columnUnits={columnUnits}
/>
)}
</div>
);