From 1dc23fbd5a05d753f5362016ea1ed1e4b7e02575 Mon Sep 17 00:00:00 2001 From: syao1226 <146495172+syao1226@users.noreply.github.com> Date: Thu, 26 Sep 2024 12:05:33 -0400 Subject: [PATCH] feat(protocol-designer): add the release notes button to settings (#16344) re AUTH-856 # Overview Add a `View release notes` button to the Settings page, and display the `AnnouncementModal` when the button is clicked. ## Test Plan and Hands on Testing - Click on the settings icon - Ensure it matches the [design](https://www.figma.com/design/WbkiUyU8VhtKz0JSuIFA45/Feature%3A-Protocol-Designer-Phase-1?node-id=3150-116483&node-type=canvas&t=VjSlf85g2lJzT7Ed-0) - Verify that the Announcement modal appears when the 'View release notes' button is clicked. ## Changelog - Added a button labeled 'View release notes' next to the PD version with a 12px gap - Create an `AnnouncementModalProp` interface with optional `isViewReleaseNotes` and `onClose` props to control the visibility of the AnnouncementModal, tracking when the user clicks the button and closes the modal - Used `useLocation` to check if the user is on the Settings page - Updated tests ## Review requests ## Risk assessment --------- Co-authored-by: shiyaochen --- .../src/assets/localization/en/shared.json | 1 + .../src/organisms/AnnouncementModal/index.tsx | 13 +- .../Settings/__tests__/Settings.test.tsx | 13 + .../src/pages/Settings/index.tsx | 272 ++++++++++-------- 4 files changed, 179 insertions(+), 120 deletions(-) diff --git a/protocol-designer/src/assets/localization/en/shared.json b/protocol-designer/src/assets/localization/en/shared.json index 6e86fc6e607..92872373624 100644 --- a/protocol-designer/src/assets/localization/en/shared.json +++ b/protocol-designer/src/assets/localization/en/shared.json @@ -115,6 +115,7 @@ "user_settings": "User settings", "uses_standard_namespace": "Opentrons verified labware", "version": "Version # {{version}}", + "view_release_notes": "View release notes", "warning": "WARNING:", "wasteChute": "Waste chute", "wasteChuteAndStagingArea": "Waste chute and staging area slot", diff --git a/protocol-designer/src/organisms/AnnouncementModal/index.tsx b/protocol-designer/src/organisms/AnnouncementModal/index.tsx index d8e673cfb5b..6eca54206ac 100644 --- a/protocol-designer/src/organisms/AnnouncementModal/index.tsx +++ b/protocol-designer/src/organisms/AnnouncementModal/index.tsx @@ -16,7 +16,15 @@ import { } from '../../persist' import { useAnnouncements } from './announcements' -export const AnnouncementModal = (): JSX.Element => { +interface AnnouncementModalProps { + isViewReleaseNotes?: boolean + onClose?: () => void +} + +export const AnnouncementModal = ( + props: AnnouncementModalProps +): JSX.Element => { + const { onClose, isViewReleaseNotes = false } = props const { t } = useTranslation(['modal', 'button']) const announcements = useAnnouncements() @@ -28,10 +36,11 @@ export const AnnouncementModal = (): JSX.Element => { getLocalStorageItem(localStorageAnnouncementKey) !== announcementKey const [showAnnouncementModal, setShowAnnouncementModal] = useState( - userHasNotSeenAnnouncement + isViewReleaseNotes || userHasNotSeenAnnouncement ) const handleClick = (): void => { + if (onClose != null) onClose() setLocalStorageItem(localStorageAnnouncementKey, announcementKey) setShowAnnouncementModal(false) } diff --git a/protocol-designer/src/pages/Settings/__tests__/Settings.test.tsx b/protocol-designer/src/pages/Settings/__tests__/Settings.test.tsx index e73034fe4d8..d9713d7ae88 100644 --- a/protocol-designer/src/pages/Settings/__tests__/Settings.test.tsx +++ b/protocol-designer/src/pages/Settings/__tests__/Settings.test.tsx @@ -1,6 +1,7 @@ import { describe, it, vi, beforeEach, expect } from 'vitest' import { MemoryRouter } from 'react-router-dom' import { fireEvent, screen } from '@testing-library/react' +import { AnnouncementModal } from '../../../organisms' import { i18n } from '../../../assets/localization' import { renderWithProviders } from '../../../__testing-utils__' import { getHasOptedIn } from '../../../analytics/selectors' @@ -11,6 +12,7 @@ import { optIn } from '../../../analytics/actions' import { setFeatureFlags } from '../../../feature-flags/actions' import { Settings } from '..' +vi.mock('../../../organisms') vi.mock('../../../feature-flags/actions') vi.mock('../../../analytics/actions') vi.mock('../../../tutorial/actions') @@ -40,6 +42,7 @@ describe('Settings', () => { screen.getByText('App settings') screen.getByText('Protocol designer version') screen.getByText('fake_PD_version') + screen.getAllByText('View release notes') screen.getByText('User settings') screen.getByText('Hints') screen.getByText('Reset all hints and tips notifications') @@ -50,6 +53,16 @@ describe('Settings', () => { 'We’re working to improve Protocol Designer. Part of the process involves watching real user sessions to understand which parts of the interface are working and which could use improvement. We never share sessions outside of Opentrons.' ) }) + it('renders the announcement modal when view release notes button is clicked', () => { + vi.mocked(AnnouncementModal).mockReturnValue( +
mock AnnouncementModal
+ ) + render() + fireEvent.click( + screen.getByTestId('AnnouncementModal_viewReleaseNotesButton') + ) + screen.getByText('mock AnnouncementModal') + }) it('renders the hints button and calls to dismiss them when text is pressed', () => { render() fireEvent.click(screen.getByText('Reset hints')) diff --git a/protocol-designer/src/pages/Settings/index.tsx b/protocol-designer/src/pages/Settings/index.tsx index 14c1aa488e6..abf449347f0 100644 --- a/protocol-designer/src/pages/Settings/index.tsx +++ b/protocol-designer/src/pages/Settings/index.tsx @@ -1,3 +1,4 @@ +import { useState } from 'react' import { useTranslation } from 'react-i18next' import { useDispatch, useSelector } from 'react-redux' import { css } from 'styled-components' @@ -14,6 +15,7 @@ import { StyledText, TYPOGRAPHY, } from '@opentrons/components' +import { AnnouncementModal } from '../../organisms' import { actions as analyticsActions, selectors as analyticsSelectors, @@ -22,6 +24,7 @@ import { actions as tutorialActions, selectors as tutorialSelectors, } from '../../tutorial' +import { BUTTON_LINK_STYLE } from '../../atoms' import { actions as featureFlagActions } from '../../feature-flags' import { getFeatureFlagData } from '../../feature-flags/selectors' import type { FlagTypes } from '../../feature-flags' @@ -29,6 +32,9 @@ import type { FlagTypes } from '../../feature-flags' export function Settings(): JSX.Element { const dispatch = useDispatch() const { t } = useTranslation(['feature_flags', 'shared']) + const [showAnnouncementModal, setShowAnnouncementModal] = useState( + false + ) const hasOptedIn = useSelector(analyticsSelectors.getHasOptedIn) const flags = useSelector(getFeatureFlagData) const canClearHintDismissals = useSelector( @@ -94,139 +100,169 @@ export function Settings(): JSX.Element { const prereleaseFlagRows = allFlags.map(toFlagRow) return ( - + <> + {showAnnouncementModal ? ( + { + setShowAnnouncementModal(false) + }} + /> + ) : null} - - {t('shared:settings')} - - - - {t('shared:app_settings')} - - - - {t('shared:pd_version')} - - - {process.env.OT_PD_VERSION} - - - - - - {t('shared:user_settings')} + + + {t('shared:settings')} - - - - {t('shared:hints')} + + + + {t('shared:app_settings')} - - - {t('shared:reset_hints_and_tips')} + + + {t('shared:pd_version')} + + + {process.env.OT_PD_VERSION} + + { + setShowAnnouncementModal(true) + }} + data-testid="AnnouncementModal_viewReleaseNotesButton" + > + + {t('shared:view_release_notes')} + + + - dispatch(tutorialActions.clearAllHintDismissals())} - > - - {canClearHintDismissals - ? t('shared:reset_hints') - : t('shared:no_hints_to_restore')} + + + {t('shared:user_settings')} - - - - - - {t('shared:privacy')} - - - - - {t('shared:shared_sessions')} + + + + {t('shared:hints')} + + + + {t('shared:reset_hints_and_tips')} + + + + + dispatch(tutorialActions.clearAllHintDismissals()) + } + > + + {canClearHintDismissals + ? t('shared:reset_hints') + : t('shared:no_hints_to_restore')} + + + + + + + {t('shared:privacy')} - - - {t('shared:we_are_improving')} - + + + + {t('shared:shared_sessions')} + + + + {t('shared:we_are_improving')} + + + + dispatch(_toggleOptedIn())} + > + + - dispatch(_toggleOptedIn())} - > - - - - {prereleaseModeEnabled ? ( - - - {t('shared:developer_ff')} - - - {prereleaseFlagRows} + {prereleaseModeEnabled ? ( + + + {t('shared:developer_ff')} + + + {prereleaseFlagRows} + - - ) : null} + ) : null} + - + ) }