From 90753dabe328d41353814f7c244e3faffb316bcc Mon Sep 17 00:00:00 2001 From: "Mahatthana (Kelvin) Nomsawadi" <mahatthana.n@gmail.com> Date: Tue, 12 Apr 2022 12:10:18 +0700 Subject: [PATCH] Migrate ExpressionEditorHelpText to TippyPopover (#21572) --- .../metabase/components/Popover/Popover.css | 1 + .../components/ExpressionPopover.jsx | 5 +- .../expressions/ExpressionEditorHelpText.tsx | 89 ++++++++++--------- .../expressions/ExpressionEditorTextfield.jsx | 7 +- .../expressions/ExpressionWidget.jsx | 5 +- 5 files changed, 63 insertions(+), 44 deletions(-) diff --git a/frontend/src/metabase/components/Popover/Popover.css b/frontend/src/metabase/components/Popover/Popover.css index 1fdc09aae15..63356fd2563 100644 --- a/frontend/src/metabase/components/Popover/Popover.css +++ b/frontend/src/metabase/components/Popover/Popover.css @@ -45,6 +45,7 @@ } .tippy-box[data-theme~="popover"] { + font-size: inherit; background-color: var(--color-bg-white); color: var(--color-text-default); border: 1px solid var(--color-border); diff --git a/frontend/src/metabase/query_builder/components/ExpressionPopover.jsx b/frontend/src/metabase/query_builder/components/ExpressionPopover.jsx index 674a27ae519..5af772ed87a 100644 --- a/frontend/src/metabase/query_builder/components/ExpressionPopover.jsx +++ b/frontend/src/metabase/query_builder/components/ExpressionPopover.jsx @@ -16,6 +16,8 @@ export default class ExpressionPopover extends React.Component { isBlank: true, }; + helpTextTarget = React.createRef(); + render() { const { title, @@ -41,8 +43,9 @@ export default class ExpressionPopover extends React.Component { <h3 className="inline-block pl1">{title}</h3> </a> </div> - <div className="p1"> + <div className="p1" ref={this.helpTextTarget}> <ExpressionEditorTextfield + helpTextTarget={this.helpTextTarget.current} startRule={startRule} expression={expression} query={query} diff --git a/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText.tsx b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText.tsx index 3c8792e889f..c2b6fce4136 100644 --- a/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText.tsx +++ b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorHelpText.tsx @@ -5,7 +5,7 @@ import MetabaseSettings from "metabase/lib/settings"; import colors from "metabase/lib/colors"; import ExternalLink from "metabase/core/components/ExternalLink"; import Icon from "metabase/components/Icon"; -import Popover from "metabase/components/Popover"; +import TippyPopover from "metabase/components/Popover/TippyPopover"; type Arg = { name: string; @@ -22,48 +22,55 @@ type HelpText = { interface HelpTextProps { helpText: HelpText; width: number; + target: Element; } -const HelpText = ({ helpText, width }: HelpTextProps) => - helpText ? ( - <Popover - tetherOptions={{ - attachment: "top left", - targetAttachment: "bottom left", - }} - style={{ width }} - isOpen - > - {/* Prevent stealing focus from input box causing the help text to be closed (metabase#17548) */} - <div onMouseDown={e => e.preventDefault()}> - <p - className="p2 m0 text-monospace text-bold" - style={{ background: colors["bg-yellow"] }} - > - {helpText.structure} - </p> - <div className="p2 border-top"> - <p className="mt0 text-bold">{helpText.description}</p> - <p className="text-code m0 text-body">{helpText.example}</p> - </div> - <div className="p2 border-top"> - {helpText.args.map(({ name, description }, index) => ( - <div key={index}> - <h4 className="text-medium">{name}</h4> - <p className="mt1 text-bold">{description}</p> +const HelpText = ({ helpText, width, target }: HelpTextProps) => { + if (!helpText) { + return null; + } + + return ( + <TippyPopover + maxWidth={width} + reference={target} + placement="bottom-start" + visible + content={ + <> + {/* Prevent stealing focus from input box causing the help text to be closed (metabase#17548) */} + <div onMouseDown={e => e.preventDefault()}> + <p + className="p2 m0 text-monospace text-bold" + style={{ background: colors["bg-yellow"] }} + > + {helpText.structure} + </p> + <div className="p2 border-top"> + <p className="mt0 text-bold">{helpText.description}</p> + <p className="text-code m0 text-body">{helpText.example}</p> + </div> + <div className="p2 border-top"> + {helpText.args.map(({ name, description }, index) => ( + <div key={index}> + <h4 className="text-medium">{name}</h4> + <p className="mt1 text-bold">{description}</p> + </div> + ))} + <ExternalLink + className="link text-bold block my1" + target="_blank" + href={MetabaseSettings.docsUrl("users-guide/expressions")} + > + <Icon name="reference" size={12} className="mr1" /> + {t`Learn more`} + </ExternalLink> </div> - ))} - <ExternalLink - className="link text-bold block my1" - target="_blank" - href={MetabaseSettings.docsUrl("users-guide/expressions")} - > - <Icon name="reference" size={12} className="mr1" /> - {t`Learn more`} - </ExternalLink> - </div> - </div> - </Popover> - ) : null; + </div> + </> + } + ></TippyPopover> + ); +}; export default HelpText; diff --git a/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorTextfield.jsx b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorTextfield.jsx index df8a2570355..0c2e1eab613 100644 --- a/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorTextfield.jsx +++ b/frontend/src/metabase/query_builder/components/expressions/ExpressionEditorTextfield.jsx @@ -60,6 +60,7 @@ export default class ExpressionEditorTextfield extends React.Component { onError: PropTypes.func.isRequired, startRule: PropTypes.string.isRequired, onBlankChange: PropTypes.func, + helpTextTarget: PropTypes.instanceOf(Element).isRequired, }; static defaultProps = { @@ -445,7 +446,11 @@ export default class ExpressionEditorTextfield extends React.Component { /> </EditorContainer> <ErrorMessage error={errorMessage} /> - <HelpText helpText={this.state.helpText} width={this.props.width} /> + <HelpText + target={this.props.helpTextTarget} + helpText={this.state.helpText} + width={this.props.width} + /> </React.Fragment> ); } diff --git a/frontend/src/metabase/query_builder/components/expressions/ExpressionWidget.jsx b/frontend/src/metabase/query_builder/components/expressions/ExpressionWidget.jsx index bdc3a14b394..fbb152dac74 100644 --- a/frontend/src/metabase/query_builder/components/expressions/ExpressionWidget.jsx +++ b/frontend/src/metabase/query_builder/components/expressions/ExpressionWidget.jsx @@ -30,6 +30,8 @@ export default class ExpressionWidget extends Component { name: "", }; + helpTextTarget = React.createRef(); + UNSAFE_componentWillMount() { this.UNSAFE_componentWillReceiveProps(this.props); } @@ -61,8 +63,9 @@ export default class ExpressionWidget extends Component { <div style={{ maxWidth: "600px" }}> <div className="p2"> <div className="h5 text-uppercase text-medium text-bold">{t`Field formula`}</div> - <div> + <div ref={this.helpTextTarget}> <ExpressionEditorTextfield + helpTextTarget={this.helpTextTarget.current} expression={expression} query={query} onChange={parsedExpression => -- GitLab