diff --git a/packages/fluentui/CHANGELOG.md b/packages/fluentui/CHANGELOG.md index ff628302c04d4..44d8bfd256989 100644 --- a/packages/fluentui/CHANGELOG.md +++ b/packages/fluentui/CHANGELOG.md @@ -18,6 +18,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased] +### BREAKING CHANGES +- Styles from `ChatMessage` content slot were moved to exact component `ChatMessageContent` @yuanboxue-amber ([#24691](https://github.com/microsoft/fluentui/pull/24691)) + ### Features - Add a new comfy layout variation for `ChatMessage` @davezuko ([#23974](https://github.com/microsoft/fluentui/pull/23974)) diff --git a/packages/fluentui/docs/src/examples/components/Chat/Content/ChatExampleReactionGroupMeReacting.shorthand.steps.ts b/packages/fluentui/docs/src/examples/components/Chat/Content/ChatExampleReactionGroupMeReacting.shorthand.steps.ts index 975ba3744a680..47c4430516daa 100644 --- a/packages/fluentui/docs/src/examples/components/Chat/Content/ChatExampleReactionGroupMeReacting.shorthand.steps.ts +++ b/packages/fluentui/docs/src/examples/components/Chat/Content/ChatExampleReactionGroupMeReacting.shorthand.steps.ts @@ -1,8 +1,8 @@ import { ALL_THEMES, ScreenerTestsConfig } from '@fluentui/scripts/screener'; -import { reactionClassName, chatMessageSlotClassNames } from '@fluentui/react-northstar'; +import { reactionClassName, chatMessageContentClassName } from '@fluentui/react-northstar'; const selectors = { - chatMessageContent: `.${chatMessageSlotClassNames.content}`, + chatMessageContent: chatMessageContentClassName, reaction: `.${reactionClassName}`, }; diff --git a/packages/fluentui/docs/src/examples/components/Chat/Playground.tsx b/packages/fluentui/docs/src/examples/components/Chat/Playground.tsx index 4fe381e260c70..1ac90de5d886a 100644 --- a/packages/fluentui/docs/src/examples/components/Chat/Playground.tsx +++ b/packages/fluentui/docs/src/examples/components/Chat/Playground.tsx @@ -15,6 +15,7 @@ import { import { Avatar, Chat, + ChatItem, ChatItemProps, ChatMessageProps, MenuProps, @@ -126,17 +127,13 @@ const ChatPlayground: React.FunctionComponent = () => { }; return ( - } size={size} />, - key: '1', - message: , - ...chatItemProps, - }, - ]} - /> + + } size={size} />} + message={} + {...chatItemProps} + /> + ); }; diff --git a/packages/fluentui/docs/src/examples/components/Chat/Types/ChatMessageExampleStyled.shorthand.tsx b/packages/fluentui/docs/src/examples/components/Chat/Types/ChatMessageExampleStyled.shorthand.tsx index fde12dc562e7b..d1932062888e5 100644 --- a/packages/fluentui/docs/src/examples/components/Chat/Types/ChatMessageExampleStyled.shorthand.tsx +++ b/packages/fluentui/docs/src/examples/components/Chat/Types/ChatMessageExampleStyled.shorthand.tsx @@ -72,7 +72,6 @@ const ChatMessageExampleStyled = () => ( backgroundColor: '#E0FFFF', }), }), - content: { ...slotLabelStyles('content'), backgroundColor: '#F08080' }, timestamp: { ...slotLabelStyles('timestamp', {}, { display: 'inline-block' }), backgroundColor: '#FFFFE0', @@ -90,10 +89,13 @@ const ChatMessageExampleStyled = () => ( backgroundColor: '#FFFFE0', }, }, + ChatMessageContent: { + root: { ...slotLabelStyles('content'), backgroundColor: '#F08080' }, + }, }, componentVariables: { - ChatMessage: siteVars => ({ - content: { + ChatMessageContent: siteVars => ({ + root: { focusOutlineColor: siteVars.colors.red[400], }, }), diff --git a/packages/fluentui/react-northstar-prototypes/src/prototypes/chatMessages/ThreadedMessages/classNames.ts b/packages/fluentui/react-northstar-prototypes/src/prototypes/chatMessages/ThreadedMessages/classNames.ts index 1f566667c5d82..05df9ed8db2b6 100644 --- a/packages/fluentui/react-northstar-prototypes/src/prototypes/chatMessages/ThreadedMessages/classNames.ts +++ b/packages/fluentui/react-northstar-prototypes/src/prototypes/chatMessages/ThreadedMessages/classNames.ts @@ -4,13 +4,14 @@ import { chatItemSlotClassNames, chatItemClassName, chatMessageClassName, + chatMessageContentClassName, } from '@fluentui/react-northstar'; const classNames = { threadedMessage: { thread: `${chatMessageClassName}__thread`, threadBody: `${chatMessageClassName}__thread-body`, - innerContent: `${chatMessageSlotClassNames.content}-inner`, + innerContent: `${chatMessageContentClassName}-inner`, author: `${chatMessageSlotClassNames.author}-inner`, timestamp: `${chatMessageSlotClassNames.timestamp}-inner`, }, diff --git a/packages/fluentui/react-northstar/src/components/Chat/ChatMessage.tsx b/packages/fluentui/react-northstar/src/components/Chat/ChatMessage.tsx index 01b32a32f448a..32ad0a8768500 100644 --- a/packages/fluentui/react-northstar/src/components/Chat/ChatMessage.tsx +++ b/packages/fluentui/react-northstar/src/components/Chat/ChatMessage.tsx @@ -17,6 +17,7 @@ import { useUnhandledProps, useMergedRefs, ForwardRefWithAs, + mergeVariablesOverrides, } from '@fluentui/react-bindings'; import { Ref } from '@fluentui/react-component-ref'; import * as customPropTypes from '@fluentui/react-proptypes'; @@ -67,6 +68,7 @@ import { ChatItemContext } from './chatItemContext'; import { ChatMessageDetails, ChatMessageDetailsProps } from './ChatMessageDetails'; import { ChatMessageHeader, ChatMessageHeaderProps } from './ChatMessageHeader'; import { ChatMessageReadStatus, ChatMessageReadStatusProps } from './ChatMessageReadStatus'; +import { ChatMessageContent } from './ChatMessageContent'; export interface ChatMessageSlotClassNames { actionMenu: string; @@ -77,7 +79,6 @@ export interface ChatMessageSlotClassNames { bubbleInset: string; body: string; compactBody: string; - content: string; reactionGroup: string; timestamp: string; } @@ -226,7 +227,6 @@ export const chatMessageSlotClassNames: ChatMessageSlotClassNames = { bubble: `${chatMessageClassName}__bubble`, bubbleInset: `${chatMessageClassName}__bubble-inset`, compactBody: `${chatMessageClassName}__compact-body`, - content: `${chatMessageClassName}__content`, reactionGroup: `${chatMessageClassName}__reactions`, timestamp: `${chatMessageClassName}__timestamp`, }; @@ -542,10 +542,10 @@ export const ChatMessage = (React.forwardRef(( }), }); - const messageContent = Box.create(content, { - defaultProps: () => ({ - className: chatMessageSlotClassNames.content, - styles: resolvedStyles.content, + const messageContent = createShorthand(ChatMessageContent, content, { + defaultProps: () => ({ badgePosition, density, failed, hasBadge: !!badge, mine, unstable_layout: layout }), + overrideProps: predefinedProps => ({ + variables: mergeVariablesOverrides(variables, predefinedProps.variables), }), }); diff --git a/packages/fluentui/react-northstar/src/components/Chat/ChatMessageContent.tsx b/packages/fluentui/react-northstar/src/components/Chat/ChatMessageContent.tsx new file mode 100644 index 0000000000000..8ca7fc7fdc1e3 --- /dev/null +++ b/packages/fluentui/react-northstar/src/components/Chat/ChatMessageContent.tsx @@ -0,0 +1,56 @@ +import { compose } from '@fluentui/react-bindings'; +import * as PropTypes from 'prop-types'; + +import { commonPropTypes } from '../../utils'; +import { Box, BoxProps, BoxStylesProps } from '../Box/Box'; +import { ChatDensity } from './chatDensity'; +import { ChatMessageLayout, ChatMessageProps } from './ChatMessage'; + +export interface ChatMessageContentOwnProps + extends Pick { + /** Indicates whether parent ChatMessage has badge. */ + hasBadge?: boolean; +} +export interface ChatMessageContentProps extends ChatMessageContentOwnProps, BoxProps {} + +export type ChatMessageContentStylesProps = Required< + Pick +> & { + layout: ChatMessageLayout; +}; +export const chatMessageContentClassName = 'ui-chat__messagecontent'; + +/** + * A ChatMessageContent provides a slot for content in the ChatMessage. + */ +export const ChatMessageContent = compose< + 'div', + ChatMessageContentOwnProps, + ChatMessageContentStylesProps, + BoxProps, + BoxStylesProps +>(Box, { + className: chatMessageContentClassName, + displayName: 'ChatMessageContent', + handledProps: ['badgePosition', 'density', 'failed', 'hasBadge', 'mine', 'unstable_layout'], + mapPropsToStylesProps: ({ badgePosition, density, failed, hasBadge, mine, unstable_layout }) => ({ + badgePosition, + density, + failed, + hasBadge, + layout: unstable_layout, + mine, + }), + overrideStyles: true, + shorthandConfig: { mappedProp: 'content' }, +}); + +ChatMessageContent.propTypes = { + ...commonPropTypes.createCommon(), + badgePosition: PropTypes.oneOf(['start', 'end']), + density: PropTypes.oneOf(['comfy', 'compact']), + failed: PropTypes.bool, + hasBadge: PropTypes.bool, + mine: PropTypes.bool, + unstable_layout: PropTypes.oneOf(['default', 'refresh']), +}; diff --git a/packages/fluentui/react-northstar/src/index.ts b/packages/fluentui/react-northstar/src/index.ts index d635199fdb0b9..2e278a33074db 100644 --- a/packages/fluentui/react-northstar/src/index.ts +++ b/packages/fluentui/react-northstar/src/index.ts @@ -61,6 +61,7 @@ export * from './components/Breadcrumb/BreadcrumbLink'; export * from './components/Chat/Chat'; export * from './components/Chat/ChatItem'; export * from './components/Chat/ChatMessage'; +export * from './components/Chat/ChatMessageContent'; export * from './components/Chat/ChatMessageDetails'; export * from './components/Chat/ChatMessageHeader'; export * from './components/Chat/ChatMessageReadStatus'; diff --git a/packages/fluentui/react-northstar/src/themes/teams/componentStyles.ts b/packages/fluentui/react-northstar/src/themes/teams/componentStyles.ts index 58d3ec740ef79..e5508657092a9 100644 --- a/packages/fluentui/react-northstar/src/themes/teams/componentStyles.ts +++ b/packages/fluentui/react-northstar/src/themes/teams/componentStyles.ts @@ -32,6 +32,7 @@ export { breadcrumbStyles as Breadcrumb } from './components/Breadcrumb/breadcru export { chatStyles as Chat } from './components/Chat/chatStyles'; export { chatItemStyles as ChatItem } from './components/Chat/chatItemStyles'; export { chatMessageStyles as ChatMessage } from './components/Chat/chatMessageStyles'; +export { chatMessageContentStyles as ChatMessageContent } from './components/Chat/chatMessageContentStyles'; export { chatMessageDetailsStyles as ChatMessageDetails } from './components/Chat/chatMessageDetailsStyles'; export { chatMessageReadStatusStyles as ChatMessageReadStatus } from './components/Chat/chatMessageReadStatusStyles'; diff --git a/packages/fluentui/react-northstar/src/themes/teams/componentVariables.ts b/packages/fluentui/react-northstar/src/themes/teams/componentVariables.ts index e86c403cf2abb..455b5f6e49e59 100644 --- a/packages/fluentui/react-northstar/src/themes/teams/componentVariables.ts +++ b/packages/fluentui/react-northstar/src/themes/teams/componentVariables.ts @@ -31,6 +31,7 @@ export { breadcrumbDividerVariables as BreadcrumbDivider } from './components/Br export { chatVariables as Chat } from './components/Chat/chatVariables'; export { chatItemVariables as ChatItem } from './components/Chat/chatItemVariables'; export { chatMessageVariables as ChatMessage } from './components/Chat/chatMessageVariables'; +export { chatMessageContentVariables as ChatMessageContent } from './components/Chat/chatMessageContentVariables'; export { chatMessageDetailsVariables as ChatMessageDetails } from './components/Chat/chatMessageDetailsVariables'; export { chatMessageReadStatusVariables as ChatMessageReadStatus } from './components/Chat/chatMessageReadStatusVariables'; diff --git a/packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageContentStyles.ts b/packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageContentStyles.ts new file mode 100644 index 0000000000000..a3268ad71db47 --- /dev/null +++ b/packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageContentStyles.ts @@ -0,0 +1,45 @@ +import { ComponentSlotStylesPrepared, ICSSInJSStyle } from '@fluentui/styles'; + +import { ChatMessageContentStylesProps } from '../../../../components/Chat/ChatMessageContent'; +import { ChatMessageVariables } from './chatMessageVariables'; +import { pxToRem } from '../../../../utils'; + +export const chatMessageContentStyles: ComponentSlotStylesPrepared< + ChatMessageContentStylesProps, + ChatMessageVariables +> = { + root: (componentStyleFunctionParam): ICSSInJSStyle => { + const { props: p, variables: v, theme } = componentStyleFunctionParam; + return { + color: v.contentColor, + display: 'block', + '& a': { + outline: 'none', + color: p.mine ? v.linkColorMine : v.linkColor, + ':focus': { + textDecoration: 'underline', + }, + }, + + ...(p.layout === 'refresh' && + p.density === 'comfy' && { + wordBreak: 'break-word', + wordWrap: 'break-word', + '& a': { + color: 'inherit', + textDecoration: 'underline', + wordBreak: 'break-all', + '&:hover': { textDecorationStyle: 'double' }, + '&:focus': { textDecorationStyle: 'double' }, + }, + ...(p.failed && { + color: theme.siteVariables.colorScheme.default.foreground, + }), + }), + + ...(p.density === 'comfy' && { + ...(p.hasBadge && p.badgePosition === 'end' && { marginRight: pxToRem(4) }), + }), + }; + }, +}; diff --git a/packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageContentVariables.ts b/packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageContentVariables.ts new file mode 100644 index 0000000000000..72a5ef81fee76 --- /dev/null +++ b/packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageContentVariables.ts @@ -0,0 +1 @@ +export { chatMessageVariables as chatMessageContentVariables } from './chatMessageVariables'; diff --git a/packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageStyles.ts b/packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageStyles.ts index dda9be3a5b6fb..3d5f9921d98dd 100644 --- a/packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageStyles.ts +++ b/packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageStyles.ts @@ -114,22 +114,6 @@ export const chatMessageStyles: ComponentSlotStylesPrepared { - const { props: p, variables: v } = componentStyleFunctionParam; - return { - color: v.contentColor, - display: 'block', - '& a': { - outline: 'none', - color: p.mine ? v.linkColorMine : v.linkColor, - ':focus': { - textDecoration: 'underline', - }, - }, - ...getChatMessageVariantStyles(p).content?.(componentStyleFunctionParam), - }; - }, - badge: (componentStyleFunctionParam): ICSSInJSStyle => { const { props: p, variables: v } = componentStyleFunctionParam; return { diff --git a/packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageStylesComfy.ts b/packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageStylesComfy.ts index 05ec82c172efe..89694a2133761 100644 --- a/packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageStylesComfy.ts +++ b/packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageStylesComfy.ts @@ -66,10 +66,6 @@ export const chatMessageStylesComfy: ComponentSlotStylesPrepared ({ - ...(p.hasBadge && p.badgePosition === 'end' && { marginRight: pxToRem(4) }), - }), - reactionGroup: ({ props: p, variables: v }) => ({ marginLeft: v.reactionGroupMarginLeft, ...(p.hasBadge && p.badgePosition === 'end' && { marginRight: pxToRem(2) }), diff --git a/packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageStylesComfyRefresh.ts b/packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageStylesComfyRefresh.ts index 58ce704aebf28..75825264f73bd 100644 --- a/packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageStylesComfyRefresh.ts +++ b/packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageStylesComfyRefresh.ts @@ -160,22 +160,6 @@ export const chatMessageStylesComfyRefresh: ComponentSlotStylesPrepared< }, }), - content: ({ props: p, variables: v, theme }): ICSSInJSStyle => ({ - color: v.contentColor, - wordBreak: 'break-word', - wordWrap: 'break-word', - '& a': { - color: 'inherit', - textDecoration: 'underline', - wordBreak: 'break-all', - '&:hover': { textDecorationStyle: 'double' }, - '&:focus': { textDecorationStyle: 'double' }, - }, - ...(p.failed && { - color: theme.siteVariables.colorScheme.default.foreground, - }), - }), - badge: ({ props: p, variables: v }): ICSSInJSStyle => ({ position: 'relative', top: pxToRem(-5), diff --git a/packages/fluentui/react-northstar/src/themes/teams/types.ts b/packages/fluentui/react-northstar/src/themes/teams/types.ts index 36a9b52bbbb4a..dff2efc58077c 100644 --- a/packages/fluentui/react-northstar/src/themes/teams/types.ts +++ b/packages/fluentui/react-northstar/src/themes/teams/types.ts @@ -29,6 +29,7 @@ import { ButtonStylesProps } from '../../components/Button/Button'; import { ButtonContentStylesProps } from '../../components/Button/ButtonContent'; import { ChatItemStylesProps } from '../../components/Chat/ChatItem'; import { ChatMessageStylesProps } from '../../components/Chat/ChatMessage'; +import { ChatMessageContentStylesProps } from '../../components/Chat/ChatMessageContent'; import { ChatMessageDetailsStylesProps } from '../../components/Chat/ChatMessageDetails'; import { ChatMessageHeaderStylesProps } from '../../components/Chat/ChatMessageHeader'; import { ChatMessageReadStatusStylesProps } from '../../components/Chat/ChatMessageReadStatus'; @@ -144,6 +145,7 @@ export type TeamsThemeStylesProps = { Chat: ChatStylesProps; ChatItem: ChatItemStylesProps; ChatMessage: ChatMessageStylesProps; + ChatMessageContent: ChatMessageContentStylesProps; ChatMessageDetails: ChatMessageDetailsStylesProps; ChatMessageHeader: ChatMessageHeaderStylesProps; ChatMessageReadStatus: ChatMessageReadStatusStylesProps; diff --git a/packages/fluentui/react-northstar/test/specs/components/Chat/ChatMessage-test.tsx b/packages/fluentui/react-northstar/test/specs/components/Chat/ChatMessage-test.tsx index 012f57f9a746c..ffd1a2ee92503 100644 --- a/packages/fluentui/react-northstar/test/specs/components/Chat/ChatMessage-test.tsx +++ b/packages/fluentui/react-northstar/test/specs/components/Chat/ChatMessage-test.tsx @@ -2,8 +2,8 @@ import { handlesAccessibility, implementsShorthandProp, isConformant } from 'tes import { ChatMessage } from 'src/components/Chat/ChatMessage'; import { Text } from 'src/components/Text/Text'; -import { Box } from 'src/components/Box/Box'; import { ChatMessageDetails } from 'src/components/Chat/ChatMessageDetails'; +import { ChatMessageContent } from 'src/components/Chat/ChatMessageContent'; const chatMessageImplementsShorthandProp = implementsShorthandProp(ChatMessage); @@ -19,7 +19,7 @@ describe('ChatMessage', () => { chatMessageImplementsShorthandProp('author', Text); chatMessageImplementsShorthandProp('timestamp', Text); chatMessageImplementsShorthandProp('details', ChatMessageDetails); - chatMessageImplementsShorthandProp('content', Box, { mapsValueToProp: 'children' }); + chatMessageImplementsShorthandProp('content', ChatMessageContent); describe('accessibility', () => { handlesAccessibility(ChatMessage); diff --git a/packages/fluentui/react-northstar/test/specs/components/Chat/ChatMessageContent-test.tsx b/packages/fluentui/react-northstar/test/specs/components/Chat/ChatMessageContent-test.tsx new file mode 100644 index 0000000000000..ee51892604056 --- /dev/null +++ b/packages/fluentui/react-northstar/test/specs/components/Chat/ChatMessageContent-test.tsx @@ -0,0 +1,9 @@ +import { ChatMessageContent } from 'src/components/Chat/ChatMessageContent'; +import { isConformant } from 'test/specs/commonTests'; + +describe('ChatMessageContent', () => { + isConformant(ChatMessageContent, { + testPath: __filename, + constructorName: 'ChatMessageContent', + }); +});