From 6f3c99e16678d50725fa0edcfb1f05c9c8cee5d9 Mon Sep 17 00:00:00 2001 From: Alexander Polyankin <alexander.polyankin@metabase.com> Date: Wed, 15 Mar 2023 18:19:32 +0200 Subject: [PATCH] Tweak boolean fields in basic action forms (#29165) --- .../FormCheckBox/FormCheckBox.stories.tsx | 34 +++++++++++++++ .../FormDateInput/FormDateInput.stories.tsx | 34 +++++++++++++++ .../components/FormField/FormField.styled.tsx | 24 +++++------ .../core/components/FormField/FormField.tsx | 17 ++++++-- .../FormFileInput/FormFileInput.stores.tsx | 34 +++++++++++++++ .../FormInput/FormInput.stories.tsx | 34 +++++++++++++++ .../FormNumericInput.stories.tsx | 34 +++++++++++++++ .../FormRadio/FormRadio.stories.tsx | 40 ++++++++++++++++++ .../FormSelect/FormSelect.stories.tsx | 42 +++++++++++++++++++ .../FormTextArea/FormTextArea.stories.tsx | 34 +++++++++++++++ .../FormToggle/FormToggle.stories.tsx | 34 +++++++++++++++ .../ActionSidebar/ActionSidebar.tsx | 2 +- 12 files changed, 345 insertions(+), 18 deletions(-) create mode 100644 frontend/src/metabase/core/components/FormCheckBox/FormCheckBox.stories.tsx create mode 100644 frontend/src/metabase/core/components/FormDateInput/FormDateInput.stories.tsx create mode 100644 frontend/src/metabase/core/components/FormFileInput/FormFileInput.stores.tsx create mode 100644 frontend/src/metabase/core/components/FormInput/FormInput.stories.tsx create mode 100644 frontend/src/metabase/core/components/FormNumericInput/FormNumericInput.stories.tsx create mode 100644 frontend/src/metabase/core/components/FormRadio/FormRadio.stories.tsx create mode 100644 frontend/src/metabase/core/components/FormSelect/FormSelect.stories.tsx create mode 100644 frontend/src/metabase/core/components/FormTextArea/FormTextArea.stories.tsx create mode 100644 frontend/src/metabase/core/components/FormToggle/FormToggle.stories.tsx diff --git a/frontend/src/metabase/core/components/FormCheckBox/FormCheckBox.stories.tsx b/frontend/src/metabase/core/components/FormCheckBox/FormCheckBox.stories.tsx new file mode 100644 index 00000000000..93dfcfb278e --- /dev/null +++ b/frontend/src/metabase/core/components/FormCheckBox/FormCheckBox.stories.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import type { ComponentStory } from "@storybook/react"; +import Form from "../Form"; +import FormProvider from "../FormProvider"; +import FormCheckBox from "./FormCheckBox"; + +export default { + title: "Core/FormCheckBox", + component: FormCheckBox, +}; + +const Template: ComponentStory<typeof FormCheckBox> = args => { + const initialValues = { value: false }; + const handleSubmit = () => undefined; + + return ( + <FormProvider initialValues={initialValues} onSubmit={handleSubmit}> + <Form> + <FormCheckBox {...args} name="value" /> + </Form> + </FormProvider> + ); +}; + +export const Default = Template.bind({}); +Default.args = { + title: "Title", +}; + +export const WithDescription = Template.bind({}); +WithDescription.args = { + title: "Title", + description: "Description", +}; diff --git a/frontend/src/metabase/core/components/FormDateInput/FormDateInput.stories.tsx b/frontend/src/metabase/core/components/FormDateInput/FormDateInput.stories.tsx new file mode 100644 index 00000000000..208fa1b0ece --- /dev/null +++ b/frontend/src/metabase/core/components/FormDateInput/FormDateInput.stories.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import type { ComponentStory } from "@storybook/react"; +import Form from "../Form"; +import FormProvider from "../FormProvider"; +import FormDateInput from "./FormDateInput"; + +export default { + title: "Core/FormDateInput", + component: FormDateInput, +}; + +const Template: ComponentStory<typeof FormDateInput> = args => { + const initialValues = { value: undefined }; + const handleSubmit = () => undefined; + + return ( + <FormProvider initialValues={initialValues} onSubmit={handleSubmit}> + <Form> + <FormDateInput {...args} name="value" /> + </Form> + </FormProvider> + ); +}; + +export const Default = Template.bind({}); +Default.args = { + title: "Title", +}; + +export const WithDescription = Template.bind({}); +WithDescription.args = { + title: "Title", + description: "Description", +}; diff --git a/frontend/src/metabase/core/components/FormField/FormField.styled.tsx b/frontend/src/metabase/core/components/FormField/FormField.styled.tsx index 3957153fba2..1e898c039fd 100644 --- a/frontend/src/metabase/core/components/FormField/FormField.styled.tsx +++ b/frontend/src/metabase/core/components/FormField/FormField.styled.tsx @@ -1,5 +1,4 @@ import styled from "@emotion/styled"; -import React from "react"; import { color } from "metabase/lib/colors"; import Icon from "metabase/components/Icon"; import { FieldAlignment, FieldOrientation } from "./types"; @@ -7,9 +6,12 @@ import { FieldAlignment, FieldOrientation } from "./types"; export interface FormCaptionProps { alignment: FieldAlignment; orientation: FieldOrientation; + hasDescription: boolean; } export const FieldCaption = styled.div<FormCaptionProps>` + align-self: ${props => + props.orientation !== "vertical" && !props.hasDescription ? "center" : ""}; margin-left: ${props => props.orientation === "horizontal" && props.alignment === "start" && @@ -38,10 +40,16 @@ export const OptionalTag = styled.span` margin-left: 0.25rem; `; -export const FieldLabelContainer = styled.div` +interface FieldLabelContainerProps { + orientation: FieldOrientation; + hasDescription: boolean; +} + +export const FieldLabelContainer = styled.div<FieldLabelContainerProps>` display: flex; align-items: center; - margin-bottom: 0.5em; + margin-bottom: ${props => + props.orientation === "vertical" || props.hasDescription ? "0.5em" : ""}; `; export const FieldLabelError = styled.span` @@ -94,13 +102,3 @@ export const FieldRoot = styled.div<FieldRootProps>` } } `; - -export const FieldLabelWithContainer = ({ - children, -}: { - children: React.ReactNode; -}) => ( - <FieldLabelContainer> - <FieldLabel hasError={false}>{children}</FieldLabel> - </FieldLabelContainer> -); diff --git a/frontend/src/metabase/core/components/FormField/FormField.tsx b/frontend/src/metabase/core/components/FormField/FormField.tsx index 8397867d014..3348eddb6cc 100644 --- a/frontend/src/metabase/core/components/FormField/FormField.tsx +++ b/frontend/src/metabase/core/components/FormField/FormField.tsx @@ -42,6 +42,8 @@ const FormField = forwardRef(function FormField( }: FormFieldProps, ref: Ref<HTMLDivElement>, ) { + const hasTitle = Boolean(title); + const hasDescription = Boolean(description); const hasError = Boolean(error); return ( @@ -52,10 +54,17 @@ const FormField = forwardRef(function FormField( orientation={orientation} > {alignment === "start" && children} - {(title || description) && ( - <FieldCaption alignment={alignment} orientation={orientation}> - <FieldLabelContainer> - {title && ( + {(hasTitle || hasDescription) && ( + <FieldCaption + alignment={alignment} + orientation={orientation} + hasDescription={hasDescription} + > + <FieldLabelContainer + orientation={orientation} + hasDescription={hasDescription} + > + {hasTitle && ( <FieldLabel hasError={hasError} htmlFor={htmlFor}> {title} {hasError && <FieldLabelError>: {error}</FieldLabelError>} diff --git a/frontend/src/metabase/core/components/FormFileInput/FormFileInput.stores.tsx b/frontend/src/metabase/core/components/FormFileInput/FormFileInput.stores.tsx new file mode 100644 index 00000000000..53c6fe0e220 --- /dev/null +++ b/frontend/src/metabase/core/components/FormFileInput/FormFileInput.stores.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import type { ComponentStory } from "@storybook/react"; +import Form from "../Form"; +import FormProvider from "../FormProvider"; +import FormFileInput from "./FormFileInput"; + +export default { + title: "Core/FormFileInput", + component: FormFileInput, +}; + +const Template: ComponentStory<typeof FormFileInput> = args => { + const initialValues = { value: undefined }; + const handleSubmit = () => undefined; + + return ( + <FormProvider initialValues={initialValues} onSubmit={handleSubmit}> + <Form> + <FormFileInput {...args} name="value" /> + </Form> + </FormProvider> + ); +}; + +export const Default = Template.bind({}); +Default.args = { + title: "Title", +}; + +export const WithDescription = Template.bind({}); +WithDescription.args = { + title: "Title", + description: "Description", +}; diff --git a/frontend/src/metabase/core/components/FormInput/FormInput.stories.tsx b/frontend/src/metabase/core/components/FormInput/FormInput.stories.tsx new file mode 100644 index 00000000000..506d9f51e5c --- /dev/null +++ b/frontend/src/metabase/core/components/FormInput/FormInput.stories.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import type { ComponentStory } from "@storybook/react"; +import Form from "../Form"; +import FormProvider from "../FormProvider"; +import FormInput from "./FormInput"; + +export default { + title: "Core/FormInput", + component: FormInput, +}; + +const Template: ComponentStory<typeof FormInput> = args => { + const initialValues = { value: false }; + const handleSubmit = () => undefined; + + return ( + <FormProvider initialValues={initialValues} onSubmit={handleSubmit}> + <Form> + <FormInput {...args} name="value" /> + </Form> + </FormProvider> + ); +}; + +export const Default = Template.bind({}); +Default.args = { + title: "Title", +}; + +export const WithDescription = Template.bind({}); +WithDescription.args = { + title: "Title", + description: "Description", +}; diff --git a/frontend/src/metabase/core/components/FormNumericInput/FormNumericInput.stories.tsx b/frontend/src/metabase/core/components/FormNumericInput/FormNumericInput.stories.tsx new file mode 100644 index 00000000000..c79447058f7 --- /dev/null +++ b/frontend/src/metabase/core/components/FormNumericInput/FormNumericInput.stories.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import type { ComponentStory } from "@storybook/react"; +import Form from "../Form"; +import FormProvider from "../FormProvider"; +import FormNumericInput from "./FormNumericInput"; + +export default { + title: "Core/FormNumericInput", + component: FormNumericInput, +}; + +const Template: ComponentStory<typeof FormNumericInput> = args => { + const initialValues = { value: undefined }; + const handleSubmit = () => undefined; + + return ( + <FormProvider initialValues={initialValues} onSubmit={handleSubmit}> + <Form> + <FormNumericInput {...args} name="value" /> + </Form> + </FormProvider> + ); +}; + +export const Default = Template.bind({}); +Default.args = { + title: "Title", +}; + +export const WithDescription = Template.bind({}); +WithDescription.args = { + title: "Title", + description: "Description", +}; diff --git a/frontend/src/metabase/core/components/FormRadio/FormRadio.stories.tsx b/frontend/src/metabase/core/components/FormRadio/FormRadio.stories.tsx new file mode 100644 index 00000000000..451959ed049 --- /dev/null +++ b/frontend/src/metabase/core/components/FormRadio/FormRadio.stories.tsx @@ -0,0 +1,40 @@ +import React from "react"; +import type { ComponentStory } from "@storybook/react"; +import Form from "../Form"; +import FormProvider from "../FormProvider"; +import FormRadio from "./FormRadio"; + +const TEST_OPTIONS = [ + { name: "Line", value: "line" }, + { name: "Area", value: "area" }, + { name: "Bar", value: "bar" }, +]; + +export default { + title: "Core/FormRadio", + component: FormRadio, +}; + +const Template: ComponentStory<typeof FormRadio> = args => { + const initialValues = { value: undefined }; + const handleSubmit = () => undefined; + + return ( + <FormProvider initialValues={initialValues} onSubmit={handleSubmit}> + <Form> + <FormRadio {...args} name="value" options={TEST_OPTIONS} /> + </Form> + </FormProvider> + ); +}; + +export const Default = Template.bind({}); +Default.args = { + title: "Title", +}; + +export const WithDescription = Template.bind({}); +WithDescription.args = { + title: "Title", + description: "Description", +}; diff --git a/frontend/src/metabase/core/components/FormSelect/FormSelect.stories.tsx b/frontend/src/metabase/core/components/FormSelect/FormSelect.stories.tsx new file mode 100644 index 00000000000..2206cb82f69 --- /dev/null +++ b/frontend/src/metabase/core/components/FormSelect/FormSelect.stories.tsx @@ -0,0 +1,42 @@ +import React from "react"; +import type { ComponentStory } from "@storybook/react"; +import Form from "../Form"; +import FormProvider from "../FormProvider"; +import FormSelect from "./FormSelect"; + +const TEST_OPTIONS = [ + { name: "Line", value: "line" }, + { name: "Area", value: "area" }, + { name: "Bar", value: "bar" }, +]; + +export default { + title: "Core/FormSelect", + component: FormSelect, +}; + +const Template: ComponentStory<typeof FormSelect> = args => { + const initialValues = { value: undefined }; + const handleSubmit = () => undefined; + + return ( + <FormProvider initialValues={initialValues} onSubmit={handleSubmit}> + <Form> + <FormSelect {...args} name="value" options={TEST_OPTIONS} /> + </Form> + </FormProvider> + ); +}; + +export const Default = Template.bind({}); +Default.args = { + title: "Title", + placeholder: "Use default", +}; + +export const WithDescription = Template.bind({}); +WithDescription.args = { + title: "Title", + placeholder: "Use default", + description: "Description", +}; diff --git a/frontend/src/metabase/core/components/FormTextArea/FormTextArea.stories.tsx b/frontend/src/metabase/core/components/FormTextArea/FormTextArea.stories.tsx new file mode 100644 index 00000000000..ffc2e26d35e --- /dev/null +++ b/frontend/src/metabase/core/components/FormTextArea/FormTextArea.stories.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import type { ComponentStory } from "@storybook/react"; +import Form from "../Form"; +import FormProvider from "../FormProvider"; +import FormTextArea from "./FormTextArea"; + +export default { + title: "Core/FormTextArea", + component: FormTextArea, +}; + +const Template: ComponentStory<typeof FormTextArea> = args => { + const initialValues = { value: false }; + const handleSubmit = () => undefined; + + return ( + <FormProvider initialValues={initialValues} onSubmit={handleSubmit}> + <Form> + <FormTextArea {...args} name="value" /> + </Form> + </FormProvider> + ); +}; + +export const Default = Template.bind({}); +Default.args = { + title: "Title", +}; + +export const WithDescription = Template.bind({}); +WithDescription.args = { + title: "Title", + description: "Description", +}; diff --git a/frontend/src/metabase/core/components/FormToggle/FormToggle.stories.tsx b/frontend/src/metabase/core/components/FormToggle/FormToggle.stories.tsx new file mode 100644 index 00000000000..32de935bf39 --- /dev/null +++ b/frontend/src/metabase/core/components/FormToggle/FormToggle.stories.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import type { ComponentStory } from "@storybook/react"; +import Form from "../Form"; +import FormProvider from "../FormProvider"; +import FormToggle from "./FormToggle"; + +export default { + title: "Core/FormToggle", + component: FormToggle, +}; + +const Template: ComponentStory<typeof FormToggle> = args => { + const initialValues = { value: false }; + const handleSubmit = () => undefined; + + return ( + <FormProvider initialValues={initialValues} onSubmit={handleSubmit}> + <Form> + <FormToggle {...args} name="value" /> + </Form> + </FormProvider> + ); +}; + +export const Default = Template.bind({}); +Default.args = { + title: "Title", +}; + +export const WithDescription = Template.bind({}); +WithDescription.args = { + title: "Title", + description: "Description", +}; diff --git a/frontend/src/metabase/dashboard/components/ActionSidebar/ActionSidebar.tsx b/frontend/src/metabase/dashboard/components/ActionSidebar/ActionSidebar.tsx index 7238071f079..4cbc72d4f9a 100644 --- a/frontend/src/metabase/dashboard/components/ActionSidebar/ActionSidebar.tsx +++ b/frontend/src/metabase/dashboard/components/ActionSidebar/ActionSidebar.tsx @@ -109,7 +109,7 @@ export function ActionSidebarFn({ /> </Form> </FormProvider> - <FieldLabelContainer> + <FieldLabelContainer orientation="vertical" hasDescription={false}> <FieldLabel hasError={false}>{t`Action`}</FieldLabel> </FieldLabelContainer> -- GitLab