-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
1 parent
92c7f72
commit 4693e2b
Showing
9 changed files
with
314 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,2 @@ | ||
# This file is generated by the 'io.freefair.lombok' Gradle plugin | ||
config.stopBubbling = true | ||
lombok.extern.findbugs.addSuppressFBWarnings = true |
46 changes: 46 additions & 0 deletions
46
ui-admin/src/forms/designer/modals/DiscardLocalDraftModal.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import React from 'react' | ||
import { render, screen } from '@testing-library/react' | ||
import userEvent from '@testing-library/user-event' | ||
import DiscardLocalDraftModal from './DiscardLocalDraftModal' | ||
|
||
describe('DiscardLocalDraftModal', () => { | ||
test('allows discarding drafts on exit', async () => { | ||
//Arrange | ||
const user = userEvent.setup() | ||
const FORM_DRAFT_KEY = 'surveyDraft_testForm_12' | ||
render(<DiscardLocalDraftModal | ||
formDraftKey={FORM_DRAFT_KEY} | ||
onExit={() => jest.fn()} | ||
onSaveDraft={() => jest.fn()} | ||
onDismiss={() => jest.fn()} | ||
/>) | ||
|
||
//Act | ||
jest.spyOn(Storage.prototype, 'removeItem') | ||
const exitAndDiscardButton = screen.getByText('Exit & discard draft') | ||
await user.click(exitAndDiscardButton) | ||
|
||
//Assert | ||
expect(localStorage.removeItem).toHaveBeenCalledWith(FORM_DRAFT_KEY) | ||
}) | ||
|
||
test('allows keeping drafts on exit', async () => { | ||
//Arrange | ||
const user = userEvent.setup() | ||
const FORM_DRAFT_KEY = 'surveyDraft_testForm_12' | ||
render(<DiscardLocalDraftModal | ||
formDraftKey={FORM_DRAFT_KEY} | ||
onExit={() => jest.fn()} | ||
onSaveDraft={() => jest.fn()} | ||
onDismiss={() => jest.fn()} | ||
/>) | ||
|
||
//Act | ||
jest.spyOn(Storage.prototype, 'removeItem') | ||
const exitAndDiscardButton = screen.getByText('Exit & save draft') | ||
await user.click(exitAndDiscardButton) | ||
|
||
//Assert | ||
expect(localStorage.removeItem).not.toHaveBeenCalled() | ||
}) | ||
}) |
54 changes: 54 additions & 0 deletions
54
ui-admin/src/forms/designer/modals/DiscardLocalDraftModal.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { Modal, ModalFooter } from 'react-bootstrap' | ||
import { Button } from 'components/forms/Button' | ||
import React from 'react' | ||
import { deleteDraft } from '../utils/formDraftUtils' | ||
|
||
/** | ||
* Modal presenting the user with the option to discard a local draft. Shown on Cancel if there is a local draft. | ||
*/ | ||
const DiscardLocalDraftModal = ({ formDraftKey, onExit, onSaveDraft, onDismiss }: { | ||
formDraftKey: string | ||
onExit: () => void | ||
onSaveDraft: () => void | ||
onDismiss: () => void | ||
}) => { | ||
return <Modal show={true} onHide={onDismiss}> | ||
<Modal.Header> | ||
<Modal.Title>Unsaved Changes</Modal.Title> | ||
</Modal.Header> | ||
<Modal.Body> | ||
<p>Are you sure you want to cancel? You have an unsaved survey draft. You can save the | ||
draft and return to edit it later, or discard the draft if it is no longer needed.</p> | ||
</Modal.Body> | ||
<ModalFooter> | ||
<Button | ||
variant="primary" | ||
onClick={() => { | ||
onSaveDraft() | ||
onDismiss() | ||
onExit() | ||
}} | ||
> | ||
Exit & save draft | ||
</Button> | ||
<Button | ||
variant="danger" | ||
onClick={() => { | ||
deleteDraft({ formDraftKey }) | ||
onDismiss() | ||
onExit() | ||
}} | ||
> | ||
Exit & discard draft | ||
</Button> | ||
<Button | ||
variant="secondary" | ||
onClick={onDismiss} | ||
> | ||
Cancel | ||
</Button> | ||
</ModalFooter> | ||
</Modal> | ||
} | ||
|
||
export default DiscardLocalDraftModal |
32 changes: 32 additions & 0 deletions
32
ui-admin/src/forms/designer/modals/LoadedLocalDraftModal.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import React from 'react' | ||
import Modal from 'react-bootstrap/Modal' | ||
import { Button } from 'components/forms/Button' | ||
|
||
/** | ||
* Shown to the user if the form editor is opened and we detect a local draft. | ||
*/ | ||
const LoadedLocalDraftModal = ({ onDismiss, lastUpdated }: { | ||
onDismiss: () => void | ||
lastUpdated: number | undefined | ||
}) => { | ||
return <Modal show={true} onHide={onDismiss}> | ||
<Modal.Header> | ||
<Modal.Title>Survey Draft Loaded</Modal.Title> | ||
</Modal.Header> | ||
<Modal.Body> | ||
<p>A previously unsaved survey draft was automatically loaded. | ||
Be sure to save the survey in order to publish your changes.</p> | ||
{lastUpdated && <span>This draft was last updated {new Date(lastUpdated).toLocaleString()}</span> } | ||
</Modal.Body> | ||
<Modal.Footer> | ||
<Button | ||
variant="primary" | ||
onClick={onDismiss} | ||
> | ||
Continue | ||
</Button> | ||
</Modal.Footer> | ||
</Modal> | ||
} | ||
|
||
export default LoadedLocalDraftModal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { VersionedForm } from '@juniper/ui-core' | ||
|
||
export type FormDraft = { | ||
content: string | ||
date: number | ||
} | ||
|
||
/** returns a form draft key for local storage */ | ||
export function getFormDraftKey({ form }: { form: VersionedForm }) { | ||
return `formDraft_${form.stableId}_${form.version}` | ||
} | ||
|
||
/** returns a form draft from local storage, if there is one */ | ||
export function getDraft({ formDraftKey }: { formDraftKey: string }): FormDraft | undefined { | ||
const draft = localStorage.getItem(formDraftKey) | ||
if (!draft) { | ||
return undefined | ||
} else { | ||
const draftParsed: FormDraft = JSON.parse(draft) | ||
return draftParsed | ||
} | ||
} | ||
|
||
/** saves a form draft to local storage with the current timestamp, if there is one */ | ||
export function saveDraft({ formDraftKey, draft, setSavingDraft }: { | ||
formDraftKey: string, | ||
draft: FormDraft | ||
setSavingDraft: (saving: boolean) => void | ||
}) { | ||
setSavingDraft(true) | ||
localStorage.setItem(formDraftKey, JSON.stringify(draft)) | ||
//Saving a draft happens so quickly that the "Saving draft..." message isn't even visible to the user. | ||
//Set a timeout to show it for 2 seconds so the user knows that their drafts are being saved. | ||
setTimeout(() => { | ||
setSavingDraft(false) | ||
}, 2000) | ||
} | ||
|
||
/** deletes a form draft from local storage */ | ||
export function deleteDraft({ formDraftKey }: { formDraftKey: string }) { | ||
localStorage.removeItem(formDraftKey) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import React from 'react' | ||
import { render, screen } from '@testing-library/react' | ||
import SurveyEditorView from './SurveyEditorView' | ||
import { getFormDraftKey } from '../../forms/designer/utils/formDraftUtils' | ||
import { VersionedForm } from '@juniper/ui-core' | ||
|
||
describe('SurveyEditorView', () => { | ||
const mockForm: VersionedForm = { | ||
id: 'testForm', | ||
version: 12, | ||
content: '{}', | ||
stableId: 'testStableId', | ||
name: '', | ||
createdAt: 0, | ||
lastUpdatedAt: 0 | ||
} | ||
|
||
test('shows the user a LoadedLocalDraftModal when a draft is loaded', async () => { | ||
//Arrange | ||
const FORM_DRAFT_KEY = getFormDraftKey({ form: mockForm }) | ||
localStorage.setItem(FORM_DRAFT_KEY, JSON.stringify({})) | ||
|
||
jest.spyOn(Storage.prototype, 'getItem') | ||
|
||
render(<SurveyEditorView | ||
currentForm={mockForm} | ||
onCancel={jest.fn()} | ||
onSave={jest.fn()} | ||
/>) | ||
|
||
//Assert | ||
const modalHeader = screen.getByText('Survey Draft Loaded') | ||
expect(modalHeader).toBeInTheDocument() | ||
}) | ||
|
||
test('checks local storage for a draft', async () => { | ||
//Arrange | ||
const FORM_DRAFT_KEY = getFormDraftKey({ form: mockForm }) | ||
|
||
jest.spyOn(Storage.prototype, 'getItem') | ||
|
||
render(<SurveyEditorView | ||
currentForm={mockForm} | ||
onCancel={jest.fn()} | ||
onSave={jest.fn()} | ||
/>) | ||
|
||
//Assert | ||
expect(localStorage.getItem).toHaveBeenCalledWith(FORM_DRAFT_KEY) | ||
expect(screen.queryByText('Survey Draft Loaded')).not.toBeInTheDocument() | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { useEffect } from 'react' | ||
|
||
/** TODO JSdoc */ | ||
export function useAutosaveEffect(saveFn: () => void, autoSaveInterval: number) { | ||
useEffect(() => { | ||
let timeoutHandle: number | ||
// run saveFn at the specified interval | ||
(function loop() { | ||
timeoutHandle = window.setTimeout(() => { | ||
saveFn() | ||
loop() | ||
}, autoSaveInterval) | ||
})() | ||
return () => { | ||
window.clearTimeout(timeoutHandle) | ||
} | ||
}, [saveFn]) | ||
} |
Oops, something went wrong.