-
Notifications
You must be signed in to change notification settings - Fork 230
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add component to iframe LTI launch (#1135)
- Loading branch information
Showing
9 changed files
with
242 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import React, { useState } from 'react'; | ||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; | ||
import { | ||
ModalDialog, | ||
Icon, | ||
useToggle, | ||
OverlayTrigger, | ||
Popover, | ||
} from '@edx/paragon'; | ||
import { ChatBubbleOutline } from '@edx/paragon/icons'; | ||
import classNames from 'classnames'; | ||
import PropTypes from 'prop-types'; | ||
import messages from './messages'; | ||
|
||
const ChatTrigger = ({ | ||
intl, | ||
enrollmentMode, | ||
isStaff, | ||
launchUrl, | ||
}) => { | ||
const [isOpen, open, close] = useToggle(false); | ||
const [hasOpenedChat, setHasOpenedChat] = useState(false); | ||
|
||
const VERIFIED_MODES = [ | ||
'professional', | ||
'verified', | ||
'no-id-professional', | ||
'credit', | ||
'masters', | ||
'executive-education', | ||
]; | ||
|
||
const isVerifiedEnrollmentMode = ( | ||
enrollmentMode !== null | ||
&& enrollmentMode !== undefined | ||
&& VERIFIED_MODES.some(mode => mode === enrollmentMode) | ||
); | ||
|
||
const shouldDisplayChat = ( | ||
launchUrl | ||
&& (isVerifiedEnrollmentMode || isStaff) // display only to non-audit or staff | ||
); | ||
|
||
const handleOpen = () => { | ||
if (!hasOpenedChat) { | ||
setHasOpenedChat(true); | ||
} | ||
open(); | ||
}; | ||
|
||
return ( | ||
<> | ||
{shouldDisplayChat && ( | ||
<div | ||
className={classNames('mt-3', 'd-flex', 'ml-auto')} | ||
> | ||
<OverlayTrigger | ||
trigger="click" | ||
key="top" | ||
show={!hasOpenedChat} | ||
overlay={( | ||
<Popover id="popover-chat-information"> | ||
<Popover.Title as="h3">{intl.formatMessage(messages.popoverTitle)}</Popover.Title> | ||
<Popover.Content> | ||
{intl.formatMessage(messages.popoverContent)} | ||
</Popover.Content> | ||
</Popover> | ||
)} | ||
> | ||
<button | ||
className="border border-light-400 bg-transparent align-items-center align-content-center d-flex" | ||
type="button" | ||
onClick={handleOpen} | ||
aria-label={intl.formatMessage(messages.openChatModalTrigger)} | ||
> | ||
<div className="icon-container d-flex position-relative align-items-center"> | ||
<Icon src={ChatBubbleOutline} className="m-0 m-auto" /> | ||
</div> | ||
</button> | ||
</OverlayTrigger> | ||
<ModalDialog | ||
onClose={close} | ||
isOpen={isOpen} | ||
title={intl.formatMessage(messages.modalTitle)} | ||
size="xl" | ||
hasCloseButton | ||
> | ||
<ModalDialog.Header> | ||
<ModalDialog.Title> | ||
{intl.formatMessage(messages.modalTitle)} | ||
</ModalDialog.Title> | ||
</ModalDialog.Header> | ||
<ModalDialog.Body> | ||
<iframe | ||
src={launchUrl} | ||
allowFullScreen | ||
style={{ | ||
width: '100%', | ||
height: '60vh', | ||
}} | ||
title={intl.formatMessage(messages.modalTitle)} | ||
/> | ||
</ModalDialog.Body> | ||
</ModalDialog> | ||
</div> | ||
)} | ||
</> | ||
); | ||
}; | ||
|
||
ChatTrigger.propTypes = { | ||
intl: intlShape.isRequired, | ||
isStaff: PropTypes.bool.isRequired, | ||
enrollmentMode: PropTypes.string.isRequired, | ||
launchUrl: PropTypes.string, | ||
}; | ||
|
||
ChatTrigger.defaultProps = { | ||
launchUrl: null, | ||
}; | ||
|
||
export default injectIntl(ChatTrigger); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { render } from '@testing-library/react'; | ||
import { IntlProvider } from '@edx/frontend-platform/i18n'; | ||
import { BrowserRouter } from 'react-router-dom'; | ||
import React from 'react'; | ||
import ChatTrigger from './ChatTrigger'; | ||
import { act, fireEvent, screen } from '../../../setupTest'; | ||
|
||
describe('ChatTrigger', () => { | ||
it('handles click to open/close chat modal', async () => { | ||
render( | ||
<IntlProvider> | ||
<BrowserRouter> | ||
<ChatTrigger | ||
enrollmentMode={null} | ||
isStaff | ||
launchUrl="https://testurl.org" | ||
/> | ||
</BrowserRouter>, | ||
</IntlProvider>, | ||
); | ||
|
||
const chatTrigger = screen.getByRole('button', { name: /Show chat modal/i }); | ||
expect(chatTrigger).toBeInTheDocument(); | ||
expect(screen.queryByText('Need help understanding course content?')).toBeInTheDocument(); | ||
|
||
await act(async () => { | ||
fireEvent.click(chatTrigger); | ||
}); | ||
const modalCloseButton = screen.getByRole('button', { name: /Close/i }); | ||
await expect(modalCloseButton).toBeInTheDocument(); | ||
|
||
await act(async () => { | ||
fireEvent.click(modalCloseButton); | ||
}); | ||
await expect(modalCloseButton).not.toBeInTheDocument(); | ||
expect(screen.queryByText('Holy guacamole!')).not.toBeInTheDocument(); | ||
}); | ||
|
||
const testCases = [ | ||
{ enrollmentMode: null, isVisible: false }, | ||
{ enrollmentMode: undefined, isVisible: false }, | ||
{ enrollmentMode: 'audit', isVisible: false }, | ||
{ enrollmentMode: 'xyz', isVisible: false }, | ||
{ enrollmentMode: 'professional', isVisible: true }, | ||
{ enrollmentMode: 'verified', isVisible: true }, | ||
{ enrollmentMode: 'no-id-professional', isVisible: true }, | ||
{ enrollmentMode: 'credit', isVisible: true }, | ||
{ enrollmentMode: 'masters', isVisible: true }, | ||
{ enrollmentMode: 'executive-education', isVisible: true }, | ||
]; | ||
|
||
testCases.forEach(test => { | ||
it(`does chat to be visible based on enrollment mode of ${test.enrollmentMode}`, async () => { | ||
render( | ||
<IntlProvider> | ||
<BrowserRouter> | ||
<ChatTrigger | ||
enrollmentMode={test.enrollmentMode} | ||
isStaff={false} | ||
launchUrl="https://testurl.org" | ||
/> | ||
</BrowserRouter>, | ||
</IntlProvider>, | ||
); | ||
|
||
const chatTrigger = screen.queryByRole('button', { name: /Show chat modal/i }); | ||
if (test.isVisible) { | ||
expect(chatTrigger).toBeInTheDocument(); | ||
} else { | ||
expect(chatTrigger).not.toBeInTheDocument(); | ||
} | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from './ChatTrigger'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { defineMessages } from '@edx/frontend-platform/i18n'; | ||
|
||
const messages = defineMessages({ | ||
popoverTitle: { | ||
id: 'popover.title', | ||
defaultMessage: 'Need help understanding course content?', | ||
description: 'Title for popover alerting user of chat modal', | ||
}, | ||
popoverContent: { | ||
id: 'popover.content', | ||
defaultMessage: 'Click here for your Xpert Learning Assistant.', | ||
description: 'Content of the popover message', | ||
}, | ||
openChatModalTrigger: { | ||
id: 'chat.model.trigger', | ||
defaultMessage: 'Show chat modal', | ||
description: 'Alt text for button that opens the chat modal', | ||
}, | ||
modalTitle: { | ||
id: 'chat.model.title', | ||
defaultMessage: 'Xpert Learning Assistant', | ||
description: 'Title for chat modal header', | ||
}, | ||
}); | ||
|
||
export default messages; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters