allow mark read/unread with unread badge
closes VICE-1128 flag=react_inbox Test Plan: - go to inbox - create a conversation > mark read/unread with unread badge Change-Id: Ifd89ad7705e2f68ce2edf70a6a8ecc4e86e93309 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/273709 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Omar Soto-Fortuño <omar.soto@instructure.com> QA-Review: Omar Soto-Fortuño <omar.soto@instructure.com> Product-Review: Omar Soto-Fortuño <omar.soto@instructure.com>
This commit is contained in:
parent
9c9059c8c4
commit
dfdcd0355e
|
@ -16,15 +16,20 @@
|
|||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {AlertManagerContext} from '@canvas/alerts/react/AlertManager'
|
||||
import I18n from 'i18n!conversations_2'
|
||||
import PropTypes from 'prop-types'
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import React, {useEffect, useState, useContext} from 'react'
|
||||
import {useMutation} from 'react-apollo'
|
||||
import {View} from '@instructure/ui-view'
|
||||
|
||||
import {MessageListItem, conversationProp} from './MessageListItem'
|
||||
import {UPDATE_CONVERSATION_PARTICIPANTS} from '../../../graphql/Mutations'
|
||||
|
||||
export const MessageListHolder = ({...props}) => {
|
||||
const [selectedMessages, setSelectedMessages] = useState([])
|
||||
const [rangeClickStart, setRangeClickStart] = useState()
|
||||
const {setOnFailure, setOnSuccess} = useContext(AlertManagerContext)
|
||||
|
||||
const provideConversationsForOnSelect = conversationIds => {
|
||||
const matchedConversations = props.conversations
|
||||
|
@ -118,6 +123,27 @@ export const MessageListHolder = ({...props}) => {
|
|||
provideConversationsForOnSelect([...updatedSelectedMessage])
|
||||
}
|
||||
|
||||
const [readStateChangeConversationParticipants] = useMutation(UPDATE_CONVERSATION_PARTICIPANTS, {
|
||||
onCompleted(data) {
|
||||
if (data.updateConversationParticipants.errors) {
|
||||
setOnFailure(I18n.t('Read state change operation failed'))
|
||||
} else {
|
||||
setOnSuccess(
|
||||
I18n.t(
|
||||
{
|
||||
one: 'Read state Changed!',
|
||||
other: 'Read states Changed!'
|
||||
},
|
||||
{count: '1000'}
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
onError() {
|
||||
setOnFailure(I18n.t('Read state change failed'))
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<View
|
||||
as="div"
|
||||
|
@ -139,6 +165,7 @@ export const MessageListHolder = ({...props}) => {
|
|||
onSelect={handleItemSelection}
|
||||
onStar={props.onStar}
|
||||
key={conversation._id}
|
||||
readStateChangeConversationParticipants={readStateChangeConversationParticipants}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
|
@ -148,13 +175,15 @@ export const MessageListHolder = ({...props}) => {
|
|||
|
||||
const conversationParticipantsProp = PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
_id: PropTypes.string,
|
||||
workflowState: PropTypes.string,
|
||||
conversation: conversationProp
|
||||
conversation: conversationProp,
|
||||
label: PropTypes.string
|
||||
})
|
||||
|
||||
MessageListHolder.propTypes = {
|
||||
conversations: PropTypes.arrayOf(conversationParticipantsProp),
|
||||
id: PropTypes.number,
|
||||
id: PropTypes.string,
|
||||
onOpen: PropTypes.func,
|
||||
onSelect: PropTypes.func,
|
||||
onStar: PropTypes.func
|
||||
|
|
|
@ -21,7 +21,12 @@ import {Button, IconButton} from '@instructure/ui-buttons'
|
|||
import {Checkbox} from '@instructure/ui-checkbox'
|
||||
import {Focusable} from '@instructure/ui-focusable'
|
||||
import {Grid} from '@instructure/ui-grid'
|
||||
import {IconStarLightLine, IconStarSolid} from '@instructure/ui-icons'
|
||||
import {
|
||||
IconStarLightLine,
|
||||
IconStarSolid,
|
||||
IconEmptyLine,
|
||||
IconEmptySolid
|
||||
} from '@instructure/ui-icons'
|
||||
import PropTypes from 'prop-types'
|
||||
import React, {useState} from 'react'
|
||||
import {ScreenReaderContent} from '@instructure/ui-a11y-content'
|
||||
|
@ -161,17 +166,25 @@ export const MessageListItem = ({...props}) => {
|
|||
<Grid.Row>
|
||||
<Grid.Col width="auto">
|
||||
<View textAlign="center" as="div" width={30} height={30} margin="0 small 0 0">
|
||||
{props.isUnread && (
|
||||
<Badge
|
||||
type="notification"
|
||||
data-testid="unread-badge"
|
||||
standalone
|
||||
margin="x-small"
|
||||
formatOutput={() => {
|
||||
return <ScreenReaderContent>{I18n.t('Unread')}</ScreenReaderContent>
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<IconButton
|
||||
color="primary"
|
||||
data-testid={props.isUnread ? 'unread-badge' : 'read-badge'}
|
||||
margin="x-small"
|
||||
onClick={() =>
|
||||
props.readStateChangeConversationParticipants({
|
||||
variables: {
|
||||
conversationIds: [props.conversation._id],
|
||||
workflowState: props.isUnread ? 'read' : 'unread'
|
||||
}
|
||||
})
|
||||
}
|
||||
screenReaderLabel={props.isUnread ? I18n.t('Unread') : I18n.t('Read')}
|
||||
size="small"
|
||||
withBackground={false}
|
||||
withBorder={false}
|
||||
>
|
||||
{props.isUnread ? <IconEmptySolid /> : <IconEmptyLine />}
|
||||
</IconButton>
|
||||
</View>
|
||||
</Grid.Col>
|
||||
<Grid.Col>{formatParticipants()}</Grid.Col>
|
||||
|
@ -277,7 +290,8 @@ export const conversationProp = PropTypes.shape({
|
|||
subject: PropTypes.string,
|
||||
participants: PropTypes.arrayOf(participantProp),
|
||||
conversationMessages: PropTypes.arrayOf(conversationMessageProp),
|
||||
conversationMessagesConnection: PropTypes.object
|
||||
conversationMessagesConnection: PropTypes.object,
|
||||
conversationParticipantsConnection: PropTypes.object
|
||||
})
|
||||
|
||||
MessageListItem.propTypes = {
|
||||
|
@ -288,5 +302,6 @@ MessageListItem.propTypes = {
|
|||
isUnread: PropTypes.bool,
|
||||
onOpen: PropTypes.func,
|
||||
onSelect: PropTypes.func,
|
||||
onStar: PropTypes.func
|
||||
onStar: PropTypes.func,
|
||||
readStateChangeConversationParticipants: PropTypes.func
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ describe('MessageListItem', () => {
|
|||
const createProps = overrides => {
|
||||
return {
|
||||
conversation: {
|
||||
_id: '191',
|
||||
subject: 'This is the subject line',
|
||||
conversationParticipantsConnection: {
|
||||
nodes: [
|
||||
|
@ -118,8 +119,36 @@ describe('MessageListItem', () => {
|
|||
it('renders the unread badge when the conversation is unread', () => {
|
||||
const props = createProps({isUnread: true})
|
||||
|
||||
const {getByText} = render(<MessageListItem {...props} />)
|
||||
const container = render(<MessageListItem {...props} />)
|
||||
|
||||
expect(getByText('Unread')).toBeInTheDocument()
|
||||
expect(container.getByText('Unread')).toBeInTheDocument()
|
||||
expect(container.getByTestId('unread-badge')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders the read badge when the conversation is read', () => {
|
||||
const props = createProps()
|
||||
|
||||
const container = render(<MessageListItem {...props} />)
|
||||
|
||||
expect(container.getByText('Read')).toBeInTheDocument()
|
||||
expect(container.getByTestId('read-badge')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('update read state called with correct parameters', () => {
|
||||
const changeReadState = jest.fn()
|
||||
|
||||
const props = createProps({readStateChangeConversationParticipants: changeReadState})
|
||||
|
||||
const container = render(<MessageListItem {...props} />)
|
||||
|
||||
const unreadBadge = container.queryByTestId('read-badge')
|
||||
fireEvent.click(unreadBadge)
|
||||
|
||||
expect(changeReadState).toHaveBeenCalledWith({
|
||||
variables: {
|
||||
conversationIds: ['191'],
|
||||
workflowState: 'unread'
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -160,21 +160,6 @@ const MessageListActionContainer = props => {
|
|||
}
|
||||
}
|
||||
|
||||
const handleReadStateComplete = data => {
|
||||
const readStateChangeSuccessMsg = I18n.t(
|
||||
{
|
||||
one: 'Read state Changed!',
|
||||
other: 'Read states Changed!'
|
||||
},
|
||||
{count: props.selectedConversations.length}
|
||||
)
|
||||
if (data.updateConversationParticipants.errors) {
|
||||
setOnFailure(I18n.t('Read state change operation failed'))
|
||||
} else {
|
||||
setOnSuccess(readStateChangeSuccessMsg)
|
||||
}
|
||||
}
|
||||
|
||||
const [archiveConversationParticipants] = useMutation(UPDATE_CONVERSATION_PARTICIPANTS, {
|
||||
update: removeOutOfScopeConversationsFromCache,
|
||||
onCompleted(data) {
|
||||
|
@ -187,7 +172,19 @@ const MessageListActionContainer = props => {
|
|||
|
||||
const [readStateChangeConversationParticipants] = useMutation(UPDATE_CONVERSATION_PARTICIPANTS, {
|
||||
onCompleted(data) {
|
||||
handleReadStateComplete(data)
|
||||
if (data.updateConversationParticipants.errors) {
|
||||
setOnFailure(I18n.t('Read state change operation failed'))
|
||||
} else {
|
||||
setOnSuccess(
|
||||
I18n.t(
|
||||
{
|
||||
one: 'Read state Changed!',
|
||||
other: 'Read states Changed!'
|
||||
},
|
||||
{count: props.selectedConversations.length}
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
onError() {
|
||||
setOnFailure(I18n.t('Read state change failed'))
|
||||
|
|
|
@ -106,6 +106,8 @@ describe('CanvasInbox Full Page', () => {
|
|||
).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// TODO: will be fixed with VICE-2077
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('should change the read state of a message', async () => {
|
||||
const container = setup()
|
||||
const conversation = await container.findByTestId('messageListItem-Checkbox')
|
||||
|
|
Loading…
Reference in New Issue