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

feat(Chat): Add expandable control messages prototype #1765

Merged
merged 12 commits into from
Aug 12, 2019
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

### Documentation
- Restore docs for `Ref` component @layershifter ([#1777](https://github.com/stardust-ui/react/pull/1777))
- Add prototype for expandable control messages in `Chat` @sophieH29 ([#1765](https://github.com/stardust-ui/react/pull/1765))

<!--------------------------------[ v0.36.1 ]------------------------------- -->
## [v0.36.1](https://github.com/stardust-ui/react/tree/v0.36.1) (2019-08-09)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as React from 'react'
import { Ref, ChatMessageProps, Chat, Icon } from '@stardust-ui/react'

interface ControlMessageProps {
focused?: boolean
icon?: boolean
message: ChatMessageProps
}
class ControlMessage extends React.Component<ControlMessageProps> {
messageRef = React.createRef<HTMLElement>()

componentDidMount() {
if (this.props.focused && this.messageRef) {
this.messageRef.current.focus()
}
}

render() {
return (
<>
{this.props.icon ? <Icon name="participant-add" /> : null}
<Ref innerRef={this.messageRef}>
<Chat.Message {...this.props.message} className="control-message" />
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's follow the convention for the other stardust component's classNames. It should be something like ui-chat__message_control

</Ref>
</>
)
}
}

export default ControlMessage
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import * as React from 'react'
import * as _ from 'lodash'
import * as keyboardKey from 'keyboard-key'

import { List, ChatMessageProps, Flex, Icon } from '@stardust-ui/react'
import ControlMessage from './ControlMessage'
import controlMessagesGroupBehavior from './controlMessagesGroupBehavior'

type GroupControlMessagesProps = {
items: ChatMessageProps[]
mainMessage: ChatMessageProps
}

const GroupControlMessages = (props: GroupControlMessagesProps) => {
const [expanded, setExpanded] = React.useState(false)
const [focused, setFocused] = React.useState(false)

const renderItems = () => {
const { items } = props
return _.map(items, (item, index) => {
return {
content: <ControlMessage message={item} />,
key: `control-message-${index}`,
}
})
}

return (
<Flex
onKeyDown={e => {
const eventCode = keyboardKey.getCode(e)
if (eventCode === keyboardKey.Enter) {
setExpanded(true)
}
if (eventCode === keyboardKey.Escape) {
setExpanded(false)
setFocused(true)
}
}}
>
<Icon
name={expanded ? 'stardust-arrow-down' : 'stardust-arrow-end'}
onClick={() => setExpanded(!expanded)}
/>
<Icon name="participant-add" />
{expanded ? (
<List
accessibility={controlMessagesGroupBehavior}
items={renderItems()}
aria-label={'control messages'}
/>
) : (
<ControlMessage focused={focused} message={props.mainMessage} />
)}
</Flex>
)
}

export default GroupControlMessages
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { chatBehavior, Accessibility } from '@stardust-ui/react'

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

behaviorData.attributes.root = {
...behaviorData.attributes.root,
'data-is-focusable': true,
}

behaviorData.focusZone.props = {
...behaviorData.focusZone.props,
shouldFocusOnMount: true,
shouldFocusInnerElementWhenReceivedFocus: true,
defaultTabbableElement: undefined,
}
return behaviorData
}

export default controlMessagesGroupBehavior
111 changes: 111 additions & 0 deletions docs/src/prototypes/chatMessages/ControlMessages/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import * as React from 'react'
import {
Chat,
ChatItemProps,
ShorthandCollection,
Avatar,
Divider,
Provider,
} from '@stardust-ui/react'
import GroupControlMessages from './GroupControlMessages'
import ControlMessage from './ControlMessage'
import { groupControlMessageItems, mainControlMessage } from './mockData'

const janeAvatar = {
image: 'public/images/avatar/small/ade.jpg',
status: {
color: 'green',
icon: 'check',
},
}

const ChatExampleWithControlMessages = () => {
const items: ShorthandCollection<ChatItemProps> = [
{
message: (
// Adding control message
<ControlMessage
icon={true}
message={{
content: (
<div>
<a href="/">John Doe</a> joined the team
</div>
),
}}
/>
),
className: 'control-message-item',
key: 'message-id-6',
},
{
// Adding Grouped control messages
message: (
<GroupControlMessages items={groupControlMessageItems} mainMessage={mainControlMessage} />
),
className: 'group-message-item',
key: 'message-id-7',
},
{
gutter: <Avatar {...janeAvatar} />,
message: (
<Chat.Message
content="Sure! Let's try it."
author="Jane Doe"
timestamp="Yesterday, 10:15 PM"
/>
),
key: 'message-id-8',
},
{
children: <Divider content="Today" color="brand" important />,
key: 'message-id-9',
},
{
message: (
<Chat.Message content="Ok, let's go." author="John Doe" timestamp="Today, 11:15 PM" mine />
),
contentPosition: 'end',
key: 'message-id-10',
},
]
return (
<Provider
theme={{
componentStyles: {
ChatItem: {
root: {
'&.group-message-item .ui-chat__item__message': {
marginLeft: 0,
},
'&.control-message-item .ui-chat__item__message': {
marginLeft: '16px',
},
},
},
ChatMessage: {
root: ({ props: p, theme: { siteVariables } }) => ({
'&.control-message': {
padding: 0,
marginLeft: '10px',
backgroundColor: siteVariables.colors.grey[100],
fontSize: '14px',
},
}),
},
ListItem: {
root: {
padding: 0,
display: 'block',
minHeight: '25px',
},
},
},
}}
>
<Chat items={items} />
</Provider>
)
}

export default ChatExampleWithControlMessages
34 changes: 34 additions & 0 deletions docs/src/prototypes/chatMessages/ControlMessages/mockData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as React from 'react'
import { ChatMessageProps } from '@stardust-ui/react'

export const groupControlMessageItems: ChatMessageProps[] = [
{
content: (
<div>
<a href="/">John Doe</a> has added <a href="/">Jane Doe1</a> to the team
</div>
),
},
{
content: (
<div>
<a href="/">John Doe</a> has added <a href="/">Jane Doe2</a> to the team
</div>
),
},
{
content: (
<div>
<a href="/">John Doe</a> has added <a href="/">Jane Doe3</a> to the team
</div>
),
},
]

export const mainControlMessage: ChatMessageProps = {
content: (
<div>
<a href="/">John Doe</a> has added <a href="/">Jane Doe1</a> and 2 other to the team
</div>
),
}
4 changes: 4 additions & 0 deletions docs/src/prototypes/chatMessages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react'
import { PrototypeSection, ComponentPrototype } from '../Prototypes'
import ImportantAndMentionMessages from './ImportantAndMentionMessages'
import ChatMessageWithPopover from './ChatMessageWithPopover'
import ControlMessages from './ControlMessages'

export default () => (
<PrototypeSection title="Chat messages">
Expand All @@ -17,5 +18,8 @@ export default () => (
>
<ImportantAndMentionMessages />
</ComponentPrototype>
<ComponentPrototype title="Control messages" description="Control messages example">
<ControlMessages />
</ComponentPrototype>
</PrototypeSection>
)
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const chatMessageBehavior: Accessibility = () => ({
attributes: {
root: {
[IS_FOCUSABLE_ATTRIBUTE]: true,
tabIndex: -1,
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this change for the prototyping or a real fix?

Copy link
Contributor Author

@sophieH29 sophieH29 Aug 9, 2019

Choose a reason for hiding this comment

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

it's additional safety thing. Helps both for prototype and overall behavior. Chat Message should be always focusable, meaning - to have set tabIndex=-1. When component re-renders based on state change, FocusZone won't pick this change up unless you move arrow keys, so the chat message will be rendered without tabIndex - thus not focusable

},
},
focusZone: {
Expand Down