Skip to content

Commit

Permalink
feat: #9990 add controlled option to tabs (#10017)
Browse files Browse the repository at this point in the history
* feat: #9990 add controlled option to tabs

* feat: #9990 bump beta
  • Loading branch information
willmcvay authored Oct 24, 2023
1 parent eaf9620 commit 3e19498
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 6 deletions.
2 changes: 1 addition & 1 deletion packages/elements/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@reapit/elements",
"version": "4.0.0-beta.5",
"version": "4.0.0-beta.6",
"description": "A collection of React components and utilities for building apps for Reapit Marketplace",
"homepage": "https://github.com/reapit/foundations#readme",
"bugs": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ exports[`Tabs should match a snapshot and render children 1`] = `
>
<mock-styled.div>
<mock-styled.input
checked="true"
id="option-1"
name="my-cool-toggle-radio"
type="radio"
Expand Down Expand Up @@ -41,6 +42,7 @@ exports[`Tabs should match a snapshot and render children 1`] = `
</span>
</mock-styled.label>
<mock-styled.input
checked="false"
id="option-2"
name="my-cool-toggle-radio"
type="radio"
Expand Down Expand Up @@ -69,6 +71,7 @@ exports[`Tabs should match a snapshot and render children 1`] = `
</span>
</mock-styled.label>
<mock-styled.input
checked="false"
id="option-3"
name="my-cool-toggle-radio"
type="radio"
Expand Down Expand Up @@ -115,6 +118,7 @@ exports[`Tabs should match a snapshot and render children 1`] = `
>
<mock-styled.div>
<mock-styled.input
checked="true"
id="option-1"
name="my-cool-toggle-radio"
type="radio"
Expand Down Expand Up @@ -143,6 +147,7 @@ exports[`Tabs should match a snapshot and render children 1`] = `
</span>
</mock-styled.label>
<mock-styled.input
checked="false"
id="option-2"
name="my-cool-toggle-radio"
type="radio"
Expand Down Expand Up @@ -171,6 +176,7 @@ exports[`Tabs should match a snapshot and render children 1`] = `
</span>
</mock-styled.label>
<mock-styled.input
checked="false"
id="option-3"
name="my-cool-toggle-radio"
type="radio"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ describe('Tabs', () => {
<Tabs
name="my-cool-toggle-radio"
isFullWidth
isControlled
options={[
{
id: 'option-1',
Expand Down
78 changes: 75 additions & 3 deletions packages/elements/src/components/tabs/tabs.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Tabs } from './index'
import { RenderHtmlMarkup } from '../../storybook/render-html-markup'
import { useState } from 'react'
import { Subtitle } from '../typography'
import { InputGroup } from '../input-group'

<Meta title="Tabs" component={Tabs} />

Expand Down Expand Up @@ -96,12 +97,14 @@ Slight variation on the above where the Tabs spans the full width of it's contai

<RenderHtmlMarkup component="Tabs" story="Tabs - Full Width" />

## Tabs - React Example with useState
## Tabs - React Example uncontrolled

Simple example demonstrating how to transition content using tabs. In practice, you may want to dispatch to a router or similar.

The underlying radio buttons are uncontrolled by default so the `isChecked` is used to set the `defaultChecked` in this example. This is to allow the radio to behave nativele i.e. it will update the state using the `onChange` event but not vice versa.

<Canvas>
<Story name="Tabs - React Example with useState">
<Story name="Tabs - React Example uncontrolled">
{() => {
const [tab, setTab] = useState('1')
return (
Expand Down Expand Up @@ -134,10 +137,79 @@ Simple example demonstrating how to transition content using tabs. In practice,
{tab === '1' && <Subtitle hasGreyText>Tab 1 Content</Subtitle>}
{tab === '2' && <Subtitle hasGreyText>Tab 2 Content</Subtitle>}
{tab === '3' && <Subtitle hasGreyText>Tab 3 Content</Subtitle>}
<InputGroup
value={tab}
label="The value of this input will change with the state, but updating the state of the input will not change the tab."
type="number"
min="1"
max="3"
onChange={(e) => {
setTab(e.target.value)
}}
/>
</>
)
}}
</Story>
</Canvas>

<RenderHtmlMarkup component="Tabs" story="Tabs - React Example uncontrolled" />

## Tabs - React Example controlled

Simple example demonstrating how to transition content using tabs. In practice, you may want to dispatch to a router or similar.

The underlying radio buttons are uncontrolled by default so the `isChecked` is used to set the `defaultChecked` in this example. This is to allow the radio to behave nativele i.e. it will update the state using the `onChange` event but not vice versa.

<Canvas>
<Story name="Tabs - React Example controlled">
{() => {
const [tab, setTab] = useState('1')
return (
<>
<Tabs
name="my-cool-tabs-react-controlled"
isFullWidth
isControlled
onChange={(e) => setTab(e.target.value)}
options={[
{
id: 'tab-1-react-controlled',
value: 1,
text: 'Tab Content 1',
isChecked: tab === '1',
},
{
id: 'tab-2-react-controlled',
value: 2,
text: 'Tab Content 2',
isChecked: tab === '2',
},
{
id: 'tab-3-react-controlled',
value: 3,
text: 'Tab Content 3',
isChecked: tab === '3',
},
]}
/>
{tab === '1' && <Subtitle hasGreyText>Tab 1 Content</Subtitle>}
{tab === '2' && <Subtitle hasGreyText>Tab 2 Content</Subtitle>}
{tab === '3' && <Subtitle hasGreyText>Tab 3 Content</Subtitle>}
<InputGroup
value={tab}
label="The value of this input will change with the state and updating the input will change the tab"
type="number"
min="1"
max="3"
onChange={(e) => {
setTab(e.target.value)
}}
/>
</>
)
}}
</Story>
</Canvas>

<RenderHtmlMarkup component="Tabs" story="Tabs - React Example with useState" />
<RenderHtmlMarkup component="Tabs" story="Tabs - React Example controlled" />
13 changes: 11 additions & 2 deletions packages/elements/src/components/tabs/tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,26 @@ export interface TabsOption {
export interface TabsProps extends HTMLAttributes<HTMLInputElement> {
options: TabsOption[]
name: string
isControlled?: boolean
isFullWidth?: boolean
hasNoBorder?: boolean
}

export const Tabs: FC<TabsProps> = ({ className, isFullWidth, hasNoBorder, name, options, ...rest }) => {
export const Tabs: FC<TabsProps> = ({ className, isFullWidth, hasNoBorder, isControlled, name, options, ...rest }) => {
return (
<ElTabsWrap className={cx(className, isFullWidth && elTabsFullWidth)}>
<ElTabsOptionsWrap>
{options.map(({ id, value, text, isChecked }) => (
<Fragment key={id}>
<ElTabs id={id} name={name} value={value} type="radio" {...rest} defaultChecked={isChecked} />
<ElTabs
id={id}
name={name}
value={value}
type="radio"
{...rest}
checked={isControlled ? isChecked : undefined}
defaultChecked={isControlled ? undefined : isChecked}
/>
<ElTabsLabel htmlFor={id}>
<span className={elTabsItem}>{text}</span>
</ElTabsLabel>
Expand Down

0 comments on commit 3e19498

Please sign in to comment.