Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: update to popup #39

Merged
merged 1 commit into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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();
});
Loading