From c55c091145730617b7718f10740dccda6dba6a68 Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Fri, 26 May 2023 09:30:46 +0100 Subject: [PATCH] Query Editor: Ensure dropdown menus position correctly (#69062) prevent showing menu until options have loaded --- .../querybuilder/components/LabelFilterItem.tsx | 15 +++++++++++++++ .../querybuilder/shared/LabelFilterItem.tsx | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/public/app/plugins/datasource/prometheus/querybuilder/components/LabelFilterItem.tsx b/public/app/plugins/datasource/prometheus/querybuilder/components/LabelFilterItem.tsx index 645339f8fea..d6fb3e61995 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/components/LabelFilterItem.tsx +++ b/public/app/plugins/datasource/prometheus/querybuilder/components/LabelFilterItem.tsx @@ -41,6 +41,11 @@ export function LabelFilterItem({ isLoadingLabelNames?: boolean; isLoadingLabelValues?: boolean; }>({}); + // there's a bug in react-select where the menu doesn't recalculate its position when the options are loaded asynchronously + // see https://github.com/grafana/grafana/issues/63558 + // instead, we explicitly control the menu visibility and prevent showing it until the options have fully loaded + const [labelNamesMenuOpen, setLabelNamesMenuOpen] = useState(false); + const [labelValuesMenuOpen, setLabelValuesMenuOpen] = useState(false); const isMultiSelect = (operator = item.op) => { return operators.find((op) => op.label === operator)?.isMultiValue; @@ -75,8 +80,13 @@ export function LabelFilterItem({ onOpenMenu={async () => { setState({ isLoadingLabelNames: true }); const labelNames = await onGetLabelNames(item); + setLabelNamesMenuOpen(true); setState({ labelNames, isLoadingLabelNames: undefined }); }} + onCloseMenu={() => { + setLabelNamesMenuOpen(false); + }} + isOpen={labelNamesMenuOpen} isLoading={state.isLoadingLabelNames ?? false} options={state.labelNames} onChange={(change) => { @@ -129,12 +139,17 @@ export function LabelFilterItem({ if (labelValues.length > PROMETHEUS_QUERY_BUILDER_MAX_RESULTS) { labelValues.splice(0, labelValues.length - PROMETHEUS_QUERY_BUILDER_MAX_RESULTS); } + setLabelValuesMenuOpen(true); setState({ ...state, labelValues, isLoadingLabelValues: undefined, }); }} + onCloseMenu={() => { + setLabelValuesMenuOpen(false); + }} + isOpen={labelValuesMenuOpen} defaultOptions={state.labelValues} isMulti={isMultiSelect()} isLoading={state.isLoadingLabelValues} diff --git a/public/app/plugins/datasource/prometheus/querybuilder/shared/LabelFilterItem.tsx b/public/app/plugins/datasource/prometheus/querybuilder/shared/LabelFilterItem.tsx index 093ff0c5c2c..cdd1f7cad00 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/shared/LabelFilterItem.tsx +++ b/public/app/plugins/datasource/prometheus/querybuilder/shared/LabelFilterItem.tsx @@ -39,6 +39,11 @@ export function LabelFilterItem({ isLoadingLabelNames?: boolean; isLoadingLabelValues?: boolean; }>({}); + // there's a bug in react-select where the menu doesn't recalculate its position when the options are loaded asynchronously + // see https://github.com/grafana/grafana/issues/63558 + // instead, we explicitly control the menu visibility and prevent showing it until the options have fully loaded + const [labelNamesMenuOpen, setLabelNamesMenuOpen] = useState(false); + const [labelValuesMenuOpen, setLabelValuesMenuOpen] = useState(false); const CONFLICTING_LABEL_FILTER_ERROR_MESSAGE = 'You have conflicting label filters'; const isMultiSelect = (operator = item.op) => { @@ -79,8 +84,13 @@ export function LabelFilterItem({ onOpenMenu={async () => { setState({ isLoadingLabelNames: true }); const labelNames = await onGetLabelNames(item); + setLabelNamesMenuOpen(true); setState({ labelNames, isLoadingLabelNames: undefined }); }} + onCloseMenu={() => { + setLabelNamesMenuOpen(false); + }} + isOpen={labelNamesMenuOpen} isLoading={state.isLoadingLabelNames} options={state.labelNames} onChange={(change) => { @@ -131,7 +141,12 @@ export function LabelFilterItem({ labelValues, isLoadingLabelValues: undefined, }); + setLabelValuesMenuOpen(true); }} + onCloseMenu={() => { + setLabelValuesMenuOpen(false); + }} + isOpen={labelValuesMenuOpen} isMulti={isMultiSelect()} isLoading={state.isLoadingLabelValues} options={getOptions()}