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

TextField 옵션 추가 및 TextTab 바인딩 추가 #89

Merged
merged 4 commits into from
Dec 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/three-coats-float.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@greenlabs/formula-components": patch
"@greenlabs/rescript-formula-components": patch
---

- feat: add TextField options (showHintOnFocusOnly, hideClearButton)
- feat: TextField options ReScript bindings
41 changes: 36 additions & 5 deletions packages/components-rescript/__tests__/Formula_test.res
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
open Formula

module TestTextContainer = {
@react.component
let make = (~className=?, ~children=?) => {
Expand All @@ -6,7 +8,6 @@ module TestTextContainer = {
}

let testText = () => {
open Formula
<>
<Text
variant=#body
Expand All @@ -31,7 +32,6 @@ let testText = () => {
}

let testIcon = () => {
open Formula
<>
<Icon.CalendarLineBold />
<Icon.CalendarLineRegular color=#"lightblue-90" />
Expand All @@ -43,15 +43,13 @@ let testIcon = () => {
}

let testDivider = () => {
open Formula
<>
<Divider />
<Divider className="some-className" variant=#large props={{"id": "id-of-divider"}} />
</>
}

let testButton = () => {
open Formula
<>
<Button.Container color=#primary size=#sm text="I'm ContainerButton" />
<Button.Container color=#"secondary-gray" size=#sm block=true text="I'm full width button" />
Expand All @@ -69,7 +67,6 @@ let testButton = () => {
}

let testTextField = () => {
open Formula
<>
<TextField />
<TextField _type=#password size=#large />
Expand All @@ -95,9 +92,43 @@ let testTextField = () => {
<input ?onChange id className ref={inputRef} />
}}
/>
<TextField
options={{
showHintOnFocusOnly: true,
}}
/>
</>
}

let textTextTab = () => {
<TextTab.List
rootProps={{"defaultValue": "a"}}
fullWidth={true}
onValueChange={_ => ()}
contents={<>
<TextTab.Content value="a"> {`this is a`->React.string} </TextTab.Content>
<TextTab.Content value="b"> {`this is b`->React.string} </TextTab.Content>
<TextTab.Content value="c"> {`this is c`->React.string} </TextTab.Content>
</>}>
<TextTab.Trigger title="텍스트 a" value="a" icon={Icon.ArrowDownLineBold.make} />
<TextTab.Trigger
title="텍스트 "
value="b"
badge={{
type_: #countSimple,
value: 9,
}}
/>
<TextTab.Trigger
value="c"
badge={{
type_: #simple,
}}>
<Icon.ArrowDownLineBold />
</TextTab.Trigger>
</TextTab.List>
}

let testCommon = () => {
<div style={ReactDOM.Style.make(~color=Formula.Theme.themeColors["blue-100"], ())} />
}
1 change: 1 addition & 0 deletions packages/components-rescript/src/Formula.res
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ module TextField = Formula__TextField
module Icon = Formula__Icon
module Divider = Formula__Divider
module Button = Formula__Button
module TextTab = Formula__TextTab
10 changes: 8 additions & 2 deletions packages/components-rescript/src/Formula__TextField.res
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ type textFieldComponentProps<'a> = {
disabled?: bool,
}

type options = {
showHintOnFocusOnly?: bool,
hideClearButton?: bool,
}

@module("@greenlabs/formula-components") @react.component
external make: (
~props: {..}=?,
Expand All @@ -24,9 +29,9 @@ external make: (
~_type: [#text | #password]=?,
~placeholder: string=?,
~prefix: React.element=?,
~prefixIcon: React.componentLike<{..}, React.element>=?,
~prefixIcon: React.componentLike<{..}, React.element>=?, // FIXME: Icon component type
~suffix: React.element=?,
~suffixIcon: React.componentLike<{..}, React.element>=?,
~suffixIcon: React.componentLike<{..}, React.element>=?, // FIXME: Icon component type
~titleText: string=?,
~hintText: string=?,
~state: [#normal | #error]=?,
Expand All @@ -35,4 +40,5 @@ external make: (
~onChange: ReactEvent.Form.t => unit=?,
~onFocus: ReactEvent.Focus.t => unit=?,
~ref: ReactDOM.Ref.t=?,
~options: options=?,
) => React.element = "TextField"
37 changes: 37 additions & 0 deletions packages/components-rescript/src/Formula__TextTab.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module List = {
@module("@greenlabs/formula-components") @react.component
external make: (
~props: {..}=?,
~rootProps: {..}=?, // TODO: RadixUI props
~contents: React.element=?,
~fullWidth: bool=?,
~onValueChange: string => unit=?,
~children: React.element,
~ref: ReactDOM.Ref.t=?,
) => React.element = "TextTab"
}

module Trigger = {
type badgeType = {
type_: [#count | #simple | #countSimple],
value?: int,
}

@module("@greenlabs/formula-components") @react.component
external make: (
~icon: React.componentLike<{..}, React.element>=?, // FIXME: Icon component type
~title: string=?,
~badge: badgeType=?,
~value: string,
~children: React.element=?,
) => React.element = "TextTab"
}

module Content = {
@module("@greenlabs/formula-components") @react.component
external make: (
~value: string,
~children: React.element=?,
~props: ReactDOM.domProps=?,
) => React.element = "TextTab"
}
119 changes: 67 additions & 52 deletions packages/components/src/Tab/TextTab/TextTab.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import type { PropsWithChildren, ReactNode } from "react"
import { useLayoutEffect, useRef, useState } from "react"
import {
forwardRef,
useLayoutEffect,
useRef,
useState,
useImperativeHandle,
} from "react"
import type { TabsProps } from "@radix-ui/react-tabs"
import { Root, TabsList } from "@radix-ui/react-tabs"
import {
Expand All @@ -25,61 +31,70 @@ interface ListProps {
onValueChange?: (value: string) => void
}
// todo: on resize
export const List = ({
children,
contents = null,
onValueChange,
fullWidth,
rootProps,
...props
}: PropsWithChildren<ListProps>) => {
const ref = useRef<HTMLDivElement>(null)
const [state, setState] = useState({ left: 0, width: 0 })
export const List = forwardRef<HTMLDivElement, PropsWithChildren<ListProps>>(
(
{ children, contents = null, onValueChange, fullWidth, rootProps },
forwardedRef
) => {
const ref = useRef<HTMLDivElement>(null)
const [state, setState] = useState({ left: 0, width: 0 })

useImperativeHandle(forwardedRef, () => ref.current as HTMLDivElement)

useLayoutEffect(() => {
const tabEl = ref.current?.querySelector("button")
if (tabEl) {
setState(extractIndicatorState(tabEl))
useLayoutEffect(() => {
const tabEl = ref.current?.querySelector("button")
if (tabEl) {
setState(extractIndicatorState(tabEl))
}
}, [])

const onValueChangeWrapped = (value: string) => {
onValueChange?.(value)
// indicator animation
const listEl = ref.current
const activeTabEl = listEl?.querySelector<TriggerHTMLType>(
'[data-state="active"]'
)
if (activeTabEl) {
setState(extractIndicatorState(activeTabEl))
}
}
}, [])

const onValueChangeWrapped = (value: string) => {
onValueChange?.(value)
// indicator animation
const listEl = ref.current
const activeTabEl = listEl?.querySelector<TriggerHTMLType>(
'[data-state="active"]'
return (
<Root
className={rootStyle}
onValueChange={onValueChangeWrapped}
{...rootProps}
>
<div className={listContainerStyle}>
<TabsList
className={`${listStyle} ${fullWidth ? classes.tabListFull : ""}`}
ref={ref}
>
{children}
</TabsList>
<div
className={indicatorStyle}
style={{
width: `${state.width}px`,
transform: `translate(${state.left}px, 1px)`,
}}
/>
</div>
{contents}
</Root>
)
if (activeTabEl) {
setState(extractIndicatorState(activeTabEl))
}
}

return (
<Root
className={rootStyle}
onValueChange={onValueChangeWrapped}
{...rootProps}
>
<div className={listContainerStyle}>
<TabsList
className={`${listStyle} ${fullWidth ? classes.tabListFull : ""}`}
ref={ref}
>
{children}
</TabsList>
<div
className={indicatorStyle}
style={{
width: `${state.width}px`,
transform: `translate(${state.left}px, 1px)`,
}}
/>
</div>
{contents}
</Root>
)
}
)

export { Trigger } from "./Trigger"
export { Content } from "@radix-ui/react-tabs"
import { Content as RadixTabsContent } from "@radix-ui/react-tabs"

export const Content = forwardRef<
HTMLDivElement,
{
value: string
children?: React.ReactNode
props?: React.RefAttributes<HTMLDivElement>
}
>(({ value, props }) => <RadixTabsContent value={value} {...props} />)
1 change: 0 additions & 1 deletion packages/components/src/Tab/index.tsx

This file was deleted.

18 changes: 15 additions & 3 deletions packages/components/src/TextField/TextField.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const commonDisabled = createDisabledArgs([
"suffixIcon",
"onChange",
"onFocus",
"options",
])

const iconControlMapping = Object.entries(IconComponents).reduce(
Expand Down Expand Up @@ -156,7 +157,9 @@ Line.args = {
}
Line.argTypes = BoxOutline.argTypes

export const Textarea_and_Ref: ComponentStory<typeof TextField> = (args) => {
export const Textarea_and_Ref_Etc: ComponentStory<typeof TextField> = (
args
) => {
const ref = React.useRef<HTMLInputElement>(null)

React.useLayoutEffect(() => {
Expand Down Expand Up @@ -205,14 +208,23 @@ export const Textarea_and_Ref: ComponentStory<typeof TextField> = (args) => {
titleText="using `react-textarea-autosize` as `inputContainer`"
inputContainer={inputContainer}
/>
<br />
<TextField
{...args}
titleText="options.showHintOnFocusOnly | options.hideClearButton"
options={{
showHintOnFocusOnly: true,
hideClearButton: true,
}}
/>
</form>
)}
/>
)
}

Textarea_and_Ref.args = Overview.args
Textarea_and_Ref.argTypes = {
Textarea_and_Ref_Etc.args = Overview.args
Textarea_and_Ref_Etc.argTypes = {
...commonDisabled,
...controls,
}
Expand Down
Loading