Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BREAKING-CHANGE: new ChatMessageContent for style caching #24691

Merged
merged 21 commits into from
Sep 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
YuanboXue-Amber marked this conversation as resolved.
Show resolved Hide resolved
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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davezuko Is this how we're styling links in TMP?
If we don't use it, let's just remove it here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In TMP it looks like links are colored the same for both my/their messages, so this doesn't seem necessary.

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