From ede598ddc20c0b6ee19ded00859b4113ebb4d38b Mon Sep 17 00:00:00 2001 From: Manuel Rodriguez Date: Thu, 30 Nov 2023 22:23:05 -0800 Subject: [PATCH 1/4] Updated Generic modal to use a variant --- source/frontend/src/assets/scss/App.scss | 108 -------------- .../src/assets/scss/_variables.module.scss | 73 +++++----- .../common/CancelConfirmationModal.tsx | 6 +- .../src/components/common/ErrorModal.tsx | 1 + .../src/components/common/ErrorTabs.tsx | 1 + .../components/common/GenericModal.test.tsx | 14 +- .../src/components/common/GenericModal.tsx | 132 +++++++++++++++++- .../components/common/ModalContainer.test.tsx | 6 +- .../src/components/common/form/NotesModal.tsx | 2 +- .../components/common/mapFSM/useMapSearch.tsx | 3 +- .../contact/ContactManagerModal.tsx | 2 +- .../AdvancedFilter/FilterContentContainer.tsx | 2 - .../AdvancedFilter/FilterContentForm.tsx | 2 - .../src/components/modals/roleMismatch.tsx | 2 +- source/frontend/src/contexts/modalContext.tsx | 7 +- .../Organization/CreateOrganizationForm.tsx | 2 + .../create/Person/CreatePersonForm.tsx | 2 + .../Organization/UpdateOrganizationForm.tsx | 1 + .../contact/edit/Person/UpdatePersonForm.tsx | 1 + .../documentDetail/DocumentDetailModal.tsx | 2 +- .../documentUpload/DocumentUploadModal.tsx | 2 +- .../documents/list/DocumentListView.tsx | 2 +- .../LeasePages/deposits/DepositsContainer.tsx | 2 + .../payment/TermPaymentsContainer.tsx | 2 + .../tenant/PrimaryContactWarningModal.tsx | 1 + .../propertyPicker/LeasePropertySelector.tsx | 3 +- .../acquisition/AcquisitionView.test.tsx | 4 +- .../acquisition/AcquisitionView.tsx | 2 +- ...enerateLetterRecipientsModal.test.tsx.snap | 93 ++++++++++++ .../common/modals/AcquisitionFormModal.tsx | 7 +- .../agreement/update/AgreementSubForm.tsx | 1 + .../UpdateCompensationRequisitionForm.tsx | 2 + .../financials/FinancialActivitiesSubForm.tsx | 1 + .../update/UpdateAcquisitionContainer.tsx | 1 + .../mapSideBar/lease/LeaseContainer.tsx | 2 +- .../project/ProjectContainerView.tsx | 1 + .../project/add/ProductsArrayForm.tsx | 2 + .../property/MotiInventoryContainer.tsx | 1 + .../detail/PropertyContactList.tsx | 1 + .../tabs/takes/update/TakeSubForm.tsx | 1 + .../mapSideBar/research/ResearchContainer.tsx | 1 + .../update/properties/UpdateProperties.tsx | 3 + .../__snapshots__/NoteContainer.test.tsx.snap | 93 ++++++++++++ .../features/notes/add/AddNotesContainer.tsx | 1 + .../AddNotesContainer.test.tsx.snap | 93 ++++++++++++ .../AddNotesFormModal.test.tsx.snap | 93 ++++++++++++ .../NoteDetailsFormModal.test.tsx.snap | 93 ++++++++++++ .../src/features/notes/list/NoteListView.tsx | 1 + .../notes/update/UpdateNoteContainer.tsx | 1 + .../UpdateNoteContainer.test.tsx.snap | 93 ++++++++++++ .../UpdateNoteFormModal.test.tsx.snap | 93 ++++++++++++ .../reports/ProjectExportContainer.tsx | 2 + .../frontend/src/hooks/useApiUserOverride.ts | 3 +- source/frontend/src/hooks/useModalContext.ts | 6 +- .../frontend/src/hooks/usePimsIdleTimer.tsx | 1 + .../frontend/src/utils/TestCommonWrapper.tsx | 1 - 56 files changed, 899 insertions(+), 179 deletions(-) diff --git a/source/frontend/src/assets/scss/App.scss b/source/frontend/src/assets/scss/App.scss index 38427edf2e..2b7eb1ddfd 100644 --- a/source/frontend/src/assets/scss/App.scss +++ b/source/frontend/src/assets/scss/App.scss @@ -1,113 +1,5 @@ @import './styles.scss'; -.modal-dialog { - div.modal-header { - height: 4.8rem; - padding: 0 1.6rem; - display: flex; - flex-direction: row; - align-items: center; - color: $primary-background-color; - background-color: $primary-color; - - .modal-title { - font-family: BcSans-Bold; - font-size: 2.2rem; - height: 2.75rem; - height: auto; - } - - .header-icon { - margin-right: 8px; - display: inline-block; - } - - .modal-close-btn { - cursor: pointer; - } - } - - div.modal-body { - padding: 2.4rem 1.8rem; - font-size: 1.8rem; - } - - div.modal-footer { - border-top: none; - - hr { - width: 100%; - } - - .button-wrap { - display: inline-flex; - margin-top: 2.4rem; - margin-bottom: 2.4rem; - } - - button { - margin-right: 2.4rem; - min-width: 9.5rem; - height: 3.9rem; - } - } - - .close { - color: black; - } - - &.modal-xl { - max-width: 100rem; - } - - &.modal-l { - max-width: 75rem; - } - - &.modal-m { - max-width: 50rem; - } - - &.modal-s { - max-width: 40rem; - } - - &.info { - .modal-header { - color: $dark-blue; - background-color: $filter-box-color; - } - - .modal-close-btn { - color: $text-color; - cursor: pointer; - } - } - - &.error { - .modal-header { - color: $font-danger-color; - background-color: $danger-background-color; - } - - .modal-close-btn { - color: $text-color; - cursor: pointer; - } - } - - &.warning { - .modal-header { - color: $font-warning-color; - background-color: $summary-color; - } - - .modal-close-btn { - color: $text-color; - cursor: pointer; - } - } -} .Toastify__toast .Toastify__toast-body { word-break: break-word; diff --git a/source/frontend/src/assets/scss/_variables.module.scss b/source/frontend/src/assets/scss/_variables.module.scss index 3db8bdbbf9..c4fa65104e 100644 --- a/source/frontend/src/assets/scss/_variables.module.scss +++ b/source/frontend/src/assets/scss/_variables.module.scss @@ -6,40 +6,6 @@ footerHeight: $footer-height; headerHeight: $header-height; - primaryColor: $primary-color; - primaryLightColor: $primary-light-color; - secondaryVariantColor: $secondary-variant-color; - selectedColor: $selected-color; - lightVariantColor: $light-variant-color; - darkVariantColor: $dark-variant-color; - textColor: $text-color; - formTextColor: $form-text-color; - formControlTextColor: $form-control-text-color; - formBackgroundColor: $form-background-color; - primaryBackgroundColor: $primary-background-color; - dropdownBackgroundColor: $dropdown-background-color; - accentColor: $accent-color; - lightAccentColor: $light-accent-color; - sresIconColor: $sres-icon-color; - dangerColor: $danger-color; - lightDangerColor: $light-danger-color; - iconLightColor: $icon-light-color; - activeColor: $active-color; - completedColor: $completed-color; - filterBackgroundColor: $filter-background-color; - slideOutBlue: $slide-out-blue; - disabledColor: $disabled-color; - disabledFieldBackgroundColor: $disabled-field-background-color; - filterBoxColor: $filter-box-color; - draftColor: $draft-color; - linkColor: $link-color; - linkHoverColor: $link-hover-color; - subtleColor: $subtle-color; - discardedColor: $discarded-color; - dangerBackgroundColor: $danger-background-color; - summaryColor: $summary-color; - summaryBorderColor: $summary-border-color; - primaryBorderColor: $primary-border-color; // table ui tableHoverColor: $table-hover-color; tableHeaderBgColor: $table-header-bg-color; @@ -55,4 +21,43 @@ // buttons buttonOutlineColor: $icon-light-color; buttonInfoColor: $button-info-color; + + // Colors + accentColor: $accent-color; + activeColor: $active-color; + completedColor: $completed-color; + dangerBackgroundColor: $danger-background-color; + dangerColor: $danger-color; + darkBlue: $dark-blue; + darkVariantColor: $dark-variant-color; + disabledColor: $disabled-color; + disabledFieldBackgroundColor: $disabled-field-background-color; + discardedColor: $discarded-color; + draftColor: $draft-color; + dropdownBackgroundColor: $dropdown-background-color; + filterBackgroundColor: $filter-background-color; + filterBoxColor: $filter-box-color; + fontDangerColor: $font-danger-color; + fontWarningColor: $font-warning-color; + formBackgroundColor: $form-background-color; + formControlTextColor: $form-control-text-color; + formTextColor: $form-text-color; + iconLightColor: $icon-light-color; + lightAccentColor: $light-accent-color; + lightDangerColor: $light-danger-color; + lightVariantColor: $light-variant-color; + linkColor: $link-color; + linkHoverColor: $link-hover-color; + primaryBackgroundColor: $primary-background-color; + primaryBorderColor: $primary-border-color; + primaryColor: $primary-color; + primaryLightColor: $primary-light-color; + secondaryVariantColor: $secondary-variant-color; + selectedColor: $selected-color; + slideOutBlue: $slide-out-blue; + sresIconColor: $sres-icon-color; + subtleColor: $subtle-color; + summaryBorderColor: $summary-border-color; + summaryColor: $summary-color; + textColor: $text-color; } diff --git a/source/frontend/src/components/common/CancelConfirmationModal.tsx b/source/frontend/src/components/common/CancelConfirmationModal.tsx index 8e9fad5dff..34068cd965 100644 --- a/source/frontend/src/components/common/CancelConfirmationModal.tsx +++ b/source/frontend/src/components/common/CancelConfirmationModal.tsx @@ -2,8 +2,11 @@ import React from 'react'; import GenericModal, { ModalProps } from '@/components/common/GenericModal'; -export const CancelConfirmationModal: React.FC> = props => { +export const CancelConfirmationModal: React.FC< + React.PropsWithChildren> +> = props => { const { + variant = 'info', title = 'Unsaved Changes', message = 'You have made changes on this form. Do you wish to leave without saving?', okButtonText = 'Confirm', @@ -13,6 +16,7 @@ export const CancelConfirmationModal: React.FC { const history = useHistory(); return ( {showTabErrorModal && ( { - const { asFragment } = render(); + const { asFragment } = render(); expect(asFragment()).toMatchSnapshot(); }); it('renders title based off of prop...', () => { - const { getByText } = render(); + const { getByText } = render(); expect(getByText('Test Title')).toBeInTheDocument(); }); it('renders message based off of prop', () => { - const { getByText } = render(); + const { getByText } = render(); expect(getByText('Test Message')).toBeInTheDocument(); }); it('renders button text based off of prop', () => { const { getByText } = render( - , + , ); expect(getByText('Ok Text')).toBeInTheDocument(); expect(getByText('Cancel Text')).toBeInTheDocument(); @@ -27,7 +27,9 @@ it('renders button text based off of prop', () => { it('calls custom ok on click', async () => { const mockOk = jest.fn(); - const { getByText } = render(); + const { getByText } = render( + , + ); const okButton = getByText('Ok Button'); await waitFor(() => { fireEvent.click(okButton); @@ -38,7 +40,7 @@ it('calls custom ok on click', async () => { it('calls custom cancel funciton on click', async () => { const mockCancel = jest.fn(); const { getByText } = render( - , + , ); const cancelButton = getByText('Cancel Button'); await waitFor(() => { diff --git a/source/frontend/src/components/common/GenericModal.tsx b/source/frontend/src/components/common/GenericModal.tsx index 01029dffef..5219baf05c 100644 --- a/source/frontend/src/components/common/GenericModal.tsx +++ b/source/frontend/src/components/common/GenericModal.tsx @@ -77,6 +77,8 @@ export interface ModalContent { closeButton?: boolean; /** provide the size of the modal, default width is 50.0rem */ modalSize?: ModalSize; + variant: 'info' | 'warning' | 'error'; + //variant?: string; className?: string; /** display this modal as a popup instead of as a modal, allowing the user to click on underlying elements */ asPopup?: boolean; @@ -106,6 +108,7 @@ export const GenericModal = (props: Omit & ModalProps) = closeButton, hideFooter, modalSize, + variant, className, headerIcon, ...rest @@ -144,7 +147,7 @@ export const GenericModal = (props: Omit & ModalProps) = return <>{headerIcon}; } - switch (className) { + switch (variant) { case 'info': case 'warning': { return ; @@ -158,15 +161,32 @@ export const GenericModal = (props: Omit & ModalProps) = } }; + const getVariantClass = () => { + switch (variant) { + case 'info': + return 'info-variant'; + case 'warning': { + return 'warning-variant'; + } + case 'error': { + return 'error-variant'; + } + default: { + return ''; + } + } + }; + const headerIconValue = getHeaderIcon(); return ( @@ -238,6 +258,114 @@ const StyledModal = styled(Modal)` font-family: 'Helvetica Narrow'; } } + + .modal-header { + height: 4.8rem; + padding: 0 1.6rem; + display: flex; + flex-direction: row; + align-items: center; + color: ${props => props.theme.css.primaryBackgroundColor}; //$primary-background-color; + background-color: ${props => props.theme.css.primaryColor}; //$primary-color; + + .modal-title { + font-family: BcSans-Bold; + font-size: 2.2rem; + height: 2.75rem; + height: auto; + } + + .header-icon { + margin-right: 8px; + display: inline-block; + } + + .modal-close-btn { + cursor: pointer; + } + } + + .modal-body { + padding: 2.4rem 1.8rem; + font-size: 1.8rem; + } + + .modal-footer { + border-top: none; + + hr { + width: 100%; + } + + .button-wrap { + display: inline-flex; + margin-top: 2.4rem; + margin-bottom: 2.4rem; + + button { + margin-right: 2.4rem; + min-width: 9.5rem; + height: 3.9rem; + } + } + } + + .close { + color: black; + } + + &.modal-xl { + max-width: 100rem; + } + + &.modal-l { + max-width: 75rem; + } + + &.modal-m { + max-width: 50rem; + } + + &.modal-s { + max-width: 40rem; + } + + &.info-variant { + .modal-header { + color: ${props => props.theme.css.darkBlue}; //$dark-blue; + background-color: ${props => props.theme.css.filterBoxColor}; //$filter-box-color; + } + + .modal-close-btn { + color: ${props => props.theme.css.textColor}; //$text-color; + cursor: pointer; + } + } + + &.error-variant { + .modal-header { + color: ${props => props.theme.css.fontDangerColor}; //$font-danger-color; + background-color: ${props => + props.theme.css.dangerBackgroundColor}; //$danger-background-color; + } + + .modal-close-btn { + color: ${props => props.theme.css.textColor}; //$text-color; + cursor: pointer; + } + } + + &.warning-variant { + .modal-header { + color: ${props => props.theme.css.fontWarningColor}; //$font-warning-color; + background-color: ${props => props.theme.css.summaryColor}; //$summary-color; + } + + .modal-close-btn { + color: $text-color; + cursor: pointer; + } + } `; const PopupContainer = styled.div` diff --git a/source/frontend/src/components/common/ModalContainer.test.tsx b/source/frontend/src/components/common/ModalContainer.test.tsx index f104e9491c..5fd5dd193c 100644 --- a/source/frontend/src/components/common/ModalContainer.test.tsx +++ b/source/frontend/src/components/common/ModalContainer.test.tsx @@ -25,7 +25,7 @@ describe('ModalContainer component', () => { // render component under test const component = render( <> - + , { ...renderOptions, @@ -44,7 +44,7 @@ describe('ModalContainer component', () => { }); it('displays a modal based on props', async () => { - setup({ modalProps: { title: 'test', message: 'test 2' }, isVisible: true }); + setup({ modalProps: { variant: 'info', title: 'test', message: 'test 2' }, isVisible: true }); expect(await screen.findByText('test')).toBeVisible(); expect(await screen.findByText('test 2')).toBeVisible(); @@ -52,7 +52,7 @@ describe('ModalContainer component', () => { it('shows/hides modal', async () => { setup({ - modalProps: { title: 'test', message: 'test 2', okButtonText: 'ok' }, + modalProps: { variant: 'info', title: 'test', message: 'test 2', okButtonText: 'ok' }, isVisible: true, }); diff --git a/source/frontend/src/components/common/form/NotesModal.tsx b/source/frontend/src/components/common/form/NotesModal.tsx index 169c88fea7..a276eab42d 100644 --- a/source/frontend/src/components/common/form/NotesModal.tsx +++ b/source/frontend/src/components/common/form/NotesModal.tsx @@ -42,7 +42,7 @@ export const NotesModal: React.FunctionComponent { planNumberInventoryData = await loadPropertiesTask; } catch (err) { setModalContent({ - className: 'info', + variant: 'error', title: 'Unable to connect to PIMS Inventory', message: 'PIMS is unable to connect to connect to the PIMS Inventory map service. You may need to log out and log into the application in order to restore this functionality. If this error persists, contact a site administrator.', @@ -235,6 +235,7 @@ export const useMapSearch = () => { pidPinInventoryData = await loadPropertiesTask; } catch (err) { setModalContent({ + variant: 'error', title: 'Unable to connect to PIMS Inventory', message: 'PIMS is unable to connect to connect to the PIMS Inventory map service. You may need to log out and log into the application in order to restore this functionality. If this error persists, contact a site administrator.', diff --git a/source/frontend/src/components/contact/ContactManagerModal.tsx b/source/frontend/src/components/contact/ContactManagerModal.tsx index f34948d8f1..4538a2aa41 100644 --- a/source/frontend/src/components/contact/ContactManagerModal.tsx +++ b/source/frontend/src/components/contact/ContactManagerModal.tsx @@ -23,7 +23,7 @@ export const ContactManagerModal: React.FunctionComponent< > = props => { return ( return ( void; setDisplayModal: (display: boolean) => void; } export const ModalContext = React.createContext({ - modalProps: undefined, + modalProps: { variant: 'info' }, setDisplayModal: (display: boolean) => { throw Error('setDisplayModal function not defined'); }, @@ -22,13 +22,14 @@ export const ModalContext = React.createContext({ export const ModalContextProvider = (props: { children: React.ReactChild | React.ReactChild[] | React.ReactNode; }) => { - const [modalProps, setModalProps] = useState(undefined); + const [modalProps, setModalProps] = useState({ variant: 'info' }); const [showModal, setShowModal] = useState(false); const updateFunction = React.useCallback( (updatedModalContent?: ModalContent) => { setModalProps({ ...updatedModalContent, + variant: updatedModalContent?.variant ?? 'info', display: showModal, setDisplay: setShowModal, }); diff --git a/source/frontend/src/features/contacts/contact/create/Organization/CreateOrganizationForm.tsx b/source/frontend/src/features/contacts/contact/create/Organization/CreateOrganizationForm.tsx index 2a1556897d..d85ceb6925 100644 --- a/source/frontend/src/features/contacts/contact/create/Organization/CreateOrganizationForm.tsx +++ b/source/frontend/src/features/contacts/contact/create/Organization/CreateOrganizationForm.tsx @@ -82,6 +82,7 @@ export const CreateOrganizationForm: React.FunctionComponent = () => { innerRef={formikRef} /> saveDuplicate()} handleCancel={() => { @@ -136,6 +137,7 @@ const CreateOrganizationComponent: React.FC { diff --git a/source/frontend/src/features/contacts/contact/create/Person/CreatePersonForm.tsx b/source/frontend/src/features/contacts/contact/create/Person/CreatePersonForm.tsx index 388ede08c9..05cd0cfc7f 100644 --- a/source/frontend/src/features/contacts/contact/create/Person/CreatePersonForm.tsx +++ b/source/frontend/src/features/contacts/contact/create/Person/CreatePersonForm.tsx @@ -81,6 +81,7 @@ export const CreatePersonForm: React.FunctionComponent saveDuplicate()} handleCancel={() => { @@ -164,6 +165,7 @@ const CreatePersonComponent: React.FC< {/* Confirmation popup when Cancel button is clicked */} { diff --git a/source/frontend/src/features/contacts/contact/edit/Organization/UpdateOrganizationForm.tsx b/source/frontend/src/features/contacts/contact/edit/Organization/UpdateOrganizationForm.tsx index 9c4d9b9e57..85935b8c6b 100644 --- a/source/frontend/src/features/contacts/contact/edit/Organization/UpdateOrganizationForm.tsx +++ b/source/frontend/src/features/contacts/contact/edit/Organization/UpdateOrganizationForm.tsx @@ -140,6 +140,7 @@ const UpdateOrganization: React.FC> = ({ {/* Confirmation popup when Cancel button is clicked */} { diff --git a/source/frontend/src/features/contacts/contact/edit/Person/UpdatePersonForm.tsx b/source/frontend/src/features/contacts/contact/edit/Person/UpdatePersonForm.tsx index 7e424a4477..f81b5c9df9 100644 --- a/source/frontend/src/features/contacts/contact/edit/Person/UpdatePersonForm.tsx +++ b/source/frontend/src/features/contacts/contact/edit/Person/UpdatePersonForm.tsx @@ -167,6 +167,7 @@ const UpdatePersonComponent: React.FC< {/* Confirmation popup when Cancel button is clicked */} { diff --git a/source/frontend/src/features/documents/documentDetail/DocumentDetailModal.tsx b/source/frontend/src/features/documents/documentDetail/DocumentDetailModal.tsx index 9ea4e61f02..ee1150b4e1 100644 --- a/source/frontend/src/features/documents/documentDetail/DocumentDetailModal.tsx +++ b/source/frontend/src/features/documents/documentDetail/DocumentDetailModal.tsx @@ -20,7 +20,7 @@ export const DocumentDetailModal: React.FunctionComponent< > = props => { return ( } display={props.display} setDisplay={props.setDisplay} diff --git a/source/frontend/src/features/documents/documentUpload/DocumentUploadModal.tsx b/source/frontend/src/features/documents/documentUpload/DocumentUploadModal.tsx index e6c4accaae..82669a4012 100644 --- a/source/frontend/src/features/documents/documentUpload/DocumentUploadModal.tsx +++ b/source/frontend/src/features/documents/documentUpload/DocumentUploadModal.tsx @@ -19,9 +19,9 @@ export const DocumentUploadModal: React.FunctionComponent< > = props => { return ( } title={'Add a Document'} diff --git a/source/frontend/src/features/documents/list/DocumentListView.tsx b/source/frontend/src/features/documents/list/DocumentListView.tsx index 9095848933..877696a799 100644 --- a/source/frontend/src/features/documents/list/DocumentListView.tsx +++ b/source/frontend/src/features/documents/list/DocumentListView.tsx @@ -206,7 +206,7 @@ export const DocumentListView: React.FunctionComponent< onClose={handleModalUploadClose} /> setConfirmDeleteModalValues(undefined)} /> org.summary); return ( { return { - className: 'info', + variant: 'info', title: 'Not inventory property', message: 'You have selected a property not previously in the inventory. Do you want to add this property to the lease?', @@ -159,6 +159,7 @@ export const LeasePropertySelector: React.FunctionComponent { return { + variant: 'info', title: 'Removing Property from form', message: 'Are you sure you want to remove this property from this lease/license?', display: false, diff --git a/source/frontend/src/features/mapSideBar/acquisition/AcquisitionView.test.tsx b/source/frontend/src/features/mapSideBar/acquisition/AcquisitionView.test.tsx index f8ac7c6efb..066d6d166c 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/AcquisitionView.test.tsx +++ b/source/frontend/src/features/mapSideBar/acquisition/AcquisitionView.test.tsx @@ -196,12 +196,12 @@ describe('AcquisitionView component', () => { it(`should show a toast and redirect to the File Details page when accessing a non-existing property index`, async () => { history.replace(`/mapview/sidebar/acquisition/1/property/99999`); - const { getByRole, findByText } = await setup(); + const { getByRole, findByText } = await act(() => setup()); const tab = getByRole('tab', { name: /File details/i }); expect(tab).toBeVisible(); expect(tab).toHaveClass('active'); // toast - expect(await findByText(/Could not find property in the file/)).toBeVisible(); + expect(await findByText(/Could not find property in the file/i)).toBeVisible(); }); it('should display the Property Selector according to routing', async () => { diff --git a/source/frontend/src/features/mapSideBar/acquisition/AcquisitionView.tsx b/source/frontend/src/features/mapSideBar/acquisition/AcquisitionView.tsx index 9084393eda..7a6b2d5cb7 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/AcquisitionView.tsx +++ b/source/frontend/src/features/mapSideBar/acquisition/AcquisitionView.tsx @@ -192,7 +192,7 @@ export const AcquisitionView: React.FunctionComponent = ( /> ; +export type IRemoveTeamMemberModalProps = Omit< + ModalProps, + 'variant' | 'okButtonText' | 'cancelButtonText' +>; export const AcquisitionFormModal = (props: IRemoveTeamMemberModalProps) => ( = !!agreement.cancellationNote ) { setModalContent({ + variant: 'warning', okButtonText: 'Yes', cancelButtonText: 'No', message: diff --git a/source/frontend/src/features/mapSideBar/acquisition/tabs/compensation/update/UpdateCompensationRequisitionForm.tsx b/source/frontend/src/features/mapSideBar/acquisition/tabs/compensation/update/UpdateCompensationRequisitionForm.tsx index 1ae3177386..a085766206 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/tabs/compensation/update/UpdateCompensationRequisitionForm.tsx +++ b/source/frontend/src/features/mapSideBar/acquisition/tabs/compensation/update/UpdateCompensationRequisitionForm.tsx @@ -325,6 +325,7 @@ const UpdateCompensationRequisitionForm: React.FC ) => { setModalContent({ + variant: 'error', title: 'Warning', message: axiosError?.response?.data.error, okButtonText: 'Close', diff --git a/source/frontend/src/features/mapSideBar/lease/LeaseContainer.tsx b/source/frontend/src/features/mapSideBar/lease/LeaseContainer.tsx index 66ec46ea97..85aaca30df 100644 --- a/source/frontend/src/features/mapSideBar/lease/LeaseContainer.tsx +++ b/source/frontend/src/features/mapSideBar/lease/LeaseContainer.tsx @@ -229,7 +229,7 @@ export const LeaseContainer: React.FC = ({ leaseId, onClos } > = ({ onSuccess={onSuccess} /> }} /> = const onDelete = useCallback( (id: number) => { setModalContent({ + variant: 'info', title: 'Confirm delete', message: 'This contact will be removed from the Property contacts. Do you wish to proceed?', okButtonText: 'Confirm', diff --git a/source/frontend/src/features/mapSideBar/property/tabs/takes/update/TakeSubForm.tsx b/source/frontend/src/features/mapSideBar/property/tabs/takes/update/TakeSubForm.tsx index cc9c0ab15f..0aef36aa86 100644 --- a/source/frontend/src/features/mapSideBar/property/tabs/takes/update/TakeSubForm.tsx +++ b/source/frontend/src/features/mapSideBar/property/tabs/takes/update/TakeSubForm.tsx @@ -53,6 +53,7 @@ const TakeSubForm: React.FunctionComponent = ({ return (e: React.ChangeEvent) => { if (e.target.value === 'false') { setModalContent({ + variant: 'info', title: 'Confirm change', message: 'The area, if provided, will be cleared. Do you wish to proceed?', okButtonText: 'Confirm', diff --git a/source/frontend/src/features/mapSideBar/research/ResearchContainer.tsx b/source/frontend/src/features/mapSideBar/research/ResearchContainer.tsx index a35594da40..d8b7ee3161 100644 --- a/source/frontend/src/features/mapSideBar/research/ResearchContainer.tsx +++ b/source/frontend/src/features/mapSideBar/research/ResearchContainer.tsx @@ -268,6 +268,7 @@ export const ResearchContainer: React.FunctionComponent< <> { if (getAgreementsReport?.status === 204) { setModalContent({ + variant: 'error', title: 'Warning', message: 'There is no data for the input parameters you entered.', okButtonText: 'Close', @@ -70,6 +71,7 @@ export const SideProjectContainer: React.FunctionComponent { if (statusCompensation === 204) { setModalContent({ + variant: 'error', title: 'Warning', message: 'There is no data for the input parameters you entered.', okButtonText: 'Close', diff --git a/source/frontend/src/hooks/useApiUserOverride.ts b/source/frontend/src/hooks/useApiUserOverride.ts index 83a1be74db..f208b78eac 100644 --- a/source/frontend/src/hooks/useApiUserOverride.ts +++ b/source/frontend/src/hooks/useApiUserOverride.ts @@ -75,7 +75,7 @@ export const useApiUserOverride = < state.userOverrideCode !== UserOverrideCode.CONTRACTOR_SELFREMOVED ) { setModalContent({ - className: 'warning', + variant: 'warning', title: 'User Override Required', message: state?.message, handleOk: async () => { @@ -111,6 +111,7 @@ export const useApiUserOverride = < ) { setModalContent({ title: 'Note', + variant: 'info', message: RemoveSelfContractorContent(), handleOk: async () => { setState({ diff --git a/source/frontend/src/hooks/useModalContext.ts b/source/frontend/src/hooks/useModalContext.ts index 487b5d1635..51bdeb3c0a 100644 --- a/source/frontend/src/hooks/useModalContext.ts +++ b/source/frontend/src/hooks/useModalContext.ts @@ -25,16 +25,18 @@ export const useModalContext = (newModalContent?: ModalContent, isVisible?: bool }; }; -export const getCancelModalProps = () => ({ +export const getCancelModalProps = (): ModalContent => ({ title: 'Unsaved Changes', message: 'You have made changes on this form. Do you wish to leave without saving?', okButtonText: 'Confirm', cancelButtonText: 'No', + variant: 'info', }); -export const getDeleteModalProps = () => ({ +export const getDeleteModalProps = (): ModalContent => ({ title: 'Confirm Delete', message: 'Are you sure you want to delete this item?', okButtonText: 'Continue', cancelButtonText: 'Cancel', + variant: 'info', }); diff --git a/source/frontend/src/hooks/usePimsIdleTimer.tsx b/source/frontend/src/hooks/usePimsIdleTimer.tsx index 0ab901aa83..55e18401b3 100644 --- a/source/frontend/src/hooks/usePimsIdleTimer.tsx +++ b/source/frontend/src/hooks/usePimsIdleTimer.tsx @@ -18,6 +18,7 @@ const usePimsIdleTimer = () => { const onPrompt = () => { setModalContent({ className: 'info', + variant: 'info', title: 'Still Working?', message: 'You have been idle for some time. Would you like to remain logged in?', okButtonText: 'Keep working', diff --git a/source/frontend/src/utils/TestCommonWrapper.tsx b/source/frontend/src/utils/TestCommonWrapper.tsx index d9f7d0bc02..c7cb75c9fc 100644 --- a/source/frontend/src/utils/TestCommonWrapper.tsx +++ b/source/frontend/src/utils/TestCommonWrapper.tsx @@ -5,7 +5,6 @@ import { ToastContainer } from 'react-toastify'; import { ThemeProvider } from 'styled-components'; import ModalContainer from '@/components/common/ModalContainer'; -//import { MapStateContextProvider } from '@/components/maps/providers/MapStateContext_old'; import { ModalContextProvider } from '@/contexts/modalContext'; import { IOrganization } from '@/interfaces'; import { TenantConsumer, TenantProvider } from '@/tenants'; From 09d286e481cfe5d3337f654266cf9d680c893c77 Mon Sep 17 00:00:00 2001 From: Manuel Rodriguez Date: Fri, 1 Dec 2023 11:10:41 -0800 Subject: [PATCH 2/4] Improved System error modal --- .../src/components/common/GenericModal.tsx | 23 ++-- .../components/layout/Header/ErrorModal.tsx | 122 ++++++++++++------ .../Header/__snapshots__/Header.test.tsx.snap | 1 + ...enerateLetterRecipientsModal.test.tsx.snap | 1 - .../__snapshots__/NoteContainer.test.tsx.snap | 1 - .../AddNotesContainer.test.tsx.snap | 1 - .../AddNotesFormModal.test.tsx.snap | 1 - .../NoteDetailsFormModal.test.tsx.snap | 1 - .../UpdateNoteContainer.test.tsx.snap | 1 - .../UpdateNoteFormModal.test.tsx.snap | 1 - .../frontend/src/hooks/usePimsIdleTimer.tsx | 1 - 11 files changed, 94 insertions(+), 60 deletions(-) diff --git a/source/frontend/src/components/common/GenericModal.tsx b/source/frontend/src/components/common/GenericModal.tsx index 5219baf05c..3a554029b0 100644 --- a/source/frontend/src/components/common/GenericModal.tsx +++ b/source/frontend/src/components/common/GenericModal.tsx @@ -265,8 +265,8 @@ const StyledModal = styled(Modal)` display: flex; flex-direction: row; align-items: center; - color: ${props => props.theme.css.primaryBackgroundColor}; //$primary-background-color; - background-color: ${props => props.theme.css.primaryColor}; //$primary-color; + color: ${props => props.theme.css.primaryBackgroundColor}; + background-color: ${props => props.theme.css.primaryColor}; .modal-title { font-family: BcSans-Bold; @@ -332,37 +332,36 @@ const StyledModal = styled(Modal)` &.info-variant { .modal-header { - color: ${props => props.theme.css.darkBlue}; //$dark-blue; - background-color: ${props => props.theme.css.filterBoxColor}; //$filter-box-color; + color: ${props => props.theme.css.darkBlue}; + background-color: ${props => props.theme.css.filterBoxColor}; } .modal-close-btn { - color: ${props => props.theme.css.textColor}; //$text-color; + color: ${props => props.theme.css.textColor}; cursor: pointer; } } &.error-variant { .modal-header { - color: ${props => props.theme.css.fontDangerColor}; //$font-danger-color; - background-color: ${props => - props.theme.css.dangerBackgroundColor}; //$danger-background-color; + color: ${props => props.theme.css.fontDangerColor}; + background-color: ${props => props.theme.css.dangerBackgroundColor}; } .modal-close-btn { - color: ${props => props.theme.css.textColor}; //$text-color; + color: ${props => props.theme.css.textColor}; cursor: pointer; } } &.warning-variant { .modal-header { - color: ${props => props.theme.css.fontWarningColor}; //$font-warning-color; - background-color: ${props => props.theme.css.summaryColor}; //$summary-color; + color: ${props => props.theme.css.fontWarningColor}; + background-color: ${props => props.theme.css.summaryColor}; } .modal-close-btn { - color: $text-color; + color: ${props => props.theme.css.textColor}; cursor: pointer; } } diff --git a/source/frontend/src/components/layout/Header/ErrorModal.tsx b/source/frontend/src/components/layout/Header/ErrorModal.tsx index afd13770c0..cb1a27b875 100644 --- a/source/frontend/src/components/layout/Header/ErrorModal.tsx +++ b/source/frontend/src/components/layout/Header/ErrorModal.tsx @@ -1,10 +1,10 @@ -import React from 'react'; -import Col from 'react-bootstrap/Col'; -import Modal from 'react-bootstrap/Modal'; -import Row from 'react-bootstrap/Row'; import { useDispatch } from 'react-redux'; +import styled from 'styled-components'; -import { Button } from '@/components/common/buttons/Button'; +import GenericModal from '@/components/common/GenericModal'; +import { Section } from '@/components/common/Section/Section'; +import { SectionField } from '@/components/common/Section/SectionField'; +import { StyledGreySection } from '@/features/documents/commonStyles'; import { IGenericNetworkAction } from '@/store/slices/network/interfaces'; import { logClear } from '@/store/slices/network/networkSlice'; @@ -27,49 +27,91 @@ export interface IErrorModalProps { */ export const ErrorModal = ({ errors, show, setShow }: IErrorModalProps) => { const dispatch = useDispatch(); - const handleClose = () => setShow(false); + const handleClose = () => { + setShow(false); + }; const handleClear = () => { errors.forEach(error => dispatch(logClear(error.name))); setShow(false); }; - return ( - - - Errors - + const errorUrl = (error: IGenericNetworkAction): string => { + return error?.error?.response?.config?.url || ''; + }; - - {errors.map((error: IGenericNetworkAction, index: number) => ( - - {process.env.NODE_ENV === 'development' ? ( - - - {error?.error?.response?.config?.url?.substr(0, 20)} - - : {error?.error?.response?.statusText} data:{' '} - {JSON.stringify(error?.error?.response?.data)} - - ) : ( - - - {error?.error?.response?.config?.url?.substr(0, 20)} - - : ({error?.error?.response?.statusText ?? 'unknown'}){' '} - {(error?.error?.response?.data as unknown & { error: string })?.error ?? ''} - - )} - - ))} - + const errorShortUrl = (error: IGenericNetworkAction): string => { + var url = errorUrl(error); + if (url.length > 20) { + return error?.error?.response?.config?.url?.substr(0, 20) + '...'; + } else { + return url; + } + }; - - - - + const errorStatus = (error: IGenericNetworkAction): string => { + return error?.error?.response?.statusText || 'unknown'; + }; + + return ( + + {errors.map((error: IGenericNetworkAction, index: number) => ( + + {process.env.NODE_ENV === 'development' ? ( +
+ + {errorStatus(error)} + + + {errorUrl(error)} + + + + {JSON.stringify(error?.error?.response?.data)} + + +
+ ) : ( +
+ + {errorStatus(error)} + + + {errorUrl(error)} + + + + {(error?.error?.response?.data as unknown & { error: string })?.error ?? ''} + + +
+ )} +
+ ))} + + } + /> ); }; export default ErrorModal; + +const ErrorWrapper = styled(StyledGreySection)` + max-height: 70rem; + overflow-y: scroll; + width: 100%; + padding: 0; +`; + +const ErrorEntry = styled.div``; + +const ErrorDescription = styled.div` + word-break: break-all; +`; diff --git a/source/frontend/src/components/layout/Header/__snapshots__/Header.test.tsx.snap b/source/frontend/src/components/layout/Header/__snapshots__/Header.test.tsx.snap index c8e836e0a6..2a1a5855eb 100644 --- a/source/frontend/src/components/layout/Header/__snapshots__/Header.test.tsx.snap +++ b/source/frontend/src/components/layout/Header/__snapshots__/Header.test.tsx.snap @@ -181,6 +181,7 @@ exports[`App Header renders correctly 1`] = `