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

fix(ChatMessage): action menu doesn't disappear on click #1590

Merged
merged 42 commits into from
Jul 11, 2019
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
c4bc9b5
wip
Jul 8, 2019
193bf23
-reverted change
Jul 8, 2019
4db446f
-moved to variables
Jul 8, 2019
12c6094
-reverted
Jul 8, 2019
3ef3f15
-cleaning up
Jul 8, 2019
ff1c50a
-added reactions in the prototype and fixed issue
Jul 9, 2019
55a34be
fixed second click on ellipses not closing the action menu
Jul 9, 2019
ab3b51b
-fixed issue with conflicting state between the chat messages
Jul 9, 2019
9bf25d9
-fixed issue with renamed variable
Jul 9, 2019
f19337f
-used onOpenMenuChanged to handle the new variable
Jul 9, 2019
046d772
wip
Jul 10, 2019
6362219
removed popupAutoFocus behavior from reactions
Jul 10, 2019
f54d294
-updated chat menu variables type
Jul 10, 2019
f42ba57
-removed unnecessary code from the Popover
Jul 10, 2019
95f781d
-removed unnecessary code from the Popover
Jul 10, 2019
a998ff0
-simplified reactions
Jul 10, 2019
f2fa7ee
-moved reactions outside of components function
Jul 10, 2019
c284720
-improved message state
Jul 10, 2019
5f5d2ff
renamed chat message open variable to actionMenuOpened
Jul 10, 2019
c56f7be
-everything resolved
Jul 10, 2019
98bd8b7
-renamed variable
Jul 10, 2019
4c9f2fd
Merge branch 'master' into fix/chat-message-action-menu
mnajdova Jul 10, 2019
a643bf9
-updated changelog
Jul 10, 2019
afe0a82
-addressing PR comments
Jul 10, 2019
3d6cb81
-keyboard event fixes
Jul 10, 2019
bd52c87
-fixed focusing chat message on actionable element clicked
Jul 10, 2019
ed18706
-reverted TeamsChatMessage to be a FC
Jul 10, 2019
cf811fb
-added keydown check
Jul 10, 2019
55e13fc
-renamed props on Popover
Jul 10, 2019
9d47dbb
-added handleBlur
Jul 10, 2019
66abbf8
-added comment
Jul 10, 2019
b242fc2
Merge branch 'master' into fix/chat-message-action-menu
mnajdova Jul 11, 2019
78560cb
-fixed action menu example
Jul 11, 2019
aef3c4e
-removed unnecessary example in the ChatMessage prototypes
Jul 11, 2019
08a4cfd
-added handling of null values for the showActionMenu variable
Jul 11, 2019
cf97f60
-improved logic in the styles for action menu
Jul 11, 2019
a88464e
-replaced visibility with opacity + width + overflow
Jul 11, 2019
e191944
-added comment
Jul 11, 2019
6917a17
Merge branch 'master' into fix/chat-message-action-menu
mnajdova Jul 11, 2019
82b432c
-renamed chatmessage ref to chat message element
Jul 11, 2019
7ec8607
Merge remote-tracking branch 'origin/fix/chat-message-action-menu' in…
Jul 11, 2019
c3b6ad0
-added comment for opacity vs visibility
Jul 11, 2019
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Removed obsolete `play-sound-mute` icon from Teams theme @amramornov-ms ([#1598](https://github.com/stardust-ui/react/pull/1598))
- Add svg pointing beak to the `Tooltip` component in Teams theme @mnajdova ([#1580](https://github.com/stardust-ui/react/pull/1580))
- Add new values to the `brand`, `onyx` colors and `background4` token for default and brand color schemes in Teams theme @mnajdova ([#1581](https://github.com/stardust-ui/react/pull/1581))
- Add additional logic for showing/hiding the `actionMenu` inside the `ChatMessage` in Teams theme, based on a variable @mnajdova ([#1590](https://github.com/stardust-ui/react/pull/1590))

### Documentation
- Ensure docs content doesn't overlap with sidebar @kuzhelov ([#1568](https://github.com/stardust-ui/react/pull/1568))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,124 +1,158 @@
import { Chat, Provider, Avatar } from '@stardust-ui/react'
import { Chat, Provider, Avatar, ChatMessageProps } from '@stardust-ui/react'
import * as React from 'react'
import Popover from './Popover'
import ReactionPopup from './ReactionPopup'
import { Ref } from '@stardust-ui/react-component-ref'

const reactions = [
{
icon: 'thumbs up',
content: '1K',
key: 'likes',
variables: { meReacting: true },
},
{
icon: 'thumbs down',
content: 2,
key: 'dislikes',
},
]

const reactionsWithPopup = reactions.map(reaction => render =>
render(reaction, (Component, props) => <ReactionPopup {...props} />),
)

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

const ChatWithPopover = () => (
<Provider
theme={{
componentStyles: {
ChatMessage: {
root: ({ props: p, theme: { siteVariables } }) => ({
'& a': {
color: siteVariables.colors.brand[600],
},
}),
},
Menu: {
root: {
background: '#fff',
transition: 'opacity 0.2s',
position: 'absolute',
const ChatWithPopover = () => {
return (
<Provider
theme={{
componentStyles: {
ChatMessage: {
root: ({ props: p, theme: { siteVariables } }) => ({
'& a': {
color: siteVariables.colors.brand[600],
},
}),
},
Menu: {
root: {
background: '#fff',
transition: 'opacity 0.2s',
position: 'absolute',

'& a:focus': {
textDecoration: 'none',
color: 'inherit',
},
'& a': {
color: 'inherit',
},
'& a:focus': {
textDecoration: 'none',
color: 'inherit',
},
'& a': {
color: 'inherit',
},

'& .smile-emoji': {
position: 'absolute',
opacity: 0,
zIndex: -1,
},
'& .smile-emoji': {
position: 'absolute',
opacity: 0,
zIndex: -1,
},

'&.focused .smile-emoji': {
position: 'initial',
zIndex: 'initial',
opacity: 1,
},
'&.focused .smile-emoji': {
position: 'initial',
zIndex: 'initial',
opacity: 1,
},

'&:hover .smile-emoji': {
position: 'initial',
zIndex: 'initial',
opacity: 1,
'&:hover .smile-emoji': {
position: 'initial',
zIndex: 'initial',
opacity: 1,
},
},
},
},
},
}}
>
<Chat
items={[
{
key: 'a',
message: {
content: (
<Chat.Message
actionMenu={<Popover />}
author="Jane Doe"
content={{
content: (
<div>
<a href="/">Link</a> Hover me to see the actions <a href="/">Some Link</a>
</div>
),
}}
timestamp="Yesterday, 10:15 PM"
/>
),
},
gutter: { content: <Avatar {...janeAvatar} /> },
},
{
key: 'b',
message: {
content: (
<Chat.Message
actionMenu={<Popover />}
author="Jane Doe"
content={{
content: (
<div>
<a href="/">Link</a> Hover me to see the actions <a href="/">Some Link</a>
</div>
),
}}
timestamp="Yesterday, 10:15 PM"
/>
),
}}
>
<Chat
items={[
{
key: 'a',
message: {
content: (
<TeamsChatMessage
author="Jane Doe"
content={{
content: (
<div>
<a href="/">Link</a> Hover me to see the actions <a href="/">Some Link</a>
</div>
),
}}
reactionGroup={{
items: reactionsWithPopup,
}}
timestamp="Yesterday, 10:15 PM"
/>
),
},
gutter: { content: <Avatar {...janeAvatar} /> },
},
gutter: { content: <Avatar {...janeAvatar} /> },
},
{
key: 'c',
message: {
content: (
<Chat.Message
actionMenu={<Popover />}
author="Jane Doe"
content={{
content: (
<div>
<a href="/">Link</a> Hover me to see the actions <a href="/">Some Link</a>
</div>
),
}}
timestamp="Yesterday, 10:15 PM"
/>
),
{
key: 'b',
message: {
content: (
<TeamsChatMessage
author="Jane Doe"
content={{
content: (
<div>
<a href="/">Link</a> Hover me to see the actions <a href="/">Some Link</a>
</div>
),
}}
reactionGroup={{
items: reactionsWithPopup,
}}
timestamp="Yesterday, 10:15 PM"
/>
),
},
gutter: { content: <Avatar {...janeAvatar} /> },
},
gutter: { content: <Avatar {...janeAvatar} /> },
},
]}
/>
</Provider>
)
]}
/>
</Provider>
)
}

const TeamsChatMessage: React.FC<ChatMessageProps> = (props: ChatMessageProps) => {
const [showActionMenu, setShowActionMenu] = React.useState(false)
const [forceShowActionMenu, setForceShowActionMenu] = React.useState(false)
const [chatMessageRef, setChatMessageRef] = React.useState<HTMLElement>(null)

const handleBlur = e => !e.currentTarget.contains(e.relatedTarget) && setShowActionMenu(false)

return (
<Ref innerRef={setChatMessageRef}>
<Chat.Message
{...props}
actionMenu={
<Popover
chatMessageRef={chatMessageRef}
onForceShowActionMenuChange={setForceShowActionMenu}
onShowActionMenuChange={setShowActionMenu}
/>
}
onMouseEnter={() => setShowActionMenu(true)}
onMouseLeave={() => !forceShowActionMenu && setShowActionMenu(false)}
onFocus={() => setShowActionMenu(true)}
onBlur={handleBlur}
variables={{ showActionMenu }}
/>
</Ref>
)
}

export default ChatWithPopover
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import cx from 'classnames'

export interface PopoverProps {
className?: string
onForceShowActionMenuChange?: (val: boolean) => void
onShowActionMenuChange?: (val: boolean) => void
chatMessageRef?: HTMLElement
mnajdova marked this conversation as resolved.
Show resolved Hide resolved
}

interface PopoverState {
Expand Down Expand Up @@ -31,10 +34,20 @@ class Popover extends React.Component<PopoverProps, PopoverState> {
this.setState({ focused: e.currentTarget.contains(e.relatedTarget) })
}

handleActionableItemClick = e => {
const { onShowActionMenuChange, chatMessageRef } = this.props
onShowActionMenuChange(false)
// Currently when the action menu is closed because of some actionable item is clicked, we focus the ChatMessage
// this was not in the spec, so it may be changed if the requirement is different
e.type === 'keydown' && chatMessageRef && chatMessageRef.focus()
}

render() {
const { onShowActionMenuChange, onForceShowActionMenuChange, ...rest } = this.props
delete rest.chatMessageRef
kuzhelov marked this conversation as resolved.
Show resolved Hide resolved
return (
<Menu
{...this.props}
{...rest}
accessibility={popoverBehavior}
iconOnly
className={cx(this.props.className, this.state.focused ? 'focused' : '')}
Expand All @@ -44,27 +57,35 @@ class Popover extends React.Component<PopoverProps, PopoverState> {
icon: 'smile',
className: 'smile-emoji',
'aria-label': 'smile one',
onClick: this.handleActionableItemClick,
},
{
key: 'smile2',
icon: 'smile',
className: 'smile-emoji',
'aria-label': 'smile two',
onClick: this.handleActionableItemClick,
},
{
key: 'smile3',
icon: 'smile',
className: 'smile-emoji',
'aria-label': 'smile three',
onClick: this.handleActionableItemClick,
},
{
key: 'a',
icon: 'thumbs up',
'aria-label': 'thumbs up',
onClick: this.handleActionableItemClick,
},
{
key: 'c',
icon: 'ellipsis horizontal',
onMenuOpenChange: (e, { menuOpen }) => {
onShowActionMenuChange(true)
onForceShowActionMenuChange(menuOpen)
},
'aria-label': 'more options',
indicator: false,
menu: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ const getAriaLabel = ({ content: numberOfPersons, icon: emojiType }: ReactionPro
return `${numberOfPersons} people reacted this message with a ${emojiType} emoji. Open menu to see people who reacted.`
}

class ReactionPopup extends React.Component<ReactionProps, any> {
class ReactionPopup extends React.Component<ReactionProps, { open: boolean }> {
state = {
open: false,
}

handleKeyDownOnMenu = e => {
if ((e.shiftKey && e.keyCode === keyboardKey.Tab) || e.keyCode === keyboardKey.Tab) {
if (e.keyCode === keyboardKey.Tab) {
mnajdova marked this conversation as resolved.
Show resolved Hide resolved
this.setState({ open: false })
}
}
Expand All @@ -35,16 +35,14 @@ class ReactionPopup extends React.Component<ReactionProps, any> {
aria-label={getAriaLabel(this.props)}
/>
}
content={{
content: (
<Menu
items={['Jane Doe', 'John Doe']}
vertical
variables={{ borderColor: 'transparent' }}
onKeyDown={this.handleKeyDownOnMenu}
/>
),
}}
content={
<Menu
items={['Jane Doe', 'John Doe']}
vertical
variables={{ borderColor: 'transparent' }}
onKeyDown={this.handleKeyDownOnMenu}
/>
}
inline
on="hover"
open={this.state.open}
Expand Down
Loading