Skip to content
This repository has been archived by the owner on Jun 5, 2024. It is now read-only.

Commit

Permalink
Controls (checkbox, radio, toggle) (#129)
Browse files Browse the repository at this point in the history
* move stateClass to src root

* impl basic checkbox using radix-ui/react-checkbox

* impl basic radio group using radix-ui/react-radio-group

- refactor css

* impl basic toggle using radix-ui/switch

* add more specs to storybook and make it spec compliant

* Add bindings

* version packages
  • Loading branch information
Jaeho Lee authored Feb 21, 2023
1 parent 0ed122c commit e1254d4
Show file tree
Hide file tree
Showing 17 changed files with 776 additions and 28 deletions.
6 changes: 6 additions & 0 deletions .changeset/happy-waves-arrive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@greenlabs/formula-components": patch
"@greenlabs/rescript-formula-components": patch
---

Controls (checkbox, radio, toggle)
18 changes: 18 additions & 0 deletions packages/components-rescript/__tests__/Formula_test.res
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,21 @@ let textTextTab = () => {
let testCommon = () => {
<div style={ReactDOM.Style.make(~color=Formula.Theme.themeColors["blue-100"], ())} />
}

let testControls = () => {
let ref = React.useRef(Js.Nullable.null)

<>
<Checkbox ref={ReactDOM.Ref.domRef(ref)} />
<RadioGroup defaultValue="a" ref={ReactDOM.Ref.domRef(ref)}>
<RadioItem value="a" />
<RadioItem value="b" />
<RadioItem value="c" />
</RadioGroup>
<form>
<Toggle
ref={ReactDOM.Ref.domRef(ref)} defaultChecked={true} disabled={true} label="disabled & on"
/>
</form>
</>
}
5 changes: 5 additions & 0 deletions packages/components-rescript/src/Formula.res
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ module Icon = Formula__Icon
module Divider = Formula__Divider
module Button = Formula__Button
module TextTab = Formula__TextTab
// Controls
module RadioItem = Formula__Controls.RadioItem
module RadioGroup = Formula__Controls.RadioGroup
module Toggle = Formula__Controls.Toggle
module Checkbox = Formula__Controls.Checkbox
105 changes: 105 additions & 0 deletions packages/components-rescript/src/Formula__Controls.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
module Checkbox = {
@module("@greenlabs/formula-components") @react.component
external make: (
~id: string=?,
~className: string=?,
~props: {..}=?,
~name: string=?,
~onChange: ReactEvent.Form.t => unit=?,
~onFocus: ReactEvent.Focus.t => unit=?,
~onBlur: ReactEvent.Focus.t => unit=?,
~onInput: ReactEvent.Form.t => unit=?,
~onKeyDown: ReactEvent.Keyboard.t => unit=?,
~onKeyUp: ReactEvent.Keyboard.t => unit=?,
~asChild: bool=?,
~defaultChecked: bool=?,
~checked: bool=?,
~onCheckedChange: bool => unit=?,
~disabled: bool=?,
~required: bool=?,
~value: [#on]=?, // "on"
~label: string=?,
~autoFocus: bool=?,
// TODO: data-state, data-disabled
~ref: ReactDOM.Ref.t=?,
) => React.element = "Checkbox"
}

module Toggle = {
@module("@greenlabs/formula-components") @react.component
external make: (
~id: string=?,
~className: string=?,
~props: {..}=?,
~name: string=?,
~onChange: ReactEvent.Form.t => unit=?,
~onFocus: ReactEvent.Focus.t => unit=?,
~onBlur: ReactEvent.Focus.t => unit=?,
~onInput: ReactEvent.Form.t => unit=?,
~onKeyDown: ReactEvent.Keyboard.t => unit=?,
~onKeyUp: ReactEvent.Keyboard.t => unit=?,
~asChild: bool=?,
~forceMount: bool=?,
~defaultChecked: bool=?,
~checked: bool=?,
~onCheckedChange: bool => unit=?,
~disabled: bool=?,
~required: bool=?,
~value: [#on]=?, // "on"
~label: string=?,
~autoFocus: bool=?,
// TODO: data-state, data-disabled
~ref: ReactDOM.Ref.t=?,
) => React.element = "Toggle"
}

module RadioGroup = {
@module("@greenlabs/formula-components") @react.component
external make: (
~id: string=?,
~className: string=?,
~props: {..}=?,
~name: string=?,
~onChange: ReactEvent.Form.t => unit=?,
~onFocus: ReactEvent.Focus.t => unit=?,
~onBlur: ReactEvent.Focus.t => unit=?,
~onInput: ReactEvent.Form.t => unit=?,
~onKeyDown: ReactEvent.Keyboard.t => unit=?,
~onKeyUp: ReactEvent.Keyboard.t => unit=?,
~asChild: bool=?,
~defaultValue: string=?,
~value: string=?,
~onValueChange: string => unit=?,
~disabled: bool=?,
~required: bool=?,
~orientation: [#horizontal | #vertical]=?,
~dir: [#ltr | #rtl]=?,
~loop: bool=?,
~children: React.element,
// TODO: data-state, data-disabled
~ref: ReactDOM.Ref.t=?,
) => React.element = "RadioGroup"
}

module RadioItem = {
@module("@greenlabs/formula-components") @react.component
external make: (
~id: string=?,
~className: string=?,
~props: {..}=?,
~onChange: ReactEvent.Form.t => unit=?,
~onFocus: ReactEvent.Focus.t => unit=?,
~onBlur: ReactEvent.Focus.t => unit=?,
~onInput: ReactEvent.Form.t => unit=?,
~onKeyDown: ReactEvent.Keyboard.t => unit=?,
~onKeyUp: ReactEvent.Keyboard.t => unit=?,
~asChild: bool=?,
~value: string=?,
~disabled: bool=?,
~required: bool=?,
~label: string=?,
~autoFocus: bool=?,
// TODO: data-disabled
~ref: ReactDOM.Ref.t=?,
) => React.element = "RadioItem"
}
3 changes: 3 additions & 0 deletions packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@
},
"dependencies": {
"@greenlabs/formula-design-token": "0.0.5-next.6",
"@radix-ui/react-checkbox": "^1.0.1",
"@radix-ui/react-dialog": "^1.0.2",
"@radix-ui/react-radio-group": "^1.1.1",
"@radix-ui/react-switch": "^1.0.1",
"@radix-ui/react-tabs": "^1.0.1",
"@vanilla-extract/css-utils": "^0.1.3",
"@vanilla-extract/dynamic": "^2.0.2",
Expand Down
58 changes: 58 additions & 0 deletions packages/components/src/Controls/Checkbox.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import * as React from "react"
import type { Meta } from "@storybook/react"
import type { ComponentStory } from "@storybook/react"

import { Checkbox } from "./Checkbox"
import { createDisabledArgs } from "../utils/storybook"
import { ThemeScope } from "../theme"

const Template: ComponentStory<typeof Checkbox> = (args) => {
return (
<ThemeScope>
<form style={{ margin: "20px 10px" }}>
<Checkbox {...args} />
</form>
<form style={{ margin: "20px 10px" }}>
<Checkbox label="disabled=true" disabled />
</form>
<form style={{ margin: "20px 10px" }}>
<Checkbox label="disabled=true, checked" disabled defaultChecked />
</form>
</ThemeScope>
)
}

const textControl = {
defaultValue: "",
control: { type: "text" },
}

const commonDisabled = createDisabledArgs([
"props",
"className",
"onChange",
"onFocus",
"name",
"onBlur",
"onKeyDown",
"onKeyUp",
"form",
"id",
"autoFocus",
])

const controls = {
label: textControl,
}

export const Overview = Template.bind({})
Overview.args = {}
Overview.argTypes = {
...commonDisabled,
...controls,
}

export default {
title: "Formula/Controls/Checkbox",
component: Checkbox,
} as Meta<typeof Checkbox>
66 changes: 66 additions & 0 deletions packages/components/src/Controls/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import * as React from "react"
import { Text } from "../Text/Text"
import * as IconComponents from "../Icon/generated"
import * as RadixUICheckbox from "@radix-ui/react-checkbox"
import type { CheckboxProps as RadixUICheckboxProps } from "@radix-ui/react-checkbox"
import { checkboxStyles } from "./styles.css"
import { stateClass } from "./common"

type RefType = React.Ref<HTMLButtonElement>

interface CheckboxProps extends RadixUICheckboxProps {
props?: {}
label?: string
}

export const Checkbox = React.forwardRef<HTMLButtonElement, CheckboxProps>(
(
{ id, className = "", props = {}, disabled, label, ...additionalProps },
forwardedRef
) => {
const inputRef: RefType = React.useRef(null)

React.useImperativeHandle(
forwardedRef,
() => inputRef.current as HTMLButtonElement
)

const innerId = React.useId()
const usedId = id ?? innerId

return (
<div
className={`${className} ${stateClass({
disabled,
})} ${checkboxStyles.container}`}
>
<RadixUICheckbox.Root
ref={inputRef}
className={checkboxStyles.button}
id={usedId}
disabled={disabled}
{...additionalProps}
{...props}
>
<span className={checkboxStyles.container}>
<IconComponents.CheckLineBold
className={checkboxStyles.icon}
size="sm"
/>
</span>
</RadixUICheckbox.Root>
{label ? (
<Text.Body
tag="label"
size="md"
props={{ htmlFor: usedId }}
className={checkboxStyles.label}
>
{label}
</Text.Body>
) : null}
</div>
)
}
)
Checkbox.displayName = "Checkbox"
71 changes: 71 additions & 0 deletions packages/components/src/Controls/Radio.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import * as React from "react"
import type { Meta } from "@storybook/react"
import type { ComponentStory } from "@storybook/react"

import { RadioItem, RadioGroup } from "./Radio"
import { createDisabledArgs } from "../utils/storybook"
import { ThemeScope } from "../theme"

const Template: ComponentStory<typeof RadioGroup> = (args) => {
return (
<ThemeScope>
<form style={{ margin: "20px 10px" }}>
<label>plain radio group</label>
<RadioGroup style={{ display: "flex", gap: "10px" }} {...args}>
<RadioItem value="a" label="a" />
<RadioItem value="b" label="b" />
<RadioItem value="c" label="c" />
</RadioGroup>
</form>
<form style={{ margin: "20px 10px" }}>
<label>default selected</label>
<RadioGroup style={{ display: "flex", gap: "10px" }} defaultValue="a">
<RadioItem value="a" label="a" />
<RadioItem value="b" label="b" />
</RadioGroup>
</form>
<form style={{ margin: "20px 10px" }}>
<label>disabled radio</label>
<RadioGroup style={{ display: "flex", gap: "10px" }} defaultValue="a">
<RadioItem value="a" label="a" disabled />
<RadioItem value="b" label="b" disabled />
</RadioGroup>
</form>
</ThemeScope>
)
}

const textControl = {
defaultValue: "",
control: { type: "text" },
}

const commonDisabled = createDisabledArgs([
"props",
"className",
"onChange",
"onFocus",
"name",
"onBlur",
"onKeyDown",
"onKeyUp",
"form",
"id",
"autoFocus",
])

const controls = {
label: textControl,
}

export const Overview = Template.bind({})
Overview.args = {}
Overview.argTypes = {
...commonDisabled,
...controls,
}

export default {
title: "Formula/Controls/Radio",
component: RadioItem,
} as Meta<typeof RadioItem>
Loading

0 comments on commit e1254d4

Please sign in to comment.