diff --git a/frontend/src/metabase/parameters/components/ParameterValueWidget.jsx b/frontend/src/metabase/parameters/components/ParameterValueWidget.jsx index b60831c896e9769449f47bab43dcdddd03707b5d..2c802296969933abcfe2b89a07fe8c730b214508 100644 --- a/frontend/src/metabase/parameters/components/ParameterValueWidget.jsx +++ b/frontend/src/metabase/parameters/components/ParameterValueWidget.jsx @@ -165,6 +165,7 @@ export default class ParameterValueWidget extends Component { isEnabled={isDashParamWithoutMapping} > <div + ref={this.trigger} className={cx(S.parameter, S.noPopover, className, { [S.selected]: hasValue, [S.isEditing]: isEditing, @@ -173,6 +174,7 @@ export default class ParameterValueWidget extends Component { {showTypeIcon && <ParameterTypeIcon parameter={parameter} />} <Widget {...this.props} + target={this.getTargetRef()} onFocusChanged={this.onFocusChanged} onPopoverClose={this.onPopoverClose} disabled={isDashParamWithoutMapping} @@ -228,6 +230,7 @@ export default class ParameterValueWidget extends Component { > <Widget {...this.props} + target={this.getTargetRef()} onFocusChanged={this.onFocusChanged} onPopoverClose={this.onPopoverClose} disabled={isDashParamWithoutMapping} @@ -270,6 +273,7 @@ function Widget({ parameters, dashboard, disabled, + target, }) { const DateWidget = DATE_WIDGETS[parameter.type]; const fields = getFields(metadata, parameter); @@ -291,6 +295,7 @@ function Widget({ } else if (fields.length > 0 && parameter.hasOnlyFieldTargets) { return ( <ParameterFieldWidget + target={target} parameter={parameter} parameters={parameters} dashboard={dashboard} diff --git a/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/ParameterFieldWidget.jsx b/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/ParameterFieldWidget.jsx index e6bc2a26b5e95170db35743449f3747a92073403..8e54fa1429ed7e326fc4a8045015e07f4fa692e3 100644 --- a/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/ParameterFieldWidget.jsx +++ b/frontend/src/metabase/parameters/components/widgets/ParameterFieldWidget/ParameterFieldWidget.jsx @@ -7,7 +7,7 @@ import _ from "underscore"; import FieldValuesWidget from "metabase/components/FieldValuesWidget"; import ParameterFieldWidgetValue from "./ParameterFieldWidgetValue/ParameterFieldWidgetValue"; -import Popover from "metabase/components/Popover"; +import TippyPopover from "metabase/components/Popover/TippyPopover"; import Button from "metabase/core/components/Button"; import { normalizeValue } from "./normalizeValue"; @@ -30,6 +30,7 @@ const propTypes = { placeholder: PropTypes.string.isRequired, setValue: PropTypes.func.isRequired, value: PropTypes.string, + target: PropTypes.instanceOf(Element).isRequired, }; const BORDER_WIDTH = 1; @@ -118,69 +119,78 @@ export default class ParameterFieldWidget extends Component { ); } else { return ( - <Popover hasArrow={false} onClose={() => focusChanged(false)}> - <div - className={cx("relative PopoverBody--marginBottom", { - p2: !isEqualsOp, - })} - > - {verboseName && !isEqualsOp && ( - <div className="text-bold mb1">{verboseName}...</div> - )} - - {_.times(numFields, index => { - const value = multi ? unsavedValue : [unsavedValue[index]]; - const onValueChange = multi - ? newValues => this.setState({ value: newValues }) - : ([value]) => { - const newValues = [...unsavedValue]; - newValues[index] = value; - this.setState({ value: newValues }); - }; - return ( - <FieldValuesWidget - key={index} - className={cx("input", numFields - 1 !== index && "mb1")} - value={value} - parameter={parameter} - parameters={parameters} - dashboard={dashboard} - onChange={onValueChange} - placeholder={placeholder} - fields={fields} - autoFocus={index === 0} - multi={multi} - disableSearch={disableSearch} - formatOptions={ - operator && getFilterArgumentFormatOptions(operator, index) + <TippyPopover + reference={this.props.target} + placement="bottom-start" + visible + onClose={() => focusChanged(false)} + content={ + <div + className={cx("relative PopoverBody--marginBottom", { + p2: !isEqualsOp, + })} + > + {verboseName && !isEqualsOp && ( + <div className="text-bold mb1">{verboseName}...</div> + )} + + {_.times(numFields, index => { + const value = multi ? unsavedValue : [unsavedValue[index]]; + const onValueChange = multi + ? newValues => this.setState({ value: newValues }) + : ([value]) => { + const newValues = [...unsavedValue]; + newValues[index] = value; + this.setState({ value: newValues }); + }; + return ( + <FieldValuesWidget + key={index} + className={cx("input", numFields - 1 !== index && "mb1")} + value={value} + parameter={parameter} + parameters={parameters} + dashboard={dashboard} + onChange={onValueChange} + placeholder={placeholder} + fields={fields} + autoFocus={index === 0} + multi={multi} + disableSearch={disableSearch} + formatOptions={ + operator && + getFilterArgumentFormatOptions(operator, index) + } + color="brand" + style={{ + borderWidth: BORDER_WIDTH, + minWidth: widgetWidth + ? widgetWidth + BORDER_WIDTH * 2 + : null, + }} + minWidth={300} + maxWidth={400} + /> + ); + })} + <div className={footerClassName}> + <Button + primary + className="ml-auto" + disabled={ + savedValue.length === 0 && unsavedValue.length === 0 } - color="brand" - style={{ - borderWidth: BORDER_WIDTH, - minWidth: widgetWidth - ? widgetWidth + BORDER_WIDTH * 2 - : null, + onClick={() => { + setValue(unsavedValue.length > 0 ? unsavedValue : null); + focusChanged(false); }} - minWidth={300} - maxWidth={400} - /> - ); - })} - <div className={footerClassName}> - <Button - primary - className="ml-auto" - disabled={savedValue.length === 0 && unsavedValue.length === 0} - onClick={() => { - setValue(unsavedValue.length > 0 ? unsavedValue : null); - focusChanged(false); - }} - > - {savedValue.length > 0 ? t`Update filter` : t`Add filter`} - </Button> + > + {savedValue.length > 0 ? t`Update filter` : t`Add filter`} + </Button> + </div> </div> - </div> - </Popover> + } + /> ); } }