Skip to content

Commit

Permalink
feat(Tabs): add ability to customize tab button headers (#1768)
Browse files Browse the repository at this point in the history
- use intelligent fallback as backwards-compatibility
- allow composing of custom tab contents
- add new snapshots and stories illustrating new behavior
- use a render prop to pass in title, active, and other future data
- creation of reusable utility type for render props
- updates to Tab props type
  • Loading branch information
booc0mtaco authored Sep 29, 2023
1 parent e044a85 commit f231ad4
Show file tree
Hide file tree
Showing 8 changed files with 674 additions and 13 deletions.
6 changes: 1 addition & 5 deletions src/components/Popover/Popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useState, createContext, useContext } from 'react';
import React from 'react';
import { createPortal } from 'react-dom';
import { usePopper } from 'react-popper';
import type { ExtractProps } from '../../util/utility-types';
import type { ExtractProps, RenderProps } from '../../util/utility-types';
import type {
PopoverOptions,
PopoverContext as PopoverContextType,
Expand Down Expand Up @@ -76,10 +76,6 @@ const PopoverGroup = (props: ExtractProps<typeof HeadlessPopover.Group>) => (
<HeadlessPopover.Group {...props} />
);

type RenderProps<RenderPropArgs> = {
children: React.ReactNode | ((args: RenderPropArgs) => React.ReactElement);
};

type PopoverButtonProps = {
as?: React.ElementType;
className?: string;
Expand Down
40 changes: 35 additions & 5 deletions src/components/Tab/Tab.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import type { ReactNode } from 'react';

import React from 'react';
import { withoutTypes } from 'react-children-by-type';

import type { RenderProps } from '../../util/utility-types';

export interface Props {
import { type TabContextArgs } from '../Tabs/Tabs';

export interface TabProps {
/**
* aria-labelledby attribute that associates a tab panel with its accompanying tab title text
* aria-labelledby attribute that associates a tab panel with its accompanying tab title
*/
'aria-labelledby'?: string;
/**
Expand All @@ -28,21 +34,24 @@ export interface Props {
title: string;
}

type TabButtonProps = RenderProps<TabContextArgs>;

/**
* `import {Tab} from "@chanzuckerberg/eds";`
*
* Individual tab within the Tabs component.
* - Use the `title` prop for text-only tab headers
* - For more custom tab headers use `<Tab.Button>` which uses a render prop with state information
*/
export const Tab = ({
children,
className,
id,
// Destructure `title` so it is not applied to the rendered element

title,
'aria-labelledby': ariaLabelledBy,
...other
}: Props) => {
}: TabProps) => {
return (
<div
aria-hidden={false}
Expand All @@ -52,7 +61,28 @@ export const Tab = ({
role="tabpanel"
{...other}
>
{children}
{withoutTypes(children, TabButton)}
</div>
);
};

/**
* This component is a stub, and exists to give a type to the custom Tab button.
* It cannot be used as a standalone sub-component. See <Tabs> for where we trigger the render prop.
*/
const TabButton = (props: TabButtonProps) => {
return <div />;
};

/**
* Allows for control of the Tab Title contents, for custom tab handling
*
* ```jsx
* <Tab.Button>
* {({active, title}) => (
* <SomeCustomComponent isActive={active} title={title} />
* )}
* </Tab.Button>
* ```
*/
Tab.Button = TabButton;
2 changes: 1 addition & 1 deletion src/components/Tab/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { Tab as default } from './Tab';
export type { Props as TabProps } from './Tab';
export type { TabProps } from './Tab';
Loading

0 comments on commit f231ad4

Please sign in to comment.