From d44608f1091a2f6d5de08b047124f8f92aefd496 Mon Sep 17 00:00:00 2001 From: Patrycja Fogelman Date: Wed, 4 Jun 2025 14:08:53 +0200 Subject: [PATCH 01/14] Added SplitButton teams component --- .../components/SplitButton/SplitButton.tsx | 69 ++++ .../src/components/SplitButton/index.ts | 3 + .../SplitButton/renderSplitButton.tsx | 47 +++ .../components/SplitButton/validateProps.ts | 29 ++ .../components/ToggleButton/renderTooltip.tsx | 10 + packages/teams-components/src/index.ts | 1 + .../stories/SplitButton/Default.stories.tsx | 328 ++++++++++++++++++ .../stories/SplitButton/index.stories.tsx | 10 + 8 files changed, 497 insertions(+) create mode 100644 packages/teams-components/src/components/SplitButton/SplitButton.tsx create mode 100644 packages/teams-components/src/components/SplitButton/index.ts create mode 100644 packages/teams-components/src/components/SplitButton/renderSplitButton.tsx create mode 100644 packages/teams-components/src/components/SplitButton/validateProps.ts create mode 100644 packages/teams-components/src/components/ToggleButton/renderTooltip.tsx create mode 100644 packages/teams-components/stories/SplitButton/Default.stories.tsx create mode 100644 packages/teams-components/stories/SplitButton/index.stories.tsx diff --git a/packages/teams-components/src/components/SplitButton/SplitButton.tsx b/packages/teams-components/src/components/SplitButton/SplitButton.tsx new file mode 100644 index 00000000..4ff25d25 --- /dev/null +++ b/packages/teams-components/src/components/SplitButton/SplitButton.tsx @@ -0,0 +1,69 @@ +import * as React from 'react'; +import { renderSplitButton_unstable } from './renderSplitButton'; +import type { SplitButtonProps as SplitButtonPropsBase } from '@fluentui/react-components'; +import { + useSplitButton_unstable, + useSplitButtonStyles_unstable, + renderSplitButton_unstable as renderSplitButtonBase_unstable, +} from '@fluentui/react-components'; +import { ForwardRefComponent } from '@fluentui/react-utilities'; +import { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts'; +import { StrictCssClass, validateStrictClasses } from '../../strictStyles'; +import { StrictSlot } from '../../strictSlot'; +import { ButtonStrictOverrides, validateMenuButton } from '../Button'; +import { validateTitleProps, validateSplitIconButton } from './validateProps'; + +export interface SplitButtonProps + extends Pick< + SplitButtonPropsBase, + 'size' | 'disabled' | 'disabledFocusable' + > { + appearance?: 'transparent' | 'primary'; + className?: StrictCssClass; + icon?: StrictSlot; + onClick?: React.MouseEventHandler; + title?: StrictSlot; + primaryTitle?: StrictSlot; + secondaryTitle?: StrictSlot; +} + +/** + * SplitButtons are a grouping of two interactive surfaces where interacting with the first one triggers a primary + * action, while interacting with the second one opens a menu with secondary actions. + */ +export const SplitButton = React.forwardRef< + HTMLButtonElement, + SplitButtonProps +>((props, ref) => { + if (process.env.NODE_ENV !== 'production') { + validateProps(props); + } + + const { className, icon, title, menuTitle, ...restProps } = props; + const buttonProps = { + ...restProps, + className: className?.toString(), + icon, + } as SplitButtonPropsBase; + const state = useSplitButton_unstable(buttonProps, ref); + + useSplitButtonStyles_unstable(state); + + useCustomStyleHook_unstable('useSplitButtonStyles_unstable')(state); + + const titleProps = { title, menuTitle }; + + return titleProps.title + ? renderSplitButton_unstable(state, titleProps) + : renderSplitButtonBase_unstable(state); + // Casting is required due to lack of distributive union to support unions on @types/react +}) as ForwardRefComponent; + +SplitButton.displayName = 'SplitButton'; + +const validateProps = (props: SplitButtonProps) => { + validateStrictClasses(props.className); + validateSplitIconButton(props); + validateMenuButton(props); + validateTitleProps(props); +}; diff --git a/packages/teams-components/src/components/SplitButton/index.ts b/packages/teams-components/src/components/SplitButton/index.ts new file mode 100644 index 00000000..8a6a36d7 --- /dev/null +++ b/packages/teams-components/src/components/SplitButton/index.ts @@ -0,0 +1,3 @@ +export * from './SplitButton'; +export * from './validateProps'; +export * from './renderSplitButton'; diff --git a/packages/teams-components/src/components/SplitButton/renderSplitButton.tsx b/packages/teams-components/src/components/SplitButton/renderSplitButton.tsx new file mode 100644 index 00000000..3e5e4fc3 --- /dev/null +++ b/packages/teams-components/src/components/SplitButton/renderSplitButton.tsx @@ -0,0 +1,47 @@ +/** @jsxRuntime classic */ +/** @jsx createElement */ + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { createElement } from '@fluentui/react-jsx-runtime'; + +import { assertSlots } from '@fluentui/react-utilities'; +import type { + SplitButtonSlots, + SplitButtonState, +} from '@fluentui/react-components'; +import { StrictSlot } from '../../strictSlot'; +import { renderTooltip } from '../ToggleButton/renderTooltip'; + +export interface SplitButtonTitleProps { + title?: StrictSlot; + menuTitle?: StrictSlot; +} + +/** + * Renders a SplitButton component by passing the state defined props to the appropriate slots. + */ +export const renderSplitButton_unstable = ( + state: SplitButtonState, + titleProps: SplitButtonTitleProps +) => { + assertSlots(state); + + const hasDualTooltip = titleProps.menuTitle !== undefined; + + return hasDualTooltip ? ( + + {state.primaryActionButton && + renderTooltip(, titleProps.title)} + {state.menuButton && + renderTooltip(, titleProps.menuTitle)} + + ) : ( + renderTooltip( + + {state.primaryActionButton && } + {state.menuButton && } + , + titleProps.title + ) + ); +}; diff --git a/packages/teams-components/src/components/SplitButton/validateProps.ts b/packages/teams-components/src/components/SplitButton/validateProps.ts new file mode 100644 index 00000000..d8c4500f --- /dev/null +++ b/packages/teams-components/src/components/SplitButton/validateProps.ts @@ -0,0 +1,29 @@ +export const validateTitleProps = (props: { + title?: unknown; + menuTitle?: unknown; +}) => { + if (props.menuTitle && !props.title) { + throw new Error( + '@fluentui-contrib/teams-components::SplitButton with menuTitle present, title must also be provided' + ); + } +}; + +export const validateSplitIconButton = (props: { + title?: unknown; + icon?: unknown; + children?: unknown; + 'aria-label'?: string; + 'aria-labelledby'?: string; +}) => { + if ( + !props.children && + props.icon && + !props.title && + !(props['aria-label'] || props['aria-labelledby']) + ) { + throw new Error( + '@fluentui-contrib/teams-components::Icon button must have a title or aria label' + ); + } +}; diff --git a/packages/teams-components/src/components/ToggleButton/renderTooltip.tsx b/packages/teams-components/src/components/ToggleButton/renderTooltip.tsx new file mode 100644 index 00000000..f524f8ba --- /dev/null +++ b/packages/teams-components/src/components/ToggleButton/renderTooltip.tsx @@ -0,0 +1,10 @@ +import * as React from 'react'; +import { Tooltip } from '@fluentui/react-components'; + +export const renderTooltip = (children: any, title: any) => { + return ( + + {children} + + ); +}; diff --git a/packages/teams-components/src/index.ts b/packages/teams-components/src/index.ts index 0d8982ff..e31d5815 100644 --- a/packages/teams-components/src/index.ts +++ b/packages/teams-components/src/index.ts @@ -3,6 +3,7 @@ export { ToggleButton, type ToggleButtonProps, } from './components/ToggleButton'; +export { SplitButton } from './components/SplitButton'; export { makeStrictStyles, mergeStrictClasses, diff --git a/packages/teams-components/stories/SplitButton/Default.stories.tsx b/packages/teams-components/stories/SplitButton/Default.stories.tsx new file mode 100644 index 00000000..6d26327e --- /dev/null +++ b/packages/teams-components/stories/SplitButton/Default.stories.tsx @@ -0,0 +1,328 @@ +import * as React from 'react'; +import { SplitButton } from '@fluentui-contrib/teams-components'; +import { + makeStyles, + tokens, + Menu, + MenuTrigger, + MenuPopover, + MenuList, + MenuItem, + MenuButtonProps, +} from '@fluentui/react-components'; +import { + CalendarRegular, + CalendarFilled, + bundleIcon, +} from '@fluentui/react-icons'; + +const CalendarIcon = bundleIcon(CalendarFilled, CalendarRegular); + +const useStyles = makeStyles({ + sampleContainer: { + display: 'grid', + gridTemplateColumns: 'repeat(5, 120px)', + gap: tokens.spacingHorizontalL, + }, + + evil: { + background: 'red', + }, +}); + +export const Default = () => { + const styles = useStyles(); + return ( +
+ + + {(triggerProps: MenuButtonProps) => ( + Split + )} + + + + + Item a + Item b + + + + + + {(triggerProps: MenuButtonProps) => ( + + Split + + )} + + + + + Item a + Item b + + + + + + {(triggerProps: MenuButtonProps) => ( + + Split + + )} + + + + + Item a + Item b + + + + + + {(triggerProps: MenuButtonProps) => ( + } + > + Split + + )} + + + + + Item a + Item b + + + + + + {(triggerProps: MenuButtonProps) => ( + } + /> + )} + + + + + Item a + Item b + + + + + + + {(triggerProps: MenuButtonProps) => ( + + Split + + )} + + + + + Item a + Item b + + + + + + {(triggerProps: MenuButtonProps) => ( + + Split + + )} + + + + + Item a + Item b + + + + + + {(triggerProps: MenuButtonProps) => ( + + Split + + )} + + + + + Item a + Item b + + + + + + {(triggerProps: MenuButtonProps) => ( + } + appearance="transparent" + > + Split + + )} + + + + + Item a + Item b + + + + + + {(triggerProps: MenuButtonProps) => ( + } + appearance="transparent" + /> + )} + + + + + Item a + Item b + + + + + + {(triggerProps: MenuButtonProps) => ( + + Split + + )} + + + + + Item a + Item b + + + + + + {(triggerProps: MenuButtonProps) => ( + + Split + + )} + + + + + Item a + Item b + + + + + + {(triggerProps: MenuButtonProps) => ( + + Split + + )} + + + + + Item a + Item b + + + + + + {(triggerProps: MenuButtonProps) => ( + } + appearance="primary" + > + Split + + )} + + + + + Item a + Item b + + + + + + {(triggerProps: MenuButtonProps) => ( + } + appearance="primary" + /> + )} + + + + + Item a + Item b + + + +
+ ); +}; diff --git a/packages/teams-components/stories/SplitButton/index.stories.tsx b/packages/teams-components/stories/SplitButton/index.stories.tsx new file mode 100644 index 00000000..bbd89c3b --- /dev/null +++ b/packages/teams-components/stories/SplitButton/index.stories.tsx @@ -0,0 +1,10 @@ +import type { Meta } from '@storybook/react'; +import { SplitButton } from '@fluentui-contrib/teams-components'; +export { Default } from './Default.stories'; + +const meta = { + title: 'Packages/teams-components/SplitButton', + component: SplitButton, +} satisfies Meta; + +export default meta; From e75f3aab63f4c2e46a83957411f121f8b39fe499 Mon Sep 17 00:00:00 2001 From: Patrycja Fogelman Date: Wed, 4 Jun 2025 14:14:41 +0200 Subject: [PATCH 02/14] Fixing SpliutButtonProps title props --- .../src/components/SplitButton/SplitButton.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/teams-components/src/components/SplitButton/SplitButton.tsx b/packages/teams-components/src/components/SplitButton/SplitButton.tsx index 4ff25d25..72968fc0 100644 --- a/packages/teams-components/src/components/SplitButton/SplitButton.tsx +++ b/packages/teams-components/src/components/SplitButton/SplitButton.tsx @@ -23,8 +23,7 @@ export interface SplitButtonProps icon?: StrictSlot; onClick?: React.MouseEventHandler; title?: StrictSlot; - primaryTitle?: StrictSlot; - secondaryTitle?: StrictSlot; + menuTitle?: StrictSlot; } /** From 9f6cf9d6d20e1605d2f27bea7adc68dd38d77609 Mon Sep 17 00:00:00 2001 From: Patrycja Fogelman Date: Fri, 27 Jun 2025 11:15:20 +0200 Subject: [PATCH 03/14] Adding basic tests --- packages/teams-components/jest.config.ts | 2 +- .../SplitButton/SplitButton.test.tsx | 30 +++++++++++++++++++ .../components/SplitButton/SplitButton.tsx | 22 +++++++------- .../components/SplitButton/validateProps.ts | 11 +++++++ 4 files changed, 52 insertions(+), 13 deletions(-) create mode 100644 packages/teams-components/src/components/SplitButton/SplitButton.test.tsx diff --git a/packages/teams-components/jest.config.ts b/packages/teams-components/jest.config.ts index 3e1c3f76..3139b631 100644 --- a/packages/teams-components/jest.config.ts +++ b/packages/teams-components/jest.config.ts @@ -24,7 +24,7 @@ export default { transform: { '^.+\\.[tj]sx?$': ['@swc/jest', swcJestConfig], }, - moduleFileExtensions: ['ts', 'js', 'html'], + moduleFileExtensions: ['ts', 'tsx', 'js', 'html'], testEnvironment: 'jsdom', coverageDirectory: '../../coverage/packages/teams-components', }; diff --git a/packages/teams-components/src/components/SplitButton/SplitButton.test.tsx b/packages/teams-components/src/components/SplitButton/SplitButton.test.tsx new file mode 100644 index 00000000..5d330045 --- /dev/null +++ b/packages/teams-components/src/components/SplitButton/SplitButton.test.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import { render } from '@testing-library/react'; +import { SplitButton } from './SplitButton'; +import { CalendarRegular } from '@fluentui/react-icons'; + +describe('SplitButton', () => { + it('should render', () => { + render(Test); + }); + + it('should throw error if menuTitle is provided without main title', () => { + expect(() => + render(Test) + ).toThrow( + '@fluentui-contrib/teams-components::SplitButton with menuTitle present, title must also be provided' + ); + }); + + it('should throw error if no content or icon is provided', () => { + expect(() => render()).toThrow( + '@fluentui-contrib/teams-components::SplitButton must have at least one of children or icon' + ); + }); + + it('should throw error if icon button has no title provided', () => { + expect(() => render(} />)).toThrow( + '@fluentui-contrib/teams-components::Icon button must have a title or aria label' + ); + }); +}); diff --git a/packages/teams-components/src/components/SplitButton/SplitButton.tsx b/packages/teams-components/src/components/SplitButton/SplitButton.tsx index 72968fc0..f532a935 100644 --- a/packages/teams-components/src/components/SplitButton/SplitButton.tsx +++ b/packages/teams-components/src/components/SplitButton/SplitButton.tsx @@ -8,21 +8,18 @@ import { } from '@fluentui/react-components'; import { ForwardRefComponent } from '@fluentui/react-utilities'; import { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts'; -import { StrictCssClass, validateStrictClasses } from '../../strictStyles'; +import { validateStrictClasses } from '../../strictStyles'; import { StrictSlot } from '../../strictSlot'; -import { ButtonStrictOverrides, validateMenuButton } from '../Button'; -import { validateTitleProps, validateSplitIconButton } from './validateProps'; +import { ButtonProps, validateMenuButton } from '../Button'; +import { + validateTitleProps, + validateSplitIconButton, + validateHasContent, +} from './validateProps'; export interface SplitButtonProps - extends Pick< - SplitButtonPropsBase, - 'size' | 'disabled' | 'disabledFocusable' - > { - appearance?: 'transparent' | 'primary'; - className?: StrictCssClass; - icon?: StrictSlot; - onClick?: React.MouseEventHandler; - title?: StrictSlot; + extends ButtonProps, + Pick { menuTitle?: StrictSlot; } @@ -61,6 +58,7 @@ export const SplitButton = React.forwardRef< SplitButton.displayName = 'SplitButton'; const validateProps = (props: SplitButtonProps) => { + validateHasContent(props); validateStrictClasses(props.className); validateSplitIconButton(props); validateMenuButton(props); diff --git a/packages/teams-components/src/components/SplitButton/validateProps.ts b/packages/teams-components/src/components/SplitButton/validateProps.ts index d8c4500f..d436c84f 100644 --- a/packages/teams-components/src/components/SplitButton/validateProps.ts +++ b/packages/teams-components/src/components/SplitButton/validateProps.ts @@ -27,3 +27,14 @@ export const validateSplitIconButton = (props: { ); } }; + +export const validateHasContent = (props: { + children?: unknown; + icon?: unknown; +}) => { + if (!props.children && !props.icon) { + throw new Error( + '@fluentui-contrib/teams-components::SplitButton must have at least one of children or icon' + ); + } +}; From c77d953f1d36d8be725e9472b28b8521bd1d211b Mon Sep 17 00:00:00 2001 From: Patrycja Fogelman Date: Wed, 2 Jul 2025 14:05:44 +0200 Subject: [PATCH 04/14] Added MenuTrigger wrapper inside of SplitButton --- .../components/SplitButton/SplitButton.tsx | 42 ++-- .../stories/SplitButton/Default.stories.tsx | 222 ++++++------------ 2 files changed, 93 insertions(+), 171 deletions(-) diff --git a/packages/teams-components/src/components/SplitButton/SplitButton.tsx b/packages/teams-components/src/components/SplitButton/SplitButton.tsx index f532a935..330dae61 100644 --- a/packages/teams-components/src/components/SplitButton/SplitButton.tsx +++ b/packages/teams-components/src/components/SplitButton/SplitButton.tsx @@ -5,6 +5,8 @@ import { useSplitButton_unstable, useSplitButtonStyles_unstable, renderSplitButton_unstable as renderSplitButtonBase_unstable, + MenuTrigger, + MenuButtonProps, } from '@fluentui/react-components'; import { ForwardRefComponent } from '@fluentui/react-utilities'; import { useCustomStyleHook_unstable } from '@fluentui/react-shared-contexts'; @@ -17,27 +19,19 @@ import { validateHasContent, } from './validateProps'; -export interface SplitButtonProps - extends ButtonProps, - Pick { +export interface SplitButtonProps extends ButtonProps { menuTitle?: StrictSlot; } -/** - * SplitButtons are a grouping of two interactive surfaces where interacting with the first one triggers a primary - * action, while interacting with the second one opens a menu with secondary actions. - */ -export const SplitButton = React.forwardRef< - HTMLButtonElement, - SplitButtonProps ->((props, ref) => { - if (process.env.NODE_ENV !== 'production') { - validateProps(props); - } - +const useTeamsSplitButton = ( + props: SplitButtonProps, + triggerProps: MenuButtonProps, + ref: React.ForwardedRef +) => { const { className, icon, title, menuTitle, ...restProps } = props; const buttonProps = { ...restProps, + ...triggerProps, className: className?.toString(), icon, } as SplitButtonPropsBase; @@ -52,7 +46,23 @@ export const SplitButton = React.forwardRef< return titleProps.title ? renderSplitButton_unstable(state, titleProps) : renderSplitButtonBase_unstable(state); - // Casting is required due to lack of distributive union to support unions on @types/react +}; + +export const SplitButton = React.forwardRef< + HTMLButtonElement, + SplitButtonProps +>((props, ref) => { + if (process.env.NODE_ENV !== 'production') { + validateProps(props); + } + + return ( + + {(triggerProps: MenuButtonProps) => + useTeamsSplitButton(props, triggerProps, ref) + } + + ); }) as ForwardRefComponent; SplitButton.displayName = 'SplitButton'; diff --git a/packages/teams-components/stories/SplitButton/Default.stories.tsx b/packages/teams-components/stories/SplitButton/Default.stories.tsx index 6d26327e..63a1f571 100644 --- a/packages/teams-components/stories/SplitButton/Default.stories.tsx +++ b/packages/teams-components/stories/SplitButton/Default.stories.tsx @@ -4,11 +4,9 @@ import { makeStyles, tokens, Menu, - MenuTrigger, MenuPopover, MenuList, MenuItem, - MenuButtonProps, } from '@fluentui/react-components'; import { CalendarRegular, @@ -35,11 +33,7 @@ export const Default = () => { return (
- - {(triggerProps: MenuButtonProps) => ( - Split - )} - + Split @@ -49,13 +43,7 @@ export const Default = () => { - - {(triggerProps: MenuButtonProps) => ( - - Split - - )} - + Split @@ -65,17 +53,9 @@ export const Default = () => { - - {(triggerProps: MenuButtonProps) => ( - - Split - - )} - + + Split + @@ -85,18 +65,13 @@ export const Default = () => { - - {(triggerProps: MenuButtonProps) => ( - } - > - Split - - )} - + } + > + Split + @@ -106,16 +81,11 @@ export const Default = () => { - - {(triggerProps: MenuButtonProps) => ( - } - /> - )} - + } + /> @@ -126,13 +96,7 @@ export const Default = () => { - - {(triggerProps: MenuButtonProps) => ( - - Split - - )} - + Split @@ -142,17 +106,9 @@ export const Default = () => { - - {(triggerProps: MenuButtonProps) => ( - - Split - - )} - + + Split + @@ -162,18 +118,13 @@ export const Default = () => { - - {(triggerProps: MenuButtonProps) => ( - - Split - - )} - + + Split + @@ -183,19 +134,14 @@ export const Default = () => { - - {(triggerProps: MenuButtonProps) => ( - } - appearance="transparent" - > - Split - - )} - + } + appearance="transparent" + > + Split + @@ -205,17 +151,12 @@ export const Default = () => { - - {(triggerProps: MenuButtonProps) => ( - } - appearance="transparent" - /> - )} - + } + appearance="transparent" + /> @@ -225,13 +166,7 @@ export const Default = () => { - - {(triggerProps: MenuButtonProps) => ( - - Split - - )} - + Split @@ -241,17 +176,9 @@ export const Default = () => { - - {(triggerProps: MenuButtonProps) => ( - - Split - - )} - + + Split + @@ -261,18 +188,13 @@ export const Default = () => { - - {(triggerProps: MenuButtonProps) => ( - - Split - - )} - + + Split + @@ -282,19 +204,14 @@ export const Default = () => { - - {(triggerProps: MenuButtonProps) => ( - } - appearance="primary" - > - Split - - )} - + } + appearance="primary" + > + Split + @@ -304,17 +221,12 @@ export const Default = () => { - - {(triggerProps: MenuButtonProps) => ( - } - appearance="primary" - /> - )} - + } + appearance="primary" + /> From 850e6477618886ec18b1daabcd564f41ed07bf74 Mon Sep 17 00:00:00 2001 From: Patrycja Fogelman Date: Wed, 2 Jul 2025 14:07:06 +0200 Subject: [PATCH 05/14] Change files --- ...ms-components-29fd49f3-75f7-4481-8b4b-b4cd75278283.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-contrib-teams-components-29fd49f3-75f7-4481-8b4b-b4cd75278283.json diff --git a/change/@fluentui-contrib-teams-components-29fd49f3-75f7-4481-8b4b-b4cd75278283.json b/change/@fluentui-contrib-teams-components-29fd49f3-75f7-4481-8b4b-b4cd75278283.json new file mode 100644 index 00000000..02231e7c --- /dev/null +++ b/change/@fluentui-contrib-teams-components-29fd49f3-75f7-4481-8b4b-b4cd75278283.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Added SplitButton teams component", + "packageName": "@fluentui-contrib/teams-components", + "email": "patrycja.fogelman@microsoft.com", + "dependentChangeType": "patch" +} From a0e89eca4ea11fdd9550e9dd47f8adb774a7a396 Mon Sep 17 00:00:00 2001 From: Patrycja Fogelman Date: Wed, 2 Jul 2025 14:41:51 +0200 Subject: [PATCH 06/14] Fixing types in renderTooltip --- packages/teams-components/package.json | 5 ++++- .../src/components/ToggleButton/renderTooltip.tsx | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/teams-components/package.json b/packages/teams-components/package.json index e25a59cb..67fe484b 100644 --- a/packages/teams-components/package.json +++ b/packages/teams-components/package.json @@ -2,7 +2,10 @@ "name": "@fluentui-contrib/teams-components", "version": "0.1.2", "dependencies": { - "@swc/helpers": "~0.5.11" + "@swc/helpers": "~0.5.11", + "@fluentui/react-utilities": "^9.16.0", + "@fluentui/react-shared-contexts": "^9.7.2", + "@fluentui/react-jsx-runtime": "^9.0.29" }, "main": "./src/index.js", "typings": "./src/index.d.ts", diff --git a/packages/teams-components/src/components/ToggleButton/renderTooltip.tsx b/packages/teams-components/src/components/ToggleButton/renderTooltip.tsx index f524f8ba..909a6b57 100644 --- a/packages/teams-components/src/components/ToggleButton/renderTooltip.tsx +++ b/packages/teams-components/src/components/ToggleButton/renderTooltip.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; import { Tooltip } from '@fluentui/react-components'; +import { StrictSlot } from '../../strictSlot'; -export const renderTooltip = (children: any, title: any) => { +export const renderTooltip = (children: StrictSlot, title: StrictSlot) => { return ( {children} From efcb5fdddbf78806a02ed8ae6db9020e20f0d2ad Mon Sep 17 00:00:00 2001 From: Patrycja Fogelman Date: Wed, 2 Jul 2025 14:44:29 +0200 Subject: [PATCH 07/14] Fixed react-shared-context version --- packages/teams-components/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/teams-components/package.json b/packages/teams-components/package.json index 67fe484b..2b6ccb0c 100644 --- a/packages/teams-components/package.json +++ b/packages/teams-components/package.json @@ -4,7 +4,7 @@ "dependencies": { "@swc/helpers": "~0.5.11", "@fluentui/react-utilities": "^9.16.0", - "@fluentui/react-shared-contexts": "^9.7.2", + "@fluentui/react-shared-contexts": ">=9.7.2 <10.0.0 ", "@fluentui/react-jsx-runtime": "^9.0.29" }, "main": "./src/index.js", From 68a9bf3495fb603725c9c3ea68142f885402b467 Mon Sep 17 00:00:00 2001 From: Patrycja Fogelman Date: Wed, 2 Jul 2025 15:15:32 +0200 Subject: [PATCH 08/14] updating yarn.lock --- yarn.lock | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/yarn.lock b/yarn.lock index 3d251f05..6ba8d4a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2413,6 +2413,14 @@ "@griffel/react" "^1.5.22" "@swc/helpers" "^0.5.1" +"@fluentui/react-shared-contexts@>=9.7.2 <10.0.0 ": + version "9.24.0" + resolved "https://registry.yarnpkg.com/@fluentui/react-shared-contexts/-/react-shared-contexts-9.24.0.tgz#33cf16ee3f2736e9f3a194680ee23533039e90a4" + integrity sha512-GA+uLv711E+YGrAP/aVB15ozvNCiuB2ZrPDC9aYF+A6sRDxoZZG8VgHjhQ/YWJfVjDXLky4ihirknzsW1sjGtg== + dependencies: + "@fluentui/react-theme" "^9.1.24" + "@swc/helpers" "^0.5.1" + "@fluentui/react-shared-contexts@^9.23.1", "@fluentui/react-shared-contexts@^9.7.2": version "9.23.1" resolved "https://registry.yarnpkg.com/@fluentui/react-shared-contexts/-/react-shared-contexts-9.23.1.tgz#96155b604574c2207d1100727d477f5ab6e6e36d" From 223a8b16330b44982b7f54252b5159ba08d5b826 Mon Sep 17 00:00:00 2001 From: Patrycja Fogelman Date: Wed, 2 Jul 2025 15:18:20 +0200 Subject: [PATCH 09/14] yarn-deduplicate --- yarn.lock | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6ba8d4a5..90fb9d5d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2413,7 +2413,7 @@ "@griffel/react" "^1.5.22" "@swc/helpers" "^0.5.1" -"@fluentui/react-shared-contexts@>=9.7.2 <10.0.0 ": +"@fluentui/react-shared-contexts@>=9.7.2 <10.0.0 ", "@fluentui/react-shared-contexts@^9.23.1", "@fluentui/react-shared-contexts@^9.7.2": version "9.24.0" resolved "https://registry.yarnpkg.com/@fluentui/react-shared-contexts/-/react-shared-contexts-9.24.0.tgz#33cf16ee3f2736e9f3a194680ee23533039e90a4" integrity sha512-GA+uLv711E+YGrAP/aVB15ozvNCiuB2ZrPDC9aYF+A6sRDxoZZG8VgHjhQ/YWJfVjDXLky4ihirknzsW1sjGtg== @@ -2421,14 +2421,6 @@ "@fluentui/react-theme" "^9.1.24" "@swc/helpers" "^0.5.1" -"@fluentui/react-shared-contexts@^9.23.1", "@fluentui/react-shared-contexts@^9.7.2": - version "9.23.1" - resolved "https://registry.yarnpkg.com/@fluentui/react-shared-contexts/-/react-shared-contexts-9.23.1.tgz#96155b604574c2207d1100727d477f5ab6e6e36d" - integrity sha512-mP+7talxLz7n0G36o7Asdvst+JPzUbqbnoMKUWRVB5YwzlOXumEgaQDgL1BkRUJYaDGOjIiSTUjHOEkBt7iSdg== - dependencies: - "@fluentui/react-theme" "^9.1.24" - "@swc/helpers" "^0.5.1" - "@fluentui/react-skeleton@^9.2.7": version "9.2.7" resolved "https://registry.yarnpkg.com/@fluentui/react-skeleton/-/react-skeleton-9.2.7.tgz#41bccb0f3e952ec4127d66a684c31a17b6e359b1" From d9f68dc0f79201aa753cc3c1c7c736580e7bfe6a Mon Sep 17 00:00:00 2001 From: Patrycja Fogelman Date: Wed, 2 Jul 2025 15:25:19 +0200 Subject: [PATCH 10/14] fixing typo in package.json --- packages/teams-components/package.json | 2 +- yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/teams-components/package.json b/packages/teams-components/package.json index 2b6ccb0c..8efb5da9 100644 --- a/packages/teams-components/package.json +++ b/packages/teams-components/package.json @@ -4,7 +4,7 @@ "dependencies": { "@swc/helpers": "~0.5.11", "@fluentui/react-utilities": "^9.16.0", - "@fluentui/react-shared-contexts": ">=9.7.2 <10.0.0 ", + "@fluentui/react-shared-contexts": ">=9.7.2 <10.0.0", "@fluentui/react-jsx-runtime": "^9.0.29" }, "main": "./src/index.js", diff --git a/yarn.lock b/yarn.lock index 90fb9d5d..4e8613de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2413,7 +2413,7 @@ "@griffel/react" "^1.5.22" "@swc/helpers" "^0.5.1" -"@fluentui/react-shared-contexts@>=9.7.2 <10.0.0 ", "@fluentui/react-shared-contexts@^9.23.1", "@fluentui/react-shared-contexts@^9.7.2": +"@fluentui/react-shared-contexts@^9.23.1", "@fluentui/react-shared-contexts@^9.7.2": version "9.24.0" resolved "https://registry.yarnpkg.com/@fluentui/react-shared-contexts/-/react-shared-contexts-9.24.0.tgz#33cf16ee3f2736e9f3a194680ee23533039e90a4" integrity sha512-GA+uLv711E+YGrAP/aVB15ozvNCiuB2ZrPDC9aYF+A6sRDxoZZG8VgHjhQ/YWJfVjDXLky4ihirknzsW1sjGtg== From aa20d0770786a5afd69c501ff00f6123885608c5 Mon Sep 17 00:00:00 2001 From: Patrycja Fogelman Date: Wed, 2 Jul 2025 15:27:36 +0200 Subject: [PATCH 11/14] new yarn.lock --- yarn.lock | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 4e8613de..6e2881ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2413,7 +2413,7 @@ "@griffel/react" "^1.5.22" "@swc/helpers" "^0.5.1" -"@fluentui/react-shared-contexts@^9.23.1", "@fluentui/react-shared-contexts@^9.7.2": +"@fluentui/react-shared-contexts@>=9.7.2 <10.0.0": version "9.24.0" resolved "https://registry.yarnpkg.com/@fluentui/react-shared-contexts/-/react-shared-contexts-9.24.0.tgz#33cf16ee3f2736e9f3a194680ee23533039e90a4" integrity sha512-GA+uLv711E+YGrAP/aVB15ozvNCiuB2ZrPDC9aYF+A6sRDxoZZG8VgHjhQ/YWJfVjDXLky4ihirknzsW1sjGtg== @@ -2421,6 +2421,14 @@ "@fluentui/react-theme" "^9.1.24" "@swc/helpers" "^0.5.1" +"@fluentui/react-shared-contexts@^9.23.1", "@fluentui/react-shared-contexts@^9.7.2": + version "9.23.1" + resolved "https://registry.yarnpkg.com/@fluentui/react-shared-contexts/-/react-shared-contexts-9.23.1.tgz#96155b604574c2207d1100727d477f5ab6e6e36d" + integrity sha512-mP+7talxLz7n0G36o7Asdvst+JPzUbqbnoMKUWRVB5YwzlOXumEgaQDgL1BkRUJYaDGOjIiSTUjHOEkBt7iSdg== + dependencies: + "@fluentui/react-theme" "^9.1.24" + "@swc/helpers" "^0.5.1" + "@fluentui/react-skeleton@^9.2.7": version "9.2.7" resolved "https://registry.yarnpkg.com/@fluentui/react-skeleton/-/react-skeleton-9.2.7.tgz#41bccb0f3e952ec4127d66a684c31a17b6e359b1" From f521737c54f6b153f9e6f5d1dfe57c10f978d929 Mon Sep 17 00:00:00 2001 From: Patrycja Fogelman Date: Wed, 2 Jul 2025 15:32:34 +0200 Subject: [PATCH 12/14] yarn-deduplicate --- yarn.lock | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6e2881ce..26c73a44 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2413,7 +2413,7 @@ "@griffel/react" "^1.5.22" "@swc/helpers" "^0.5.1" -"@fluentui/react-shared-contexts@>=9.7.2 <10.0.0": +"@fluentui/react-shared-contexts@>=9.7.2 <10.0.0", "@fluentui/react-shared-contexts@^9.23.1", "@fluentui/react-shared-contexts@^9.7.2": version "9.24.0" resolved "https://registry.yarnpkg.com/@fluentui/react-shared-contexts/-/react-shared-contexts-9.24.0.tgz#33cf16ee3f2736e9f3a194680ee23533039e90a4" integrity sha512-GA+uLv711E+YGrAP/aVB15ozvNCiuB2ZrPDC9aYF+A6sRDxoZZG8VgHjhQ/YWJfVjDXLky4ihirknzsW1sjGtg== @@ -2421,14 +2421,6 @@ "@fluentui/react-theme" "^9.1.24" "@swc/helpers" "^0.5.1" -"@fluentui/react-shared-contexts@^9.23.1", "@fluentui/react-shared-contexts@^9.7.2": - version "9.23.1" - resolved "https://registry.yarnpkg.com/@fluentui/react-shared-contexts/-/react-shared-contexts-9.23.1.tgz#96155b604574c2207d1100727d477f5ab6e6e36d" - integrity sha512-mP+7talxLz7n0G36o7Asdvst+JPzUbqbnoMKUWRVB5YwzlOXumEgaQDgL1BkRUJYaDGOjIiSTUjHOEkBt7iSdg== - dependencies: - "@fluentui/react-theme" "^9.1.24" - "@swc/helpers" "^0.5.1" - "@fluentui/react-skeleton@^9.2.7": version "9.2.7" resolved "https://registry.yarnpkg.com/@fluentui/react-skeleton/-/react-skeleton-9.2.7.tgz#41bccb0f3e952ec4127d66a684c31a17b6e359b1" From 32652b947716e63e1ce63b7535462a815df1515f Mon Sep 17 00:00:00 2001 From: Patrycja Fogelman Date: Thu, 3 Jul 2025 10:23:24 +0200 Subject: [PATCH 13/14] strengthening types --- .../src/components/SplitButton/SplitButton.tsx | 10 ++++++++-- .../components/SplitButton/renderSplitButton.tsx | 15 +++++++++++---- .../src/components/ToggleButton/renderTooltip.tsx | 7 +++++-- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/packages/teams-components/src/components/SplitButton/SplitButton.tsx b/packages/teams-components/src/components/SplitButton/SplitButton.tsx index 330dae61..e2aa0858 100644 --- a/packages/teams-components/src/components/SplitButton/SplitButton.tsx +++ b/packages/teams-components/src/components/SplitButton/SplitButton.tsx @@ -1,5 +1,8 @@ import * as React from 'react'; -import { renderSplitButton_unstable } from './renderSplitButton'; +import { + renderSplitButton_unstable, + SplitButtonTitleProps, +} from './renderSplitButton'; import type { SplitButtonProps as SplitButtonPropsBase } from '@fluentui/react-components'; import { useSplitButton_unstable, @@ -41,7 +44,10 @@ const useTeamsSplitButton = ( useCustomStyleHook_unstable('useSplitButtonStyles_unstable')(state); - const titleProps = { title, menuTitle }; + const titleProps: SplitButtonTitleProps = { + title: title ?? undefined, + menuTitle: menuTitle ?? undefined, + }; return titleProps.title ? renderSplitButton_unstable(state, titleProps) diff --git a/packages/teams-components/src/components/SplitButton/renderSplitButton.tsx b/packages/teams-components/src/components/SplitButton/renderSplitButton.tsx index 3e5e4fc3..20024d06 100644 --- a/packages/teams-components/src/components/SplitButton/renderSplitButton.tsx +++ b/packages/teams-components/src/components/SplitButton/renderSplitButton.tsx @@ -13,8 +13,8 @@ import { StrictSlot } from '../../strictSlot'; import { renderTooltip } from '../ToggleButton/renderTooltip'; export interface SplitButtonTitleProps { - title?: StrictSlot; - menuTitle?: StrictSlot; + title?: NonNullable; + menuTitle?: NonNullable; } /** @@ -26,9 +26,16 @@ export const renderSplitButton_unstable = ( ) => { assertSlots(state); - const hasDualTooltip = titleProps.menuTitle !== undefined; + if (titleProps.title === undefined) { + return ( + + {state.primaryActionButton && } + {state.menuButton && } + + ); + } - return hasDualTooltip ? ( + return titleProps.menuTitle !== undefined ? ( {state.primaryActionButton && renderTooltip(, titleProps.title)} diff --git a/packages/teams-components/src/components/ToggleButton/renderTooltip.tsx b/packages/teams-components/src/components/ToggleButton/renderTooltip.tsx index 909a6b57..783661f5 100644 --- a/packages/teams-components/src/components/ToggleButton/renderTooltip.tsx +++ b/packages/teams-components/src/components/ToggleButton/renderTooltip.tsx @@ -1,8 +1,11 @@ import * as React from 'react'; -import { Tooltip } from '@fluentui/react-components'; +import { Tooltip, TooltipProps } from '@fluentui/react-components'; import { StrictSlot } from '../../strictSlot'; -export const renderTooltip = (children: StrictSlot, title: StrictSlot) => { +export const renderTooltip = ( + children: TooltipProps['children'], + title: NonNullable +) => { return ( {children} From cbe8b5f247d306cba93bd321cbd65de49a16ea27 Mon Sep 17 00:00:00 2001 From: Patrycja Fogelman Date: Tue, 8 Jul 2025 11:27:50 +0200 Subject: [PATCH 14/14] Fixing comments pt 1 --- .../SplitButton/renderSplitButton.tsx | 35 +++++++++++-------- .../renderTooltip.tsx | 0 2 files changed, 20 insertions(+), 15 deletions(-) rename packages/teams-components/src/components/{ToggleButton => SplitButton}/renderTooltip.tsx (100%) diff --git a/packages/teams-components/src/components/SplitButton/renderSplitButton.tsx b/packages/teams-components/src/components/SplitButton/renderSplitButton.tsx index 20024d06..32ed7392 100644 --- a/packages/teams-components/src/components/SplitButton/renderSplitButton.tsx +++ b/packages/teams-components/src/components/SplitButton/renderSplitButton.tsx @@ -10,7 +10,7 @@ import type { SplitButtonState, } from '@fluentui/react-components'; import { StrictSlot } from '../../strictSlot'; -import { renderTooltip } from '../ToggleButton/renderTooltip'; +import { renderTooltip } from './renderTooltip'; export interface SplitButtonTitleProps { title?: NonNullable; @@ -26,6 +26,7 @@ export const renderSplitButton_unstable = ( ) => { assertSlots(state); + // rendering without tootlip if title is not defined if (titleProps.title === undefined) { return ( @@ -35,20 +36,24 @@ export const renderSplitButton_unstable = ( ); } - return titleProps.menuTitle !== undefined ? ( - - {state.primaryActionButton && - renderTooltip(, titleProps.title)} - {state.menuButton && - renderTooltip(, titleProps.menuTitle)} - - ) : ( - renderTooltip( + // if both title and menuTitle are defined, render separate tooltips for each button + if (titleProps.menuTitle !== undefined) { + return ( - {state.primaryActionButton && } - {state.menuButton && } - , - titleProps.title - ) + {state.primaryActionButton && + renderTooltip(, titleProps.title)} + {state.menuButton && + renderTooltip(, titleProps.menuTitle)} + + ); + } + + // render single tooltip for the whole split button + return renderTooltip( + + {state.primaryActionButton && } + {state.menuButton && } + , + titleProps.title ); }; diff --git a/packages/teams-components/src/components/ToggleButton/renderTooltip.tsx b/packages/teams-components/src/components/SplitButton/renderTooltip.tsx similarity index 100% rename from packages/teams-components/src/components/ToggleButton/renderTooltip.tsx rename to packages/teams-components/src/components/SplitButton/renderTooltip.tsx