Skip to content

Commit

Permalink
feat: update call to action message (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
alangsto authored Aug 29, 2023
1 parent 49d8db1 commit 4e4d88c
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 22 deletions.
51 changes: 41 additions & 10 deletions src/components/ToggleXpertButton/index.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import PropTypes from 'prop-types';
import { Button } from '@edx/paragon';
import { useState } from 'react';

import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { Button, Icon, IconButton } from '@edx/paragon';
import { Close } from '@edx/paragon/icons';

import { ReactComponent as XpertLogo } from '../../assets/xpert-logo.svg';

import './index.scss';

const ToggleXpert = ({ isOpen, setIsOpen, courseId }) => {
const [hasDismissed, setHasDismissed] = useState(false);
const handleClick = () => {
// log event if the tool is opened
if (!isOpen) {
Expand All @@ -17,15 +20,43 @@ const ToggleXpert = ({ isOpen, setIsOpen, courseId }) => {
setIsOpen(!isOpen);
};

const handleDismiss = (event) => {
// prevent default and propagation to prevent sidebar from opening
event.preventDefault();
event.stopPropagation();
setHasDismissed(true);
};

return (
<Button
variant="primary"
className="toggle closed d-flex flex-column position-fixed mx-3"
data-testid="toggle-button"
onClick={handleClick}
>
<XpertLogo />
</Button>
<div className="toggle closed d-flex flex-column position-fixed justify-content-end align-items-end mx-3 border-0">
{!hasDismissed && (
<div className="d-flex justify-content-end flex-row" data-testid="action-message">
<IconButton
src={Close}
iconAs={Icon}
alt="dismiss"
onClick={handleDismiss}
variant="light"
className="dismiss-button mx-2 mt-2 bg-gray"
size="sm"
/>
<div className="action-message open-negative-margin px-3 py-3 my-2">
<span>
Hi there! 👋 I&apos;m Xpert,
an AI-powered assistant from edX who can help you with questions about this course.
</span>
</div>
</div>
)}
<Button
variant="primary"
className="toggle button-icon"
data-testid="toggle-button"
onClick={handleClick}
>
<XpertLogo />
</Button>
</div>
);
};

Expand Down
21 changes: 20 additions & 1 deletion src/components/ToggleXpertButton/index.scss
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
@use '../../utils/variables';

.toggle {
background-color: variables.$dark-green;
border-radius: 2rem;
bottom: 1rem;
height: 3rem;

&.button-icon {
background-color: variables.$dark-green;
}

&.open {
right: 31%;
}
Expand All @@ -20,3 +23,19 @@
width: 100%;
}
}

.action-message {
background-color: white;
box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.3), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
color: black;
font-size: 14px;
width: 40%;
text-align: left;
border-radius: 1rem;
}

.dismiss-button {
// override paragon width and height for dismiss button
width: 1.5rem !important;
height: 1.5rem !important;
}
35 changes: 24 additions & 11 deletions src/widgets/Xpert.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,30 @@ test('initial load displays correct elements', () => {
render(<Xpert courseId={courseId} />, { preloadedState: initialState });

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

// assert that UI elements in the sidebar are not in the DOM
assertSidebarElementsNotInDOM();
});
test('clicking the call to action dismiss button removes the message', async () => {
const user = userEvent.setup();
render(<Xpert courseId={courseId} />, { preloadedState: initialState });

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

await user.click(screen.getByRole('button', { name: 'dismiss' }));
expect(screen.queryByTestId('toggle-button')).toBeVisible();
expect(screen.queryByTestId('action-message')).not.toBeInTheDocument();
});
test('clicking the toggle button opens the sidebar', async () => {
const user = userEvent.setup();

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

await user.click(screen.getByRole('button'));
await user.click(screen.queryByTestId('toggle-button'));

// assert that UI elements present in the sidebar are visible
expect(screen.getByRole('heading', { name: 'Hi, I\'m Xpert!' })).toBeVisible();
Expand All @@ -69,7 +82,7 @@ test('submitted text appears as message in the sidebar', async () => {

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

await user.click(screen.getByRole('button'));
await user.click(screen.queryByTestId('toggle-button'));

// type the user message
const input = screen.getByRole('textbox');
Expand Down Expand Up @@ -97,7 +110,7 @@ test('loading message appears in the sidebar while the response loads', async ()

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

await user.click(screen.getByRole('button'));
await user.click(screen.queryByTestId('toggle-button'));

// type the user message
await user.type(screen.getByRole('textbox'), userMessage);
Expand All @@ -121,7 +134,7 @@ test('response text appears as message in the sidebar', async () => {

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

await user.click(screen.getByRole('button'));
await user.click(screen.queryByTestId('toggle-button'));

// type the user message
const input = screen.getByRole('textbox');
Expand All @@ -142,7 +155,7 @@ test('clicking the clear button clears messages in the sidebar', async () => {

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

await user.click(screen.getByRole('button'));
await user.click(screen.queryByTestId('toggle-button'));

// type the user message
const input = screen.getByRole('textbox');
Expand All @@ -160,7 +173,7 @@ test('clicking the close button closes the sidebar', async () => {
const user = userEvent.setup();
render(<Xpert courseId={courseId} />, { preloadedState: initialState });

await user.click(screen.getByRole('button'));
await user.click(screen.queryByTestId('toggle-button'));
await user.click(screen.getByTestId('close-button'));

// assert that UI elements in the sidebar are not in the DOM
Expand All @@ -170,7 +183,7 @@ test('clicking the toggle button closes the sidebar', async () => {
const user = userEvent.setup();
render(<Xpert courseId={courseId} />, { preloadedState: initialState });

await user.click(screen.getByRole('button'));
await user.click(screen.queryByTestId('toggle-button'));
await user.click(screen.getByTestId('toggle-button'));

// assert that UI elements in the sidebar are not in the DOM
Expand All @@ -193,7 +206,7 @@ test('error message should disappear upon succesful api call', async () => {
};
render(<Xpert courseId={courseId} />, { preloadedState: errorState });

await user.click(screen.getByRole('button'));
await user.click(screen.queryByTestId('toggle-button'));

// assert that error has focus
expect(screen.queryByTestId('alert-heading')).toHaveFocus();
Expand Down Expand Up @@ -223,7 +236,7 @@ test('error message should disappear when dismissed', async () => {
};
render(<Xpert courseId={courseId} />, { preloadedState: errorState });

await user.click(screen.getByRole('button'));
await user.click(screen.queryByTestId('toggle-button'));

// assert that error message exists
expect(screen.queryByText('Please try again by sending another question.')).toBeInTheDocument();
Expand All @@ -248,7 +261,7 @@ test('error message should disappear when messages cleared', async () => {
};
render(<Xpert courseId={courseId} />, { preloadedState: errorState });

await user.click(screen.getByRole('button'));
await user.click(screen.queryByTestId('toggle-button'));

// assert that error message exists
expect(screen.queryByText('Please try again by sending another question.')).toBeInTheDocument();
Expand Down

0 comments on commit 4e4d88c

Please sign in to comment.