Skip to content

Commit

Permalink
feat: allow wrapping tab panel (#6541)
Browse files Browse the repository at this point in the history
* fix: allow wrapping tab panels

* chore: add types
  • Loading branch information
segunadebayo committed Aug 23, 2022
1 parent 54fbfe6 commit 35b4167
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changeset/small-bugs-prove.md
@@ -0,0 +1,5 @@
---
"@chakra-ui/tabs": minor
---

Allow wrapping tab panels
35 changes: 24 additions & 11 deletions packages/tabs/src/use-tabs.ts
Expand Up @@ -7,13 +7,13 @@ import { getValidChildren } from "@chakra-ui/react-children-utils"
import { mergeRefs } from "@chakra-ui/react-use-merge-refs"
import { lazyDisclosure, LazyMode } from "@chakra-ui/lazy-utils"
import { callAllHandlers } from "@chakra-ui/shared-utils"
import React, {
cloneElement,
import {
useCallback,
useEffect,
useRef,
useState,
useId,
createElement,
} from "react"

/* -------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -177,8 +177,6 @@ export const [TabsProvider, useTabsContext] = createContext<UseTabsReturn>({
"useTabsContext: `context` is undefined. Seems you forgot to wrap all tabs components within <Tabs />",
})

type Child = React.ReactElement<any>

export interface UseTabListProps {
children?: React.ReactNode
onKeyDown?: React.KeyboardEventHandler
Expand Down Expand Up @@ -323,6 +321,13 @@ export interface UseTabPanelsProps {
children?: React.ReactNode
}

const [TabPanelProvider, useTabPanelContext] = createContext<{
isSelected: boolean
id: string
tabId: string
selectedIndex: number
}>({})

/**
* Tabs hook for managing the visibility of multiple tab panels.
*
Expand All @@ -340,12 +345,18 @@ export function useTabPanels<P extends UseTabPanelsProps>(props: P) {
const validChildren = getValidChildren(props.children)

const children = validChildren.map((child, index) =>
cloneElement(child as Child, {
isSelected: index === selectedIndex,
id: makeTabPanelId(id, index),
// Refers to the associated tab element, and also provides an accessible name to the tab panel.
"aria-labelledby": makeTabId(id, index),
}),
createElement(
TabPanelProvider,
{
value: {
isSelected: index === selectedIndex,
id: makeTabPanelId(id, index),
tabId: makeTabId(id, index),
selectedIndex,
},
},
child,
),
)

return { ...props, children }
Expand All @@ -358,8 +369,9 @@ export function useTabPanels<P extends UseTabPanelsProps>(props: P) {
* @param props props object for the tab panel
*/
export function useTabPanel(props: Record<string, any>) {
const { isSelected, id, children, ...htmlProps } = props
const { children, ...htmlProps } = props
const { isLazy, lazyBehavior } = useTabsContext()
const { isSelected, id, tabId } = useTabPanelContext()

const hasBeenSelected = useRef(false)
if (isSelected) {
Expand All @@ -379,6 +391,7 @@ export function useTabPanel(props: Record<string, any>) {
...htmlProps,
children: shouldRenderChildren ? children : null,
role: "tabpanel",
"aria-labelledby": tabId,
hidden: !isSelected,
id,
}
Expand Down
21 changes: 21 additions & 0 deletions packages/tabs/stories/tabs.stories.tsx
Expand Up @@ -280,3 +280,24 @@ export const withinDrawer = () => (
</DrawerOverlay>
</Drawer>
)

export const WithTabPanelWrapper = () => (
<Tabs>
<TabList>
<Tab>FIrst Tab</Tab>
<Tab>Second Tab</Tab>
<Tab>Third Tab</Tab>
</TabList>
<TabPanels>
<div>
<TabPanel>Tab panel 1</TabPanel>
</div>
<div>
<TabPanel>Tab panel 2</TabPanel>
</div>
<div>
<TabPanel>Tab panel 3</TabPanel>
</div>
</TabPanels>
</Tabs>
)

1 comment on commit 35b4167

@vercel
Copy link

@vercel vercel bot commented on 35b4167 Aug 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.