diff --git a/docs-ui/src/content/toggle-button-group.props.ts b/docs-ui/src/content/toggle-button-group.props.ts
index d1c9c915dc..b8f12941de 100644
--- a/docs-ui/src/content/toggle-button-group.props.ts
+++ b/docs-ui/src/content/toggle-button-group.props.ts
@@ -30,6 +30,7 @@ export const toggleButtonGroupUsageSnippet = `import { ToggleButtonGroup, Toggle
Dogs
Cats
+ Birds
`;
export const toggleButtonGroupSingleSnippet = `
@@ -45,9 +46,9 @@ export const toggleButtonGroupMultipleSnippet = ``;
export const toggleButtonGroupVerticalSnippet = `
- Low
- Medium
- High
+ Morning
+ Afternoon
+ Evening
`;
export const toggleButtonGroupDisabledSnippet = `
@@ -62,16 +63,29 @@ export const toggleButtonGroupDisallowEmptySnippet = `Three
`;
-export const toggleButtonGroupIconsSnippet = `
- Cloud
- Starred
- Next
+export const toggleButtonGroupIconsSnippet = `import { RiCloudLine, RiStarFill, RiStarLine, RiArrowRightSLine } from '@remixicon/react';
+
+
+ } />
+ }
+ />
+ }>
+ Star
+
+ }>
+ Next
+
`;
-export const toggleButtonGroupIconsOnlySnippet = `
-
-
-
+export const toggleButtonGroupIconsOnlySnippet = `import { RiCloudLine, RiStarLine, RiArrowRightSLine } from '@remixicon/react';
+
+
+ } />
+ } />
+ } />
`;
export const toggleButtonGroupSurfacesSnippet = `
diff --git a/docs-ui/src/content/toggle-button.mdx b/docs-ui/src/content/toggle-button.mdx
index 593d97b02a..78750ec365 100644
--- a/docs-ui/src/content/toggle-button.mdx
+++ b/docs-ui/src/content/toggle-button.mdx
@@ -10,6 +10,7 @@ import {
toggleButtonIconsSnippet,
toggleButtonDisabledSnippet,
toggleButtonControlledSnippet,
+ toggleButtonFunctionChildrenSnippet,
} from './toggle-button.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
@@ -88,6 +89,18 @@ import { ToggleButtonDefinition } from '../utils/definitions';
code={toggleButtonControlledSnippet}
/>
+### Dynamic Content with Function Children
+
+The `children` prop can be a function that receives render props, allowing you to dynamically customize the button content based on component state (such as `isSelected`, `isDisabled`, `isHovered`, etc.).
+
+}
+ code={toggleButtonFunctionChildrenSnippet}
+/>
+
diff --git a/docs-ui/src/content/toggle-button.props.ts b/docs-ui/src/content/toggle-button.props.ts
index 9df1a24791..9115b29603 100644
--- a/docs-ui/src/content/toggle-button.props.ts
+++ b/docs-ui/src/content/toggle-button.props.ts
@@ -23,7 +23,12 @@ export const toggleButtonPropDefs: Record = {
defaultSelected: { type: 'boolean' },
onChange: { type: 'enum', values: ['(isSelected: boolean) => void'] },
isDisabled: { type: 'boolean', default: 'false' },
- children: { type: 'enum', values: ['ReactNode'] },
+ children: {
+ type: 'enum',
+ values: ['ReactNode', '(values: ToggleButtonRenderProps) => ReactNode'],
+ description:
+ 'The children of the component. A function may be provided to alter the children based on component state (such as `isSelected`, `isDisabled`, `isHovered`, etc.).',
+ },
...classNamePropDefs,
...stylePropDefs,
};
@@ -70,9 +75,12 @@ export const toggleButtonSizesSnippet = `
Medium
`;
-export const toggleButtonIconsSnippet = `
- Favorite
- Confirm
+export const toggleButtonIconsSnippet = `import { RiStarLine, RiStarFill, RiCheckLine } from '@remixicon/react';
+
+
+ }>Favorite
+ } defaultSelected>Starred
+ }>Confirm
`;
export const toggleButtonDisabledSnippet = `
@@ -80,8 +88,108 @@ export const toggleButtonDisabledSnippet = `
Selected
`;
-export const toggleButtonControlledSnippet = `const [selected, setSelected] = useState(false);
+export const toggleButtonControlledSnippet = `import { useState } from 'react';
+import { RiStarFill, RiStarLine } from '@remixicon/react';
-
- {selected ? 'On' : 'Off'}
+const [selected, setSelected] = useState(false);
+
+ : }
+>
+ {selected ? 'Starred' : 'Not starred'}
`;
+
+export const toggleButtonFunctionChildrenSnippet = `import { RiStarFill, RiStarLine } from '@remixicon/react';
+
+
+ Example 1: Selection State
+
+
+ {({ isSelected }) => (isSelected ? '✓ Selected' : 'Not Selected')}
+
+
+ {({ isSelected }) => (isSelected ? '✓ Selected' : 'Not Selected')}
+
+
+
+
+
+ Example 2: Multiple States
+
+
+ {({ isSelected, isHovered }) => {
+ const states = [];
+ if (isSelected) states.push('on');
+ else states.push('off');
+ if (isHovered) states.push('hovered');
+ return \`Email (\${states.join(', ')})\`;
+ }}
+
+
+ {({ isSelected, isHovered }) => {
+ const states = [];
+ if (isSelected) states.push('on');
+ else states.push('off');
+ if (isHovered) states.push('hovered');
+ return \`Push (\${states.join(', ')})\`;
+ }}
+
+
+
+
+
+ Example 3: Conditional Icons
+
+
+ {({ isSelected }) => (
+ <>
+ {isSelected ? : }
+ {isSelected ? 'Starred' : 'Star'}
+ >
+ )}
+
+
+
+
+
+ Example 4: Status Indicators
+
+
+ {({ isSelected }) => (
+
+
+ Active
+
+ )}
+
+
+ {({ isSelected }) => (
+
+
+ Inactive
+
+ )}
+
+
+
+`;
diff --git a/packages/ui/report.api.md b/packages/ui/report.api.md
index afd08072ee..b7b6604421 100644
--- a/packages/ui/report.api.md
+++ b/packages/ui/report.api.md
@@ -1856,8 +1856,6 @@ export interface ToggleButtonGroupProps
// @public
export interface ToggleButtonProps extends ToggleButtonProps_2 {
- // (undocumented)
- children?: ReactNode;
// (undocumented)
iconEnd?: ReactElement;
// (undocumented)
diff --git a/packages/ui/src/components/ToggleButton/ToggleButton.stories.tsx b/packages/ui/src/components/ToggleButton/ToggleButton.stories.tsx
index f4be5cb46b..14183e6ede 100644
--- a/packages/ui/src/components/ToggleButton/ToggleButton.stories.tsx
+++ b/packages/ui/src/components/ToggleButton/ToggleButton.stories.tsx
@@ -16,13 +16,11 @@
import preview from '../../../../../.storybook/preview';
import { ToggleButton } from './ToggleButton';
-import { ToggleButtonGroup } from '../ToggleButtonGroup/ToggleButtonGroup';
import { Flex } from '../Flex';
import { Text } from '../Text';
import { useState } from 'react';
import {
RiCheckLine,
- RiHeartLine,
RiStarFill,
RiStarLine,
RiCloudLine,
@@ -150,20 +148,6 @@ export const IconsAndText = meta.story({
),
});
-export const MixedIcons = meta.story({
- render: () => (
-
- }>
- Cloud
-
- } />
- }>
- Next
-
-
- ),
-});
-
export const Disabled = meta.story({
render: () => (
@@ -194,52 +178,128 @@ export const Controlled = meta.story({
},
});
-export const GroupSingle = meta.story({
- render: () => (
-
- Apples
- Oranges
- Bananas
-
- ),
+export const FunctionChildren = meta.story({
+ render: () => {
+ const [disabled, setDisabled] = useState(false);
+ return (
+
+
+
+ {({ isDisabled, isSelected }) =>
+ isDisabled
+ ? `Disabled ${isSelected ? '(Selected)' : '(Unselected)'}`
+ : `Enabled ${isSelected ? '(Selected)' : '(Unselected)'}`
+ }
+
+ setDisabled(!disabled)}
+ >
+ {disabled ? 'Enable' : 'Disable'}
+
+
+
+ Toggle the button to change the disabled state and see text update
+
+
+ );
+ },
});
-export const GroupMultiple = meta.story({
- render: () => (
-
- Dogs
- Cats
- Rabbits
-
- ),
-});
+export const DynamicContent = meta.story({
+ render: () => {
+ return (
+
+
+ Example 1: Selection State
+
+
+ {({ isSelected }) => (isSelected ? '✓ Selected' : 'Not Selected')}
+
+
+ {({ isSelected }) => (isSelected ? '✓ Selected' : 'Not Selected')}
+
+
+
-export const GroupWithIcons = meta.story({
- render: () => (
-
- }>
- Chill
-
- }>
- Focus
-
- }>
- Party
-
-
- ),
-});
+
+ Example 2: Multiple States
+
+
+ {({ isSelected, isHovered }) => {
+ const states = [];
+ if (isSelected) states.push('on');
+ else states.push('off');
+ if (isHovered) states.push('hovered');
+ return `Email (${states.join(', ')})`;
+ }}
+
+
+ {({ isSelected, isHovered }) => {
+ const states = [];
+ if (isSelected) states.push('on');
+ else states.push('off');
+ if (isHovered) states.push('hovered');
+ return `Push (${states.join(', ')})`;
+ }}
+
+
+
-export const VerticalGroup = meta.story({
- render: () => (
-
- Low
- Medium
- High
-
- ),
+
+ Example 3: Conditional Icons
+
+
+ {({ isSelected }) => (
+ <>
+ {isSelected ? : }
+ {isSelected ? 'Starred' : 'Star'}
+ >
+ )}
+
+
+
+
+
+ Example 4: Status Indicators
+
+
+ {({ isSelected }) => (
+
+
+ Active
+
+ )}
+
+
+ {({ isSelected }) => (
+
+
+ Inactive
+
+ )}
+
+
+
+
+ );
+ },
});
diff --git a/packages/ui/src/components/ToggleButton/ToggleButton.tsx b/packages/ui/src/components/ToggleButton/ToggleButton.tsx
index dd11554621..9c96304a80 100644
--- a/packages/ui/src/components/ToggleButton/ToggleButton.tsx
+++ b/packages/ui/src/components/ToggleButton/ToggleButton.tsx
@@ -47,14 +47,22 @@ export const ToggleButton = forwardRef(
{...(typeof surface === 'string' ? { 'data-on-surface': surface } : {})}
{...rest}
>
-
- {iconStart}
- {children}
- {iconEnd}
-
+ {renderProps => {
+ // If children is a function, call it with render props; otherwise use children as-is
+ const renderedChildren =
+ typeof children === 'function' ? children(renderProps) : children;
+
+ return (
+
+ {iconStart}
+ {renderedChildren}
+ {iconEnd}
+
+ );
+ }}
);
},
diff --git a/packages/ui/src/components/ToggleButton/types.ts b/packages/ui/src/components/ToggleButton/types.ts
index aba31e4f25..de70bd3c29 100644
--- a/packages/ui/src/components/ToggleButton/types.ts
+++ b/packages/ui/src/components/ToggleButton/types.ts
@@ -15,7 +15,7 @@
*/
import type { Breakpoint } from '../..';
-import type { ReactElement, ReactNode } from 'react';
+import type { ReactElement } from 'react';
import type { ToggleButtonProps as AriaToggleButtonProps } from 'react-aria-components';
import type { Responsive, Surface } from '../../types';
@@ -28,7 +28,6 @@ export interface ToggleButtonProps extends AriaToggleButtonProps {
size?: 'small' | 'medium' | Partial>;
iconStart?: ReactElement;
iconEnd?: ReactElement;
- children?: ReactNode;
/** Surface the toggle button is placed on. Defaults to context surface if available */
onSurface?: Responsive;
}
diff --git a/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.module.css b/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.module.css
index 36309ad9dd..90bedd1756 100644
--- a/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.module.css
+++ b/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.module.css
@@ -24,6 +24,7 @@
border-radius: var(--bui-radius-2);
overflow: hidden;
box-shadow: inset 0 0 0 1px var(--bui-border);
+ width: fit-content;
}
.bui-ToggleButtonGroup[data-orientation='vertical'] {
diff --git a/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.tsx b/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.tsx
index 6934b42eb6..43449f8472 100644
--- a/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.tsx
+++ b/packages/ui/src/components/ToggleButtonGroup/ToggleButtonGroup.tsx
@@ -15,28 +15,13 @@
*/
import clsx from 'clsx';
-import { forwardRef, Ref, createContext, useContext } from 'react';
+import { forwardRef, Ref } from 'react';
import { ToggleButtonGroup as AriaToggleButtonGroup } from 'react-aria-components';
import type { ToggleButtonGroupProps } from './types';
import { useStyles } from '../../hooks/useStyles';
import { ToggleButtonGroupDefinition } from './definition';
import styles from './ToggleButtonGroup.module.css';
-/** @internal */
-export interface ToggleButtonGroupContextValue {}
-
-const ToggleButtonGroupContext = createContext<
- ToggleButtonGroupContextValue | undefined
->(undefined);
-
-/**
- * Hook to access the ToggleButtonGroup context.
- * @internal
- */
-export const useToggleButtonGroupContext = () => {
- return useContext(ToggleButtonGroupContext);
-};
-
/** @public */
export const ToggleButtonGroup = forwardRef(
(props: ToggleButtonGroupProps, ref: Ref) => {
@@ -49,19 +34,15 @@ export const ToggleButtonGroup = forwardRef(
const { className, children, ...rest } = cleanedProps;
- const contextValue: ToggleButtonGroupContextValue = {};
-
return (
-
-
- {children}
-
-
+
+ {children}
+
);
},
);