Skip to content

Commit

Permalink
feat: standardize modal behavior (#215)
Browse files Browse the repository at this point in the history
  • Loading branch information
thenick775 authored Nov 19, 2024
1 parent 1c1068b commit fee3c1f
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 101 deletions.
41 changes: 24 additions & 17 deletions gbajs3/src/components/modals/upload-cheats.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@ import { productTourLocalStorageKey } from '../product-tour/consts.tsx';
import type { GBAEmulator } from '../../emulator/mgba/mgba-emulator.tsx';

describe('<UploadCheatsModal />', () => {
it('uploads file', async () => {
it('uploads file and closes modal', async () => {
const uploadCheatsSpy: (file: File, cb?: () => void) => void = vi.fn(
(_file, cb) => cb && cb()
);
const syncActionIfEnabledSpy = vi.fn();
const setIsModalOpenSpy = vi.fn();

const { useEmulatorContext: originalEmulator } = await vi.importActual<
typeof contextHooks
>('../../hooks/context.tsx');
const {
useEmulatorContext: originalEmulator,
useModalContext: originalModal
} = await vi.importActual<typeof contextHooks>('../../hooks/context.tsx');
const { useAddCallbacks: originalCallbacks } = await vi.importActual<
typeof addCallbackHooks
>('../../hooks/emulator/use-add-callbacks.tsx');
Expand All @@ -36,6 +38,11 @@ describe('<UploadCheatsModal />', () => {
syncActionIfEnabled: syncActionIfEnabledSpy
}));

vi.spyOn(contextHooks, 'useModalContext').mockImplementation(() => ({
...originalModal(),
setIsModalOpen: setIsModalOpenSpy
}));

const testCheatFile = new File(['Some cheat file contents'], 'rom1.cheats');

renderWithContext(<UploadCheatsModal />);
Expand All @@ -57,21 +64,20 @@ describe('<UploadCheatsModal />', () => {
expect.anything()
);
expect(syncActionIfEnabledSpy).toHaveBeenCalledOnce();

expect(screen.getByText('Upload complete!')).toBeVisible();
expect(screen.queryByText('File to upload:')).not.toBeInTheDocument();
expect(screen.queryByText('rom1.cheats')).not.toBeInTheDocument();
expect(setIsModalOpenSpy).toHaveBeenCalledWith(false);
});

it('uploads multiple files', async () => {
it('uploads multiple files and closes modal', async () => {
const uploadCheatsSpy: (file: File, cb?: () => void) => void = vi.fn(
(_file, cb) => cb && cb()
);
const syncActionIfEnabledSpy = vi.fn();
const setIsModalOpenSpy = vi.fn();

const { useEmulatorContext: originalEmulator } = await vi.importActual<
typeof contextHooks
>('../../hooks/context.tsx');
const {
useEmulatorContext: originalEmulator,
useModalContext: originalModal
} = await vi.importActual<typeof contextHooks>('../../hooks/context.tsx');
const { useAddCallbacks: originalCallbacks } = await vi.importActual<
typeof addCallbackHooks
>('../../hooks/emulator/use-add-callbacks.tsx');
Expand All @@ -88,6 +94,11 @@ describe('<UploadCheatsModal />', () => {
syncActionIfEnabled: syncActionIfEnabledSpy
}));

vi.spyOn(contextHooks, 'useModalContext').mockImplementation(() => ({
...originalModal(),
setIsModalOpen: setIsModalOpenSpy
}));

const testCheatFiles = [
new File(['Some cheat file contents 1'], 'rom1.cheats'),
new File(['Some cheat file contents 2'], 'rom2.cheats')
Expand Down Expand Up @@ -117,11 +128,7 @@ describe('<UploadCheatsModal />', () => {
expect.anything()
);
expect(syncActionIfEnabledSpy).toHaveBeenCalledOnce();

expect(screen.getByText('Upload complete!')).toBeVisible();
expect(screen.queryByText('Files to upload:')).not.toBeInTheDocument();
expect(screen.queryByText('rom1.cheats')).not.toBeInTheDocument();
expect(screen.queryByText('rom2.cheats')).not.toBeInTheDocument();
expect(setIsModalOpenSpy).toHaveBeenCalledWith(false);
});

it('renders form validation error', async () => {
Expand Down
29 changes: 6 additions & 23 deletions gbajs3/src/components/modals/upload-cheats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ import {
EmbeddedProductTour,
type TourSteps
} from '../product-tour/embedded-product-tour.tsx';
import { CircleCheckButton } from '../shared/circle-check-button.tsx';
import { DragAndDropInput } from '../shared/drag-and-drop-input.tsx';
import { CenteredTextContainer } from '../shared/styled.tsx';

type InputProps = {
cheatFiles: File[];
Expand All @@ -24,13 +22,7 @@ const validFileExtensions = ['.cheats'];
export const UploadCheatsModal = () => {
const { setIsModalOpen } = useModalContext();
const { emulator } = useEmulatorContext();
const {
reset,
handleSubmit,
setValue,
formState: { isSubmitSuccessful },
control
} = useForm<InputProps>();
const { reset, handleSubmit, setValue, control } = useForm<InputProps>();
const { syncActionIfEnabled } = useAddCallbacks();
const cheatsFormId = useId();

Expand All @@ -52,8 +44,8 @@ export const UploadCheatsModal = () => {
)
);

reset();
syncActionIfEnabled();
await syncActionIfEnabled();
setIsModalOpen(false);
};

const tourSteps: TourSteps = [
Expand Down Expand Up @@ -101,7 +93,6 @@ export const UploadCheatsModal = () => {
validFileExtensions={validFileExtensions}
error={error?.message}
hideAcceptedFiles={!value?.length}
hideErrors={isSubmitSuccessful}
multiple
>
<p>
Expand All @@ -112,20 +103,12 @@ export const UploadCheatsModal = () => {
</DragAndDropInput>
)}
/>
{isSubmitSuccessful && (
<CenteredTextContainer>
<p>Upload complete!</p>
</CenteredTextContainer>
)}
</form>
</ModalBody>
<ModalFooter>
<CircleCheckButton
copy="Upload"
form={cheatsFormId}
showSuccess={isSubmitSuccessful}
type="submit"
/>
<Button form={cheatsFormId} type="submit" variant="contained">
Upload
</Button>
<Button variant="outlined" onClick={() => setIsModalOpen(false)}>
Close
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,6 @@ describe('<UploadPublicExternalRomsModal />', () => {
expect(onLoadOrDismissSpy).toHaveBeenCalledOnce();
expect(onLoadOrDismissSpy).toHaveBeenCalledWith('loaded');
expect(setIsModalOpenSpy).toHaveBeenCalledWith(false);

expect(await screen.findByText('Upload complete!')).toBeVisible();
});

it('renders external rom error', async () => {
Expand Down
19 changes: 4 additions & 15 deletions gbajs3/src/components/modals/upload-public-external-roms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
LoadingIndicator,
PacmanIndicator
} from '../shared/loading-indicator.tsx';
import { CenteredTextContainer } from '../shared/styled.tsx';

import type { PublicRomUploadStatus } from '../../hooks/use-show-load-public-roms.tsx';

Expand Down Expand Up @@ -60,7 +59,6 @@ export const UploadPublicExternalRomsModal = ({
const theme = useTheme();
const { setIsModalOpen } = useModalContext();
const { emulator } = useEmulatorContext();
const [hasCompletedUpload, setHasCompletedUpload] = useState(false);
const [currentRomURL, setCurrentRomURL] = useState<string | null>(null);
const uploadRomButtonId = useId();
const runGame = useRunGame();
Expand All @@ -87,7 +85,6 @@ export const UploadPublicExternalRomsModal = ({
};
emulator?.uploadRom(externalRomFile, runCallback);
setCurrentRomURL(null);
setHasCompletedUpload(true);
}
}, [
onLoadOrDismiss,
Expand All @@ -113,30 +110,22 @@ export const UploadPublicExternalRomsModal = ({
indicator={<PacmanIndicator />}
loadingCopy="Loading rom from url:"
>
{!hasCompletedUpload && (
<>
<p>A public rom URL has been shared with you.</p>
<p>You can load it using the upload button!</p>
<p>Make sure you trust the provider before uploading:</p>
</>
)}
<p>A public rom URL has been shared with you.</p>
<p>You can load it using the upload button!</p>
<p>Make sure you trust the provider before uploading:</p>
<URLDisplay url={url} />
{!!externalRomLoadError && (
<ErrorWithIcon
icon={<BiError style={{ color: theme.errorRed }} />}
text="Loading rom from URL has failed"
/>
)}
{hasCompletedUpload && (
<CenteredTextContainer>
<p>Upload complete!</p>
</CenteredTextContainer>
)}
</LoadingIndicator>
</ModalBody>
<ModalFooter>
<Button
id={uploadRomButtonId}
disabled={isExternalRomLoading}
onClick={() => {
setCurrentRomURL(url.href);
executeLoadExternalRom({ url: url });
Expand Down
41 changes: 24 additions & 17 deletions gbajs3/src/components/modals/upload-saves.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ import { productTourLocalStorageKey } from '../product-tour/consts.tsx';
import type { GBAEmulator } from '../../emulator/mgba/mgba-emulator.tsx';

describe('<UploadSavesModal />', () => {
it('uploads file', async () => {
it('uploads file and closes modal', async () => {
const uploadSaveOrSaveStateSpy: (file: File, cb?: () => void) => void =
vi.fn((_file, cb) => cb && cb());
const syncActionIfEnabledSpy = vi.fn();
const setIsModalOpenSpy = vi.fn();

const { useEmulatorContext: originalEmulator } = await vi.importActual<
typeof contextHooks
>('../../hooks/context.tsx');
const {
useEmulatorContext: originalEmulator,
useModalContext: originalModal
} = await vi.importActual<typeof contextHooks>('../../hooks/context.tsx');
const { useAddCallbacks: originalCallbacks } = await vi.importActual<
typeof addCallbackHooks
>('../../hooks/emulator/use-add-callbacks.tsx');
Expand All @@ -35,6 +37,11 @@ describe('<UploadSavesModal />', () => {
syncActionIfEnabled: syncActionIfEnabledSpy
}));

vi.spyOn(contextHooks, 'useModalContext').mockImplementation(() => ({
...originalModal(),
setIsModalOpen: setIsModalOpenSpy
}));

const testSaveFile = new File(['Some save file contents'], 'rom1.sav');

renderWithContext(<UploadSavesModal />);
Expand All @@ -56,21 +63,20 @@ describe('<UploadSavesModal />', () => {
expect.anything()
);
expect(syncActionIfEnabledSpy).toHaveBeenCalledOnce();

expect(screen.getByText('Upload complete!')).toBeVisible();
expect(screen.queryByText('File to upload:')).not.toBeInTheDocument();
expect(screen.queryByText('rom1.sav')).not.toBeInTheDocument();
expect(setIsModalOpenSpy).toHaveBeenCalledWith(false);
});

it('uploads multiple files', async () => {
it('uploads multiple files and closes modal', async () => {
const uploadSaveSpy: (file: File, cb?: () => void) => void = vi.fn(
(_file, cb) => cb && cb()
);
const syncActionIfEnabledSpy = vi.fn();
const setIsModalOpenSpy = vi.fn();

const { useEmulatorContext: originalEmulator } = await vi.importActual<
typeof contextHooks
>('../../hooks/context.tsx');
const {
useEmulatorContext: originalEmulator,
useModalContext: originalModal
} = await vi.importActual<typeof contextHooks>('../../hooks/context.tsx');
const { useAddCallbacks: originalCallbacks } = await vi.importActual<
typeof addCallbackHooks
>('../../hooks/emulator/use-add-callbacks.tsx');
Expand All @@ -87,6 +93,11 @@ describe('<UploadSavesModal />', () => {
syncActionIfEnabled: syncActionIfEnabledSpy
}));

vi.spyOn(contextHooks, 'useModalContext').mockImplementation(() => ({
...originalModal(),
setIsModalOpen: setIsModalOpenSpy
}));

const testSaveFiles = [
new File(['Some save file contents 1'], 'rom1.sav'),
new File(['Some save file contents 2'], 'rom2.sav')
Expand Down Expand Up @@ -116,11 +127,7 @@ describe('<UploadSavesModal />', () => {
expect.anything()
);
expect(syncActionIfEnabledSpy).toHaveBeenCalledOnce();

expect(screen.getByText('Upload complete!')).toBeVisible();
expect(screen.queryByText('Files to upload:')).not.toBeInTheDocument();
expect(screen.queryByText('rom1.sav')).not.toBeInTheDocument();
expect(screen.queryByText('rom2.sav')).not.toBeInTheDocument();
expect(setIsModalOpenSpy).toHaveBeenCalledWith(false);
});

it('renders form validation error', async () => {
Expand Down
29 changes: 6 additions & 23 deletions gbajs3/src/components/modals/upload-saves.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ import {
EmbeddedProductTour,
type TourSteps
} from '../product-tour/embedded-product-tour.tsx';
import { CircleCheckButton } from '../shared/circle-check-button.tsx';
import { DragAndDropInput } from '../shared/drag-and-drop-input.tsx';
import { CenteredTextContainer } from '../shared/styled.tsx';

type InputProps = {
saveFiles: File[];
Expand All @@ -28,13 +26,7 @@ export const UploadSavesModal = () => {
const { setIsModalOpen } = useModalContext();
const { emulator } = useEmulatorContext();
const { syncActionIfEnabled } = useAddCallbacks();
const {
reset,
handleSubmit,
setValue,
formState: { isSubmitSuccessful },
control
} = useForm<InputProps>();
const { reset, handleSubmit, setValue, control } = useForm<InputProps>();
const uploadSavesFormId = useId();

const onDrop = useCallback(
Expand All @@ -55,8 +47,8 @@ export const UploadSavesModal = () => {
)
);

reset();
syncActionIfEnabled();
await syncActionIfEnabled();
setIsModalOpen(false);
};

const tourSteps: TourSteps = [
Expand Down Expand Up @@ -105,7 +97,6 @@ export const UploadSavesModal = () => {
validFileExtensions={validFileExtensions}
error={error?.message}
hideAcceptedFiles={!value?.length}
hideErrors={isSubmitSuccessful}
multiple
>
<p>
Expand All @@ -115,20 +106,12 @@ export const UploadSavesModal = () => {
</DragAndDropInput>
)}
/>
{isSubmitSuccessful && (
<CenteredTextContainer>
<p>Upload complete!</p>
</CenteredTextContainer>
)}
</form>
</ModalBody>
<ModalFooter>
<CircleCheckButton
copy="Upload"
form={uploadSavesFormId}
showSuccess={isSubmitSuccessful}
type="submit"
/>
<Button form={uploadSavesFormId} type="submit" variant="contained">
Upload
</Button>
<Button variant="outlined" onClick={() => setIsModalOpen(false)}>
Close
</Button>
Expand Down
4 changes: 0 additions & 4 deletions gbajs3/src/components/shared/styled.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { BiPlus } from 'react-icons/bi';
import { styled } from 'styled-components';

export const CenteredTextContainer = styled.div`
text-align: center;
`;

export const CenteredText = styled.p`
text-align: center;
margin: 0;
Expand Down

0 comments on commit fee3c1f

Please sign in to comment.