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
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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}
styles={{
padding: 0,
marginLeft: '10px',
backgroundColor: '#f3f2f1',
fontSize: '14px',
}}
/>
</Ref>
</>
)
}
}

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

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

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

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

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

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

const renderItems = () => {
return _.map(groupControlMessageItems, item => {
return {
content: <ControlMessage message={item} />,
styles: { padding: 0, display: 'block', minHeight: '25px' },
}
})
}

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={overridenChatBehavior}
items={renderItems()}
aria-label={'control messages'}
/>
) : (
<ControlMessage focused={focused} message={controlMessage} />
)}
</Flex>
)
}

export default GroupControlMessages

const groupControlMessageItems = [
{
key: 'joe-doe1',
content: (
<div>
<a href="/">John Doe</a> has added <a href="/">Jane Doe1</a> to the team
</div>
),
},
{
key: 'joe-doe2',
content: (
<div>
<a href="/">John Doe</a> has added <a href="/">Jane Doe2</a> to the team
</div>
),
},
{
key: 'joe-doe3',
content: (
<div>
<a href="/">John Doe</a> has added <a href="/">Jane Doe3</a> to the team
</div>
),
},
]

const controlMessage = {
content: (
<div>
<a href="/">John Doe</a> has added <a href="/">Jane Doe1</a> and 2 other to the team
</div>
),
}
84 changes: 84 additions & 0 deletions docs/src/prototypes/chatMessages/ControlMessages/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import * as React from 'react'
import { Chat, ChatItemProps, ShorthandCollection, Avatar, Divider } from '@stardust-ui/react'
import GroupControlMessages from './GroupControlMessages'
import ControlMessage from './ControlMessage'

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

const ChatExample = () => {
const items: ShorthandCollection<ChatItemProps> = [
{
message: {
content: (
// Adding control message
<ControlMessage
icon={true}
message={{
content: (
<div>
<a href="/">John Doe</a> joined the team
</div>
),
}}
/>
),
styles: {
marginLeft: '16px',
},
},
},
{
message: {
content: (
// Adding Grouped control messages
<GroupControlMessages />
),
styles: {
marginLeft: 0,
},
},
},
{
gutter: {
content: <Avatar {...janeAvatar} />,
},
message: {
content: (
<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: {
content: (
<Chat.Message
content="Ok, let's go."
author="John Doe"
timestamp="Today, 11:15 PM"
mine
/>
),
},
contentPosition: 'end',
key: 'message-id-10',
},
]
return <Chat items={items} />
}

export default ChatExample
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