Skip to content

Commit

Permalink
feat: update to popup (#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
alangsto authored Jan 23, 2024
1 parent b4c6b47 commit f706d72
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 23 deletions.
74 changes: 51 additions & 23 deletions src/components/ToggleXpertButton/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
Button,
Icon,
IconButton,
ProductTour,
ModalCloseButton,
ModalPopup,
} from '@edx/paragon';
import { Close } from '@edx/paragon/icons';

Expand All @@ -20,7 +21,9 @@ const ToggleXpert = ({
courseId,
contentToolsEnabled,
}) => {
const [hasDismissed, setHasDismissed] = useState(false);
const [hasDismissedCTA, setHasDismissedCTA] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(true);
const [target, setTarget] = useState(null);
const { userId } = getAuthenticatedUser();

const handleClick = (event) => {
Expand All @@ -35,23 +38,25 @@ const ToggleXpert = ({
},
);
}
setIsModalOpen(false);
localStorage.setItem('completedLearningAssistantTour', 'true');
setIsOpen(!isOpen);
};

const handleDismiss = (event) => {
// prevent default and propagation to prevent sidebar from opening
event.preventDefault();
event.stopPropagation();
setHasDismissed(true);
setHasDismissedCTA(true);
localStorage.setItem('dismissedLearningAssistantCallToAction', 'true');
sendTrackEvent('edx.ui.lms.learning_assistant.dismiss_action_message', {
course_id: courseId,
});
};

const handleProductTourEnd = () => {
setIsOpen(true);
const handleModalClose = () => {
localStorage.setItem('completedLearningAssistantTour', 'true');
setIsModalOpen(false);
sendTrackEvent(
'edx.ui.lms.learning_assistant.launch',
{
Expand All @@ -62,31 +67,53 @@ const ToggleXpert = ({
);
};

const learningAssistantTour = {
tourId: 'learningAssistantTour',
endButtonText: 'Check it out',
onEnd: () => { handleProductTourEnd(); },
enabled: !localStorage.getItem('completedLearningAssistantTour'),
checkpoints: [
{
placement: 'left',
target: '#cta-button',
body: 'Xpert is a new part of your learning experience. '
+ 'You can ask questions and get tutoring help during your course.',
},
],
};
const shouldDisplayCTA = (
(!localStorage.getItem('dismissedLearningAssistantCallToAction') && !hasDismissedCTA)
&& (localStorage.getItem('completedLearningAssistantTour') || !isModalOpen)
);

return (
(!isOpen && (
<>
<ProductTour tours={[learningAssistantTour]} />
<div className={
`toggle closed d-flex flex-column position-fixed justify-content-end align-items-end mx-3 border-0
<div
className="position-fixed learning-assistant-popup-modal"
>
<ModalPopup
hasArrow
placement="left"
positionRef={target}
isOpen={isModalOpen && !localStorage.getItem('completedLearningAssistantTour')}
onClose={handleModalClose}
>
<div
className="bg-white p-3 rounded shadow border"
style={{ textAlign: 'start' }}
>
<p data-testid="modal-message">
Xpert is a new part of your learning experience.<br />
You can ask questions and get tutoring help during your course.
</p>
<div className="d-flex justify-content-start" style={{ gap: '10px' }}>
<ModalCloseButton variant="outline-primary" data-testid="close-button">Close</ModalCloseButton>
<Button
variant="primary"
className="mie-2"
onClick={handleClick}
data-testid="check-button"
>
Check it out
</Button>
</div>
</div>
</ModalPopup>
</div>
<div
className={
`toggle position-fixed closed d-flex flex-column justify-content-end align-items-end mx-3 border-0
${contentToolsEnabled ? 'chat-content-tools-margin' : ''}`
}
>
{(!localStorage.getItem('dismissedLearningAssistantCallToAction') && !hasDismissed) && (
{ shouldDisplayCTA && (
<div
className="d-flex justify-content-end flex-row "
data-testid="action-message"
Expand Down Expand Up @@ -121,6 +148,7 @@ const ToggleXpert = ({
data-testid="toggle-button"
onClick={handleClick}
id="toggle-button"
ref={setTarget}
>
<XpertLogo />
</Button>
Expand Down
4 changes: 4 additions & 0 deletions src/components/ToggleXpertButton/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,7 @@
.chat-content-tools-margin {
margin-bottom: 2rem;
}

.learning-assistant-popup-modal {
width: 100%;
}
39 changes: 39 additions & 0 deletions src/widgets/Xpert.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ beforeEach(() => {
const responseMessage = createRandomResponseForTesting();
jest.spyOn(api, 'default').mockResolvedValue(responseMessage);
window.localStorage.clear();
// Popup modal should be ignored for all tests unless explicitly enabled. This is because
// it makes all other elements non-clickable, so it is easier to test most test cases without the popup.
window.localStorage.setItem('completedLearningAssistantTour', 'true');
});

test('initial load displays correct elements', () => {
Expand Down Expand Up @@ -298,3 +301,39 @@ test('error message should disappear when messages cleared', async () => {
await user.click(screen.getByText('Clear'));
expect(screen.queryByText('Please try again by sending another question.')).not.toBeInTheDocument();
});
test('popup modal should open chat', async () => {
const user = userEvent.setup();
window.localStorage.clear();

render(<Xpert courseId={courseId} contentToolsEnabled={false} />, { preloadedState: initialState });

// button to open chat should be in the DOM
expect(screen.queryByTestId('toggle-button')).toBeVisible();
expect(screen.queryByTestId('modal-message')).toBeVisible();

// click check it out
await user.click(screen.queryByTestId('check-button'));

// assert that UI elements present in the sidebar are visible
expect(screen.getByRole('heading', { name: 'Hi, I\'m Xpert!' })).toBeVisible();
expect(screen.getByRole('textbox')).toBeVisible();
expect(screen.getByRole('button', { name: 'submit' })).toBeVisible();
expect(screen.getByTestId('close-button')).toBeVisible();
expect(screen.getByRole('button', { name: 'clear' })).toBeVisible();
});
test('popup modal should close and display CTA', async () => {
const user = userEvent.setup();
window.localStorage.clear();

render(<Xpert courseId={courseId} contentToolsEnabled={false} />, { preloadedState: initialState });

// button to open chat should be in the DOM
expect(screen.queryByTestId('toggle-button')).toBeVisible();
expect(screen.queryByTestId('modal-message')).toBeVisible();

// click close
await user.click(screen.queryByTestId('close-button'));

assertSidebarElementsNotInDOM();
expect(screen.queryByTestId('action-message')).toBeVisible();
});

0 comments on commit f706d72

Please sign in to comment.