Skip to content
This repository has been archived by the owner on Mar 4, 2020. It is now read-only.

chore(Chat): move to functional component #2366

Merged
merged 11 commits into from
Feb 18, 2020
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Styles and variables from `ProviderBox` are moved to `Provider` @layershifter ([#2192](https://github.com/microsoft/fluent-ui-react/pull/2192))
- Fix an issue with `offset` and `pointing` in `Popup` @layershifter ([#2361](https://github.com/microsoft/fluent-ui-react/pull/2361))
- Restricted prop set in the `TooltipContent` component which is passed to styles functions @layershifter ([#2357](https://github.com/microsoft/fluent-ui-react/pull/2357))
- Restricted prop sets in the `Chat`, `ChatItem`, `ChatMessage` components which are passed to styles functions @layershifter ([#2366](https://github.com/microsoft/fluent-ui-react/pull/2366))

### Fixes
- Remove dependency on Lodash in TypeScript typings @layershifter ([#2323](https://github.com/microsoft/fluent-ui-react/pull/2323))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { chatBehavior, Accessibility } from '@fluentui/react'
import { chatBehavior, Accessibility, ChatBehaviorProps } from '@fluentui/react'

const controlMessagesGroupBehavior: Accessibility<any> = props => {
const controlMessagesGroupBehavior: Accessibility<ChatBehaviorProps> = props => {
const behaviorData = chatBehavior(props)

behaviorData.attributes.root = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { chatBehavior, Accessibility } from '@fluentui/react'
import { chatBehavior, Accessibility, ChatBehaviorProps } from '@fluentui/react'
import classNames from './classNames'

const threadChatBehavior: Accessibility = props => {
const threadChatBehavior: Accessibility<ChatBehaviorProps> = props => {
const behaviorData = chatBehavior(props)

behaviorData.focusZone.props = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { chatMessageBehavior, Accessibility, FocusZoneTabbableElements } from '@fluentui/react'
import {
chatMessageBehavior,
Accessibility,
FocusZoneTabbableElements,
ChatMessageBehaviorProps,
} from '@fluentui/react'
import * as keyboardKey from 'keyboard-key'

const threadedMessageBehavior: Accessibility = props => {
const threadedMessageBehavior: Accessibility<ChatMessageBehaviorProps> = props => {
const behaviorData = chatMessageBehavior(props)

behaviorData.focusZone.props = {
Expand Down
4 changes: 3 additions & 1 deletion packages/accessibility/src/behaviors/Chat/chatBehavior.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const CHAT_FOCUSZONE_ATTRIBUTE = 'chat-focuszone'
* Focused active element of the component is reset when TAB from the component.
* Focus can be moved inside a child component with embeded inner FocusZone by pressing a specified key.
*/
const ChatBehavior: Accessibility = () => ({
const ChatBehavior: Accessibility<ChatBehaviorProps> = () => ({
attributes: {
root: {},
},
Expand All @@ -43,4 +43,6 @@ const getLastTabbableElement = (root: HTMLElement): HTMLElement => {
: null
}

export type ChatBehaviorProps = never

export default ChatBehavior
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { FocusZoneTabbableElements, FocusZoneDirection } from '../../focusZone/t
* Keyboard navigation is circular.
* Focus is moved within the focusable children of the component using TAB key.
*/
const chatMessageBehavior: Accessibility = () => ({
const chatMessageBehavior: Accessibility<ChatMessageBehaviorProps> = () => ({
attributes: {
root: {
[IS_FOCUSABLE_ATTRIBUTE]: true,
Expand All @@ -43,4 +43,6 @@ const chatMessageBehavior: Accessibility = () => ({
},
})

export type ChatMessageBehaviorProps = never

export default chatMessageBehavior
2 changes: 2 additions & 0 deletions packages/accessibility/src/behaviors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ export { default as radioGroupBehavior } from './Radio/radioGroupBehavior'
export { default as radioGroupItemBehavior } from './Radio/radioGroupItemBehavior'
export { default as popupBehavior } from './Popup/popupBehavior'
export { default as chatBehavior } from './Chat/chatBehavior'
export * from './Chat/chatBehavior'
export { default as chatMessageBehavior } from './Chat/chatMessageBehavior'
export * from './Chat/chatMessageBehavior'
export { default as gridBehavior } from './Grid/gridBehavior'
export { default as gridHorizontalBehavior } from './Grid/gridHorizontalBehavior'
export { default as hierarchicalTreeBehavior } from './HierarchicalTree/hierarchicalTreeBehavior'
Expand Down
140 changes: 93 additions & 47 deletions packages/react/src/components/Chat/Chat.tsx
Original file line number Diff line number Diff line change
@@ -1,80 +1,126 @@
import { Accessibility, chatBehavior } from '@fluentui/accessibility'
import { Accessibility, chatBehavior, ChatBehaviorProps } from '@fluentui/accessibility'
import {
getElementType,
getUnhandledProps,
useAccessibility,
useStyles,
useTelemetry,
} from '@fluentui/react-bindings'
import * as customPropTypes from '@fluentui/react-proptypes'
import * as _ from 'lodash'
import * as PropTypes from 'prop-types'
import * as React from 'react'
// @ts-ignore
import { ThemeContext } from 'react-fela'

import {
childrenExist,
UIComponent,
ChildrenComponentProps,
commonPropTypes,
createShorthandFactory,
rtlTextContainer,
applyAccessibilityKeyHandlers,
UIComponentProps,
} from '../../utils'
import {
WithAsProp,
withSafeTypeForAs,
ShorthandCollection,
FluentComponentStaticProps,
ProviderContextPrepared,
} from '../../types'
import ChatItem, { ChatItemProps } from './ChatItem'
import ChatMessage from './ChatMessage'
import { WithAsProp, withSafeTypeForAs, ShorthandCollection } from '../../types'
import { UIComponentProps, ChildrenComponentProps } from '../../utils/commonPropInterfaces'

export interface ChatSlotClassNames {
item: string
}

export interface ChatProps extends UIComponentProps, ChildrenComponentProps {
/** Accessibility behavior if overridden by the user. */
accessibility?: Accessibility
accessibility?: Accessibility<ChatBehaviorProps>

/** Shorthand array of the items inside the chat. */
items?: ShorthandCollection<ChatItemProps>
}

class Chat extends UIComponent<WithAsProp<ChatProps>, any> {
static displayName = 'Chat'
export type ChatStylesProps = {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Yeeey!


static className = 'ui-chat'
const Chat: React.FC<WithAsProp<ChatProps>> &
FluentComponentStaticProps<ChatProps> & {
slotClassNames: ChatSlotClassNames
Item: typeof ChatItem
Message: typeof ChatMessage
} = props => {
const context: ProviderContextPrepared = React.useContext(ThemeContext)
const { setStart, setEnd } = useTelemetry(Chat.displayName, context.telemetry)
setStart()

static slotClassNames: ChatSlotClassNames = {
item: `${Chat.className}__item`,
}
const { accessibility, children, className, design, items, styles, variables } = props

static propTypes = {
...commonPropTypes.createCommon({
content: false,
const getA11Props = useAccessibility(accessibility, {
debugName: Chat.displayName,
rtl: context.rtl,
})
const { classes } = useStyles<ChatStylesProps>(Chat.displayName, {
className: Chat.className,
mapPropsToInlineStyles: () => ({
className,
design,
styles,
variables,
}),
items: PropTypes.arrayOf(customPropTypes.itemShorthand),
}

static defaultProps = {
accessibility: chatBehavior,
as: 'ul',
}

static Item = ChatItem
static Message = ChatMessage

renderComponent({ ElementType, classes, accessibility, unhandledProps }) {
const { children, items } = this.props

return (
<ElementType
className={classes.root}
{...accessibility.attributes.root}
{...rtlTextContainer.getAttributes({ forElements: [children] })}
{...unhandledProps}
{...applyAccessibilityKeyHandlers(accessibility.keyHandlers.root, unhandledProps)}
>
{childrenExist(children)
? children
: _.map(items, item =>
ChatItem.create(item, {
defaultProps: () => ({ className: Chat.slotClassNames.item }),
}),
)}
</ElementType>
)
}
rtl: context.rtl,
})

const ElementType = getElementType(props)
const unhandledProps = getUnhandledProps(Chat.handledProps, props)

const element = getA11Props.unstable_wrapWithFocusZone(
<ElementType
{...getA11Props('root', {
className: classes.root,
...rtlTextContainer.getAttributes({ forElements: [children] }),
...unhandledProps,
})}
>
{childrenExist(children)
? children
: _.map(items, item =>
ChatItem.create(item, {
defaultProps: () => ({ className: Chat.slotClassNames.item }),
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't we by convention send here resolvedStyles.item? I hope not, but still, thought I'd ask

Copy link
Member Author

Choose a reason for hiding this comment

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

Discussed offline: let's avoid changes there and keep previous behavior.

}),
)}
</ElementType>,
)
setEnd()

return element
}

Chat.className = 'ui-chat'
Chat.displayName = 'Chat'

Chat.slotClassNames = {
item: `${Chat.className}__item`,
}

Chat.defaultProps = {
accessibility: chatBehavior,
as: 'ul',
}
Chat.propTypes = {
...commonPropTypes.createCommon({
content: false,
}),
items: PropTypes.arrayOf(customPropTypes.itemShorthand),
}
Chat.handledProps = Object.keys(Chat.propTypes) as any

Chat.Item = ChatItem
Chat.Message = ChatMessage

Chat.create = createShorthandFactory({ Component: Chat })

/**
* A Chat displays messages from a conversation between multiple users.
*/
Expand Down
Loading