From d9aa16e2c689b3db275d812b175e4bbe94f22e86 Mon Sep 17 00:00:00 2001 From: Emmad Usmani <emmadusmani@berkeley.edu> Date: Fri, 6 Oct 2023 10:50:51 -0700 Subject: [PATCH] Mantine Switch (#34005) * wip * Add comma and fix spelling error * Added some padding and things * switch control styles * text styles * add storybook examples * Add guidance text * Add top padding for small switches * change default size from `sm` to `md` * fix missing default `size` prop * fix paddings * remove label padding if empty * use `getStylesRef` --------- Co-authored-by: Oisin Coveney <oisin@metabase.com> Co-authored-by: Maz Ameli <maz@metabase.com> --- .../inputs/Switch/Switch.stories.mdx | 118 +++++++++++++++++ .../inputs/Switch/Switch.styled.tsx | 120 ++++++++++++++++++ .../ui/components/inputs/Switch/index.ts | 3 + .../metabase/ui/components/inputs/index.ts | 1 + frontend/src/metabase/ui/theme.ts | 2 + 5 files changed, 244 insertions(+) create mode 100644 frontend/src/metabase/ui/components/inputs/Switch/Switch.stories.mdx create mode 100644 frontend/src/metabase/ui/components/inputs/Switch/Switch.styled.tsx create mode 100644 frontend/src/metabase/ui/components/inputs/Switch/index.ts diff --git a/frontend/src/metabase/ui/components/inputs/Switch/Switch.stories.mdx b/frontend/src/metabase/ui/components/inputs/Switch/Switch.stories.mdx new file mode 100644 index 00000000000..b8a3122cc47 --- /dev/null +++ b/frontend/src/metabase/ui/components/inputs/Switch/Switch.stories.mdx @@ -0,0 +1,118 @@ +import { Canvas, Story, Meta } from "@storybook/addon-docs"; + +import { Switch, Stack } from "metabase/ui"; + +export const args = { + labelPosition: "right", + label: "Eat all the cheese", + description: undefined, + size: "md", + disabled: false, +}; + +export const argTypes = { + labelPosition: { + control: { type: "inline-radio" }, + options: ["left", "right"], + }, + label: { + control: { type: "text" }, + }, + description: { + control: { type: "text" }, + }, + size: { + control: { type: "inline-radio" }, + options: ["xs", "sm", "md"], + }, + disabled: { + control: { type: "boolean" }, + }, +}; + +<Meta + title="Inputs/Switch" + component={Switch} + args={args} + argTypes={argTypes} +/> + +# Switch + +Our themed wrapper around [Mantine Switch](https://mantine.dev/core/switch/). + +## When to use Switch + +Use Switch when you have a setting that can be only either on or off, or enabled/disabled. If there are multiple related options in a list that could be selected or chosen, use a checkbox group instead. + +## Docs + +- [Figma File](https://www.figma.com/file/xdNKMROC99J6Z4Sqg6V3JI/Input-%2F-Switch?type=design&node-id=1-96&mode=design&t=0K7GSP6rqj8M2muz-0) +- [Mantine Checkbox Docs](https://mantine.dev/core/switch/) + +## Usage guidelines + +In most situations you should use the `md` size variant, with the label on the left and the toggle on the right. (This allows users to read what the setting or option is first.) + +## Examples + +export const DefaultTemplate = args => <Switch {...args} />; + +export const StateTemplate = args => ( + <Stack> + <Switch {...args} label="Unchecked switch" checked={false} /> + <Switch {...args} label="Checked switch" checked /> + <Switch {...args} label="Disabled unchecked switch" disabled /> + <Switch {...args} label="Disabled checked switch" disabled checked /> + </Stack> +); + +export const Default = DefaultTemplate.bind({}); + +<Canvas> + <Story name="Default">{Default}</Story> +</Canvas> + +### Label + +export const Label = StateTemplate.bind({}); + +<Canvas> + <Story name="Label">{Label}</Story> +</Canvas> + +### Left label position + +export const LabelLeft = StateTemplate.bind({}); +LabelLeft.args = { + labelPosition: "left", +}; + +<Canvas> + <Story name="Label, left position" args={LabelLeft.args}> + {LabelLeft} + </Story> +</Canvas> + +### Description + +export const Description = StateTemplate.bind({}); +Description.args = { + description: "Every type of cheese will be consumed, regardless of stink.", +}; + +<Canvas> + <Story name="Description">{Description}</Story> +</Canvas> + +### Left description position + +export const DescriptionLeft = StateTemplate.bind({}); +DescriptionLeft.args = { + labelPosition: "left", + description: "Every type of cheese will be consumed, regardless of stink.", +}; + +<Canvas> + <Story name="Description, left position">{DescriptionLeft}</Story> +</Canvas> diff --git a/frontend/src/metabase/ui/components/inputs/Switch/Switch.styled.tsx b/frontend/src/metabase/ui/components/inputs/Switch/Switch.styled.tsx new file mode 100644 index 00000000000..dedf2f604b9 --- /dev/null +++ b/frontend/src/metabase/ui/components/inputs/Switch/Switch.styled.tsx @@ -0,0 +1,120 @@ +import type { MantineThemeOverride, SwitchStylesParams } from "@mantine/core"; +import { rem, getSize, getStylesRef } from "@mantine/core"; + +import { color } from "metabase/lib/colors"; + +const LABEL_FONT_SIZES = { + xs: rem(12), + sm: rem(14), + md: rem(16), +}; + +const LABEL_LINE_HEIGHT = { + xs: rem(16), + sm: rem(24), + md: rem(24), +}; + +const SWITCH_PADDING = { + xs: rem(8), + sm: rem(8), + md: rem(16), +}; + +const TRACK_HEIGHTS = { + xs: rem(16), + sm: rem(20), + md: rem(24), +}; + +const TRACK_WIDTHS = { + xs: rem(32), + sm: rem(40), + md: rem(48), + lg: rem(64), +}; + +const THUMB_SIZES = { + xs: rem(12), + sm: rem(14), + md: rem(18), +}; + +const TRACK_PADDING_TOP = { + xs: rem(0), + sm: rem(2), + md: rem(0), +}; + +export const getSwitchOverrides = (): MantineThemeOverride["components"] => ({ + Switch: { + defaultProps: { + color: "brand", + size: "md", + }, + styles: ( + theme, + { error, labelPosition }: SwitchStylesParams, + { size = "md" }, + ) => { + return { + labelWrapper: { + [labelPosition === "left" ? "paddingRight" : "paddingLeft"]: getSize({ + size, + sizes: SWITCH_PADDING, + }), + "&:empty": { + padding: 0, + }, + }, + label: { + padding: 0, + fontWeight: 700, + fontSize: getSize({ size, sizes: LABEL_FONT_SIZES }), + lineHeight: getSize({ size, sizes: LABEL_LINE_HEIGHT }), + color: theme.colors.text[2], + cursor: "pointer", + "&[data-disabled]": { + color: theme.colors.text[0], + cursor: "default", + }, + }, + description: { + padding: 0, + marginTop: rem(8), + fontSize: rem(12), + color: theme.colors.text[1], + }, + error: { + padding: 0, + marginTop: rem(8), + fontSize: rem(12), + color: theme.colors.error[0], + }, + track: { + backgroundColor: theme.colors.bg[1], + border: error ? `1px solid ${color("accent3")}` : "none", + boxSizing: "border-box", + borderRadius: rem(24), + height: getSize({ size, sizes: TRACK_HEIGHTS }), + width: getSize({ size, sizes: TRACK_WIDTHS }), + cursor: "pointer", + [`${getStylesRef("input")}:disabled + &`]: { + backgroundColor: theme.colors.bg[1], + }, + marginTop: getSize({ size, sizes: TRACK_PADDING_TOP }), + }, + thumb: { + backgroundColor: theme.white, + border: "none", + borderRadius: rem(22), + height: getSize({ size, sizes: THUMB_SIZES }), + width: getSize({ size, sizes: THUMB_SIZES }), + [`${getStylesRef("input")}:disabled + * > &`]: { + backgroundColor: theme.colors.bg[0], + }, + }, + }; + }, + }, +}); diff --git a/frontend/src/metabase/ui/components/inputs/Switch/index.ts b/frontend/src/metabase/ui/components/inputs/Switch/index.ts new file mode 100644 index 00000000000..2e040ba4f9d --- /dev/null +++ b/frontend/src/metabase/ui/components/inputs/Switch/index.ts @@ -0,0 +1,3 @@ +export { Switch } from "@mantine/core"; +export type { SwitchProps, SwitchGroupProps } from "@mantine/core"; +export { getSwitchOverrides } from "./Switch.styled"; diff --git a/frontend/src/metabase/ui/components/inputs/index.ts b/frontend/src/metabase/ui/components/inputs/index.ts index b9e0952b33d..c277fe05c32 100644 --- a/frontend/src/metabase/ui/components/inputs/index.ts +++ b/frontend/src/metabase/ui/components/inputs/index.ts @@ -5,4 +5,5 @@ export * from "./NumberInput"; export * from "./Radio"; export * from "./Select"; export * from "./Textarea"; +export * from "./Switch"; export * from "./TextInput"; diff --git a/frontend/src/metabase/ui/theme.ts b/frontend/src/metabase/ui/theme.ts index 790e56d7917..31463cbed36 100644 --- a/frontend/src/metabase/ui/theme.ts +++ b/frontend/src/metabase/ui/theme.ts @@ -15,6 +15,7 @@ import { getPaperOverrides, getSelectOverrides, getTextareaOverrides, + getSwitchOverrides, getTextInputOverrides, getTextOverrides, getTitleOverrides, @@ -115,6 +116,7 @@ export const getThemeOverrides = (): MantineThemeOverride => ({ ...getPaperOverrides(), ...getSelectOverrides(), ...getTextareaOverrides(), + ...getSwitchOverrides(), ...getTextInputOverrides(), ...getTextOverrides(), ...getTitleOverrides(), -- GitLab