From 9a3144eca054fee43609b865e36806315a291cd5 Mon Sep 17 00:00:00 2001 From: Ryan Laurie <30528226+iethree@users.noreply.github.com> Date: Wed, 14 Sep 2022 14:54:32 -0600 Subject: [PATCH] Action Creator 6: Allow user-defined options for select and radio inputs (#25404) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * allow user-defined select options * Update frontend/src/metabase/writeback/components/ActionCreator/FormCreator/utils.ts 🤦 --- .../FormCreator/FormCreator.styled.tsx | 16 +++++++ .../ActionCreator/FormCreator/FormCreator.tsx | 37 ++++++++++++++- .../FormCreator/OptionEditor.styled.tsx | 28 ++++++++++++ .../FormCreator/OptionEditor.tsx | 45 +++++++++++++++++++ .../ActionCreator/FormCreator/utils.ts | 11 ++++- 5 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 frontend/src/metabase/writeback/components/ActionCreator/FormCreator/OptionEditor.styled.tsx create mode 100644 frontend/src/metabase/writeback/components/ActionCreator/FormCreator/OptionEditor.tsx diff --git a/frontend/src/metabase/writeback/components/ActionCreator/FormCreator/FormCreator.styled.tsx b/frontend/src/metabase/writeback/components/ActionCreator/FormCreator/FormCreator.styled.tsx index 6ec31bbcc09..43b1a3e784f 100644 --- a/frontend/src/metabase/writeback/components/ActionCreator/FormCreator/FormCreator.styled.tsx +++ b/frontend/src/metabase/writeback/components/ActionCreator/FormCreator/FormCreator.styled.tsx @@ -1,5 +1,6 @@ import styled from "@emotion/styled"; import InputBase from "metabase/core/components/Input"; +import Button from "metabase/core/components/Button"; import { color } from "metabase/lib/colors"; import { space } from "metabase/styled-components/theme"; @@ -40,3 +41,18 @@ export const EmptyFormPlaceholderWrapper = styled.div` max-width: 20rem; margin: 5rem auto; `; + +export const EditButton = styled(Button)` + color: ${color("brand")}; + background-opacity: 0; + &:hover { + color: ${color("accent0-light")}; + } +`; + +export const FormSettingsPreviewContainer = styled.div` + display: flex; + flex-direction: column; + gap: ${space(1)}; + min-width: 12rem; +`; diff --git a/frontend/src/metabase/writeback/components/ActionCreator/FormCreator/FormCreator.tsx b/frontend/src/metabase/writeback/components/ActionCreator/FormCreator/FormCreator.tsx index d43b0c355f9..fe84275af26 100644 --- a/frontend/src/metabase/writeback/components/ActionCreator/FormCreator/FormCreator.tsx +++ b/frontend/src/metabase/writeback/components/ActionCreator/FormCreator/FormCreator.tsx @@ -7,6 +7,7 @@ import type { ActionFormSettings, FieldSettings } from "metabase-types/api"; import { FieldSettingsPopover } from "./FieldSettingsPopover"; import { getDefaultFormSettings, getDefaultFieldSettings } from "./utils"; import { FormField } from "./FormField"; +import { OptionEditor } from "./OptionEditor"; import { FormItemWrapper, @@ -14,6 +15,8 @@ import { FormItemName, FormSettings, EmptyFormPlaceholderWrapper, + FormSettingsPreviewContainer, + EditButton, } from "./FormCreator.styled"; export function FormCreator({ @@ -74,12 +77,44 @@ function FormItem({ fieldSettings: FieldSettings; onChange: (fieldSettings: FieldSettings) => void; }) { + const [isEditingOptions, setIsEditingOptions] = useState(false); const name = tag.name; + + const updateOptions = (newOptions: (string | number)[]) => { + onChange({ + ...fieldSettings, + valueOptions: newOptions, + }); + setIsEditingOptions(false); + }; + + const hasOptions = + fieldSettings.inputType === "dropdown" || + fieldSettings.inputType === "inline-select"; + return ( <FormItemWrapper> <FormItemName>{name}</FormItemName> <FormSettings> - <FormField tag={tag} fieldSettings={fieldSettings} /> + <FormSettingsPreviewContainer> + {isEditingOptions && hasOptions ? ( + <OptionEditor + options={fieldSettings.valueOptions ?? []} + onChange={updateOptions} + /> + ) : ( + <FormField tag={tag} fieldSettings={fieldSettings} /> + )} + {!isEditingOptions && hasOptions && ( + <EditButton + onClick={() => setIsEditingOptions(true)} + borderless + small + > + {t`Edit options`} + </EditButton> + )} + </FormSettingsPreviewContainer> <FieldSettingsPopover fieldSettings={fieldSettings} onChange={onChange} diff --git a/frontend/src/metabase/writeback/components/ActionCreator/FormCreator/OptionEditor.styled.tsx b/frontend/src/metabase/writeback/components/ActionCreator/FormCreator/OptionEditor.styled.tsx new file mode 100644 index 00000000000..358e4133b7f --- /dev/null +++ b/frontend/src/metabase/writeback/components/ActionCreator/FormCreator/OptionEditor.styled.tsx @@ -0,0 +1,28 @@ +import styled from "@emotion/styled"; +import InputBase from "metabase/core/components/Input"; + +import { color } from "metabase/lib/colors"; +import { space } from "metabase/styled-components/theme"; + +export const OptionEditorContainer = styled.div` + display: flex; + flex-direction: column; +`; + +export const AddMorePrompt = styled.div` + text-align: center; + font-size: 0.875rem; + margin: ${space(1)} 0; + height: 1.25rem; + color: ${color("text-light")}; + transition: opacity 0.2s ease-in-out; +`; + +export const StyledTextArea = styled.textarea` + resize: none; + border: none; + outline: 1px solid ${color("border")}; + width: 20rem; + border-radius: ${space(1)}; + padding: ${space(1)}; +`; diff --git a/frontend/src/metabase/writeback/components/ActionCreator/FormCreator/OptionEditor.tsx b/frontend/src/metabase/writeback/components/ActionCreator/FormCreator/OptionEditor.tsx new file mode 100644 index 00000000000..860a0e2710a --- /dev/null +++ b/frontend/src/metabase/writeback/components/ActionCreator/FormCreator/OptionEditor.tsx @@ -0,0 +1,45 @@ +import React, { useState } from "react"; +import { t } from "ttag"; + +import Button from "metabase/core/components/Button"; + +import { + OptionEditorContainer, + AddMorePrompt, + StyledTextArea, +} from "./OptionEditor.styled"; + +type ValueOptions = (string | number)[]; + +const optionsToText = (options: ValueOptions) => options.join("\n"); +const textToOptions = (text: string): ValueOptions => + text.split("\n").map(option => option.trim()); + +export const OptionEditor = ({ + options, + onChange, +}: { + options: ValueOptions; + onChange: (options: ValueOptions) => void; +}) => { + const [text, setText] = useState(optionsToText(options)); + const save = () => { + onChange(textToOptions(text)); + }; + + return ( + <OptionEditorContainer> + <StyledTextArea + value={text} + onChange={e => setText(e.target.value)} + placeholder={t`Enter one option per line`} + /> + <AddMorePrompt style={{ opacity: text.length ? 1 : 0 }}> + {t`Press enter to add another option`} + </AddMorePrompt> + <Button onClick={save} small> + {t`Save`} + </Button> + </OptionEditorContainer> + ); +}; diff --git a/frontend/src/metabase/writeback/components/ActionCreator/FormCreator/utils.ts b/frontend/src/metabase/writeback/components/ActionCreator/FormCreator/utils.ts index a7168563550..e0676b02f5d 100644 --- a/frontend/src/metabase/writeback/components/ActionCreator/FormCreator/utils.ts +++ b/frontend/src/metabase/writeback/components/ActionCreator/FormCreator/utils.ts @@ -40,6 +40,9 @@ const getSampleOptions = () => [ { name: t`Option Three`, value: 3 }, ]; +const getOptionsFromArray = (options: (number | string)[]) => + options.map(o => ({ name: o, value: o })); + const getParameterFieldProps = (fieldSettings: FieldSettings) => { switch (fieldSettings.inputType) { case "string": @@ -56,12 +59,16 @@ const getParameterFieldProps = (fieldSettings: FieldSettings) => { case "dropdown": return { type: "select", - options: fieldSettings.valueOptions ?? getSampleOptions(), + options: fieldSettings.valueOptions?.length + ? getOptionsFromArray(fieldSettings.valueOptions) + : getSampleOptions(), }; case "inline-select": return { type: "radio", - options: fieldSettings.valueOptions ?? getSampleOptions(), + options: fieldSettings.valueOptions?.length + ? getOptionsFromArray(fieldSettings.valueOptions) + : getSampleOptions(), }; default: return { type: "input" }; -- GitLab