Skip to content

Commit

Permalink
BREAKING-CHANGE: new ChatMessageContent for style caching (microsoft#…
Browse files Browse the repository at this point in the history
…24691)

* new chat msg content

* fix playground

* export them

* UT

* remove cruft

* fix style and example

* add missing props

* fix unhandled

* chglog

* chglog

* Update packages/fluentui/docs/src/examples/components/Chat/Playground.tsx

Co-authored-by: Oleksandr Fediashov <alexander.mcgarret@gmail.com>

* Update packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageContentStyles.ts

Co-authored-by: Oleksandr Fediashov <alexander.mcgarret@gmail.com>

* apply comment

* remove old classname

* remove old classname

* small prop update

* Update packages/fluentui/react-northstar/src/themes/teams/components/Chat/chatMessageContentStyles.ts

* remove function in chatmsgcontentstyles

* remove function in chatmsgcontentstyles

Co-authored-by: Oleksandr Fediashov <alexander.mcgarret@gmail.com>
  • Loading branch information
2 people authored and NotWoods committed Nov 18, 2022
1 parent 0805271 commit 1ab91a9
Show file tree
Hide file tree
Showing 18 changed files with 144 additions and 61 deletions.
3 changes: 3 additions & 0 deletions packages/fluentui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
@@ -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}`,
};

Expand Down
19 changes: 8 additions & 11 deletions packages/fluentui/docs/src/examples/components/Chat/Playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import {
Avatar,
Chat,
ChatItem,
ChatItemProps,
ChatMessageProps,
MenuProps,
Expand Down Expand Up @@ -126,17 +127,13 @@ const ChatPlayground: React.FunctionComponent = () => {
};

return (
<Chat
density={density}
items={[
{
gutter: <Avatar icon={<PersonIcon />} size={size} />,
key: '1',
message: <Chat.Message mine={mine} reactionGroupPosition={reactionGroupPosition} {...chatMessageProps} />,
...chatItemProps,
},
]}
/>
<Chat density={density}>
<ChatItem
gutter={<Avatar icon={<PersonIcon />} size={size} />}
message={<Chat.Message mine={mine} reactionGroupPosition={reactionGroupPosition} {...chatMessageProps} />}
{...chatItemProps}
/>
</Chat>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ const ChatMessageExampleStyled = () => (
backgroundColor: '#E0FFFF',
}),
}),
content: { ...slotLabelStyles('content'), backgroundColor: '#F08080' },
timestamp: {
...slotLabelStyles('timestamp', {}, { display: 'inline-block' }),
backgroundColor: '#FFFFE0',
Expand All @@ -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],
},
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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`,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
Expand All @@ -77,7 +79,6 @@ export interface ChatMessageSlotClassNames {
bubbleInset: string;
body: string;
compactBody: string;
content: string;
reactionGroup: string;
timestamp: string;
}
Expand Down Expand Up @@ -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`,
};
Expand Down Expand Up @@ -542,10 +542,10 @@ export const ChatMessage = (React.forwardRef<HTMLDivElement, ChatMessageProps>((
}),
});

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),
}),
});

Expand Down
Original file line number Diff line number Diff line change
@@ -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<ChatMessageProps, 'badgePosition' | 'density' | 'failed' | 'mine' | 'unstable_layout'> {
/** Indicates whether parent ChatMessage has badge. */
hasBadge?: boolean;
}
export interface ChatMessageContentProps extends ChatMessageContentOwnProps, BoxProps {}

export type ChatMessageContentStylesProps = Required<
Pick<ChatMessageContentOwnProps, 'badgePosition' | 'density' | 'failed' | 'hasBadge' | 'mine'>
> & {
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<ChatDensity>(['comfy', 'compact']),
failed: PropTypes.bool,
hasBadge: PropTypes.bool,
mine: PropTypes.bool,
unstable_layout: PropTypes.oneOf(['default', 'refresh']),
};
1 change: 1 addition & 0 deletions packages/fluentui/react-northstar/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
Original file line number Diff line number Diff line change
@@ -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) }),
}),
};
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { chatMessageVariables as chatMessageContentVariables } from './chatMessageVariables';
Original file line number Diff line number Diff line change
Expand Up @@ -114,22 +114,6 @@ export const chatMessageStyles: ComponentSlotStylesPrepared<ChatMessageStylesPro
};
},

content: (componentStyleFunctionParam): ICSSInJSStyle => {
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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,6 @@ export const chatMessageStylesComfy: ComponentSlotStylesPrepared<ChatMessageStyl
position: 'absolute',
}),

content: ({ props: p }): ICSSInJSStyle => ({
...(p.hasBadge && p.badgePosition === 'end' && { marginRight: pxToRem(4) }),
}),

reactionGroup: ({ props: p, variables: v }) => ({
marginLeft: v.reactionGroupMarginLeft,
...(p.hasBadge && p.badgePosition === 'end' && { marginRight: pxToRem(2) }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
2 changes: 2 additions & 0 deletions packages/fluentui/react-northstar/src/themes/teams/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -144,6 +145,7 @@ export type TeamsThemeStylesProps = {
Chat: ChatStylesProps;
ChatItem: ChatItemStylesProps;
ChatMessage: ChatMessageStylesProps;
ChatMessageContent: ChatMessageContentStylesProps;
ChatMessageDetails: ChatMessageDetailsStylesProps;
ChatMessageHeader: ChatMessageHeaderStylesProps;
ChatMessageReadStatus: ChatMessageReadStatusStylesProps;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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',
});
});

0 comments on commit 1ab91a9

Please sign in to comment.