diff --git a/src/courseware/course/Course.jsx b/src/courseware/course/Course.jsx index 459d76bee0..76873b06e3 100644 --- a/src/courseware/course/Course.jsx +++ b/src/courseware/course/Course.jsx @@ -17,6 +17,7 @@ import SidebarProvider from './sidebar/SidebarContextProvider'; import SidebarTriggers from './sidebar/SidebarTriggers'; import { useModel } from '../../generic/model-store'; +import { getSessionStorage, setSessionStorage } from '../../data/sessionStorage'; const Course = ({ courseId, @@ -31,6 +32,7 @@ const Course = ({ const { celebrations, isStaff, + verifiedMode, } = useModel('courseHomeMeta', courseId); const sequence = useModel('sequences', sequenceId); const section = useModel('sections', sequence ? sequence.sectionId : null); @@ -53,6 +55,16 @@ const Course = ({ const shouldDisplayTriggers = windowWidth >= breakpoints.small.minWidth; const daysPerWeek = course?.courseGoals?.selectedGoal?.daysPerWeek; + // 1. open the notification tray if the user has not purchased the course and is on a desktop + // 2. default to close on the first time access + // 3. use the last state if the user has access the course before + const shouldDisplayNotificationTrayOpenOnLoad = windowWidth > breakpoints.medium.minWidth && !!verifiedMode; + if (shouldDisplayNotificationTrayOpenOnLoad) { + setSessionStorage(`notificationTrayStatus.${courseId}`, 'open'); + } else if (!getSessionStorage(`notificationTrayStatus.${courseId}`)) { + setSessionStorage(`notificationTrayStatus.${courseId}`, 'closed'); + } + useEffect(() => { const celebrateFirstSection = celebrations && celebrations.firstSection; setFirstSectionCelebrationOpen(shouldCelebrateOnSectionLoad( diff --git a/src/courseware/course/Course.test.jsx b/src/courseware/course/Course.test.jsx index 07349601e6..d2c60d8fbe 100644 --- a/src/courseware/course/Course.test.jsx +++ b/src/courseware/course/Course.test.jsx @@ -136,9 +136,9 @@ describe('Course', () => { const notificationTrigger = screen.getByRole('button', { name: /Show notification tray/i }); expect(notificationTrigger).toBeInTheDocument(); - expect(notificationTrigger.parentNode).not.toHaveClass('mt-3', { exact: true }); - fireEvent.click(notificationTrigger); expect(notificationTrigger.parentNode).toHaveClass('mt-3'); + fireEvent.click(notificationTrigger); + expect(notificationTrigger.parentNode).not.toHaveClass('mt-3', { exact: true }); }); it('handles click to open/close discussions sidebar', async () => { @@ -146,17 +146,17 @@ describe('Course', () => { const discussionsTrigger = await screen.getByRole('button', { name: /Show discussions tray/i }); const discussionsSideBar = await waitFor(() => screen.findByTestId('sidebar-DISCUSSIONS')); - expect(discussionsSideBar).toHaveClass('d-none'); + expect(discussionsSideBar).not.toHaveClass('d-none'); await act(async () => { fireEvent.click(discussionsTrigger); }); - await expect(discussionsSideBar).not.toHaveClass('d-none'); + await expect(discussionsSideBar).toHaveClass('d-none'); await act(async () => { fireEvent.click(discussionsTrigger); }); - await expect(discussionsSideBar).toHaveClass('d-none'); + await expect(discussionsSideBar).not.toHaveClass('d-none'); }); it('displays discussions sidebar when unit changes', async () => { @@ -177,9 +177,6 @@ describe('Course', () => { await waitFor(() => { expect(screen.getByTestId('sidebar-DISCUSSIONS')).toBeInTheDocument(); - expect(screen.getByTestId('sidebar-DISCUSSIONS')).toHaveClass('d-none'); - const discussionsTrigger = screen.getByRole('button', { name: /Show discussions tray/i }); - fireEvent.click(discussionsTrigger); expect(screen.getByTestId('sidebar-DISCUSSIONS')).not.toHaveClass('d-none'); }); @@ -189,21 +186,20 @@ describe('Course', () => { it('handles click to open/close notification tray', async () => { sessionStorage.clear(); render(, { wrapWithRouter: true }); - expect(sessionStorage.getItem(`notificationTrayStatus.${mockData.courseId}`)).toBe(null); + expect(sessionStorage.getItem(`notificationTrayStatus.${mockData.courseId}`)).toBe('"open"'); const notificationShowButton = await screen.findByRole('button', { name: /Show notification tray/i }); - expect(screen.queryByRole('region', { name: /notification tray/i })).not.toHaveClass('d-none'); - fireEvent.click(notificationShowButton); - expect(sessionStorage.getItem(`notificationTrayStatus.${mockData.courseId}`)).toBe('open'); expect(screen.queryByRole('region', { name: /notification tray/i })).toHaveClass('d-none'); + fireEvent.click(notificationShowButton); + expect(sessionStorage.getItem(`notificationTrayStatus.${mockData.courseId}`)).toBe('"closed"'); + expect(screen.queryByRole('region', { name: /notification tray/i })).not.toHaveClass('d-none'); }); it('handles reload persisting notification tray status', async () => { sessionStorage.clear(); render(, { wrapWithRouter: true }); const notificationShowButton = await screen.findByRole('button', { name: /Show notification tray/i }); - expect(sessionStorage.getItem(`notificationTrayStatus.${mockData.courseId}`)).toBe(null); fireEvent.click(notificationShowButton); - expect(sessionStorage.getItem(`notificationTrayStatus.${mockData.courseId}`)).toBe('open'); + expect(sessionStorage.getItem(`notificationTrayStatus.${mockData.courseId}`)).toBe('"closed"'); // Mock reload window, this doesn't happen in the Course component, // calling the reload to check if the tray remains closed @@ -213,7 +209,7 @@ describe('Course', () => { window.location.reload(); expect(window.location.reload).toHaveBeenCalled(); window.location = location; - expect(sessionStorage.getItem(`notificationTrayStatus.${mockData.courseId}`)).toBe('open'); + expect(sessionStorage.getItem(`notificationTrayStatus.${mockData.courseId}`)).toBe('"closed"'); expect(screen.queryByTestId('NotificationTray')).not.toBeInTheDocument(); }); @@ -222,18 +218,18 @@ describe('Course', () => { const courseMetadataSecondCourse = Factory.build('courseMetadata', { id: 'second_course' }); // set sessionStorage for a different course before rendering Course - sessionStorage.setItem(`notificationTrayStatus.${courseMetadataSecondCourse.id}`, 'open'); + sessionStorage.setItem(`notificationTrayStatus.${courseMetadataSecondCourse.id}`, '"open"'); render(, { wrapWithRouter: true }); - expect(sessionStorage.getItem(`notificationTrayStatus.${mockData.courseId}`)).toBe(null); + expect(sessionStorage.getItem(`notificationTrayStatus.${mockData.courseId}`)).toBe('"open"'); const notificationShowButton = await screen.findByRole('button', { name: /Show notification tray/i }); fireEvent.click(notificationShowButton); // Verify sessionStorage was updated for the original course - expect(sessionStorage.getItem(`notificationTrayStatus.${mockData.courseId}`)).toBe('open'); + expect(sessionStorage.getItem(`notificationTrayStatus.${mockData.courseId}`)).toBe('"closed"'); // Verify the second course sessionStorage was not changed - expect(sessionStorage.getItem(`notificationTrayStatus.${courseMetadataSecondCourse.id}`)).toBe('open'); + expect(sessionStorage.getItem(`notificationTrayStatus.${courseMetadataSecondCourse.id}`)).toBe('"open"'); }); it('renders course breadcrumbs as expected', async () => { diff --git a/src/courseware/course/sidebar/SidebarContextProvider.jsx b/src/courseware/course/sidebar/SidebarContextProvider.jsx index 8a4be654fc..b61c577b19 100644 --- a/src/courseware/course/sidebar/SidebarContextProvider.jsx +++ b/src/courseware/course/sidebar/SidebarContextProvider.jsx @@ -4,10 +4,7 @@ import React, { useEffect, useState, useMemo, useCallback, } from 'react'; -import { useModel } from '../../../generic/model-store'; import { getLocalStorage, setLocalStorage } from '../../../data/localStorage'; -import { getSessionStorage } from '../../../data/sessionStorage'; - import SidebarContext from './SidebarContext'; import { SIDEBARS } from './sidebars'; @@ -16,8 +13,6 @@ const SidebarProvider = ({ unitId, children, }) => { - const { verifiedMode } = useModel('courseHomeMeta', courseId); - const shouldDisplayFullScreen = useWindowSize().width < breakpoints.large.minWidth; const shouldDisplaySidebarOpen = useWindowSize().width > breakpoints.medium.minWidth; const query = new URLSearchParams(window.location.search); @@ -27,11 +22,7 @@ const SidebarProvider = ({ const [upgradeNotificationCurrentState, setUpgradeNotificationCurrentState] = useState(getLocalStorage(`upgradeNotificationCurrentState.${courseId}`)); useEffect(() => { - // if the user has not purchased the course or previously opened the notification tray, show the notification tray - const showNotificationsOnLoad = !!verifiedMode || getSessionStorage(`notificationTrayStatus.${courseId}`) !== 'closed'; - if (showNotificationsOnLoad) { - setCurrentSidebar(SIDEBARS.NOTIFICATIONS.ID); - } + setCurrentSidebar(SIDEBARS.DISCUSSIONS.ID); // eslint-disable-next-line react-hooks/exhaustive-deps }, [unitId]); diff --git a/src/data/sessionStorage.js b/src/data/sessionStorage.js index ef809330d3..fe86b577d0 100644 --- a/src/data/sessionStorage.js +++ b/src/data/sessionStorage.js @@ -21,7 +21,7 @@ function getSessionStorage(key) { function setSessionStorage(key, value) { try { if (global.sessionStorage) { - global.sessionStorage.setItem(key, value); + global.sessionStorage.setItem(key, JSON.stringify(value)); } } catch (e) { // If this fails, just bail.