mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-04-27 07:48:01 -07:00
Webclient: Handle firing an event once (#4499)
* draft: handle firing an event once * lint * Prevent rapid double-click on sending messages * no rest spread on single primative when sibling components exist * clear message instead of using a fireOnce handler. * fix tests * remove unnecessary validate mock
This commit is contained in:
parent
4bb13677c8
commit
513fcb0908
16 changed files with 21467 additions and 161 deletions
1
webclient/src/hooks/useFireOnce/index.ts
Normal file
1
webclient/src/hooks/useFireOnce/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from './useFireOnce'
|
||||
103
webclient/src/hooks/useFireOnce/useFireOnce.spec.tsx
Normal file
103
webclient/src/hooks/useFireOnce/useFireOnce.spec.tsx
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import {
|
||||
render,
|
||||
fireEvent,
|
||||
getByRole,
|
||||
waitFor,
|
||||
act
|
||||
} from '@testing-library/react';
|
||||
import { useFireOnce } from './useFireOnce';
|
||||
|
||||
describe('useFireOnce hook', () => {
|
||||
test('it only fires once when button is clicked twice', async () => {
|
||||
// Mock a promise with a delay
|
||||
const onClickWithPromise = jest.fn((e) => {
|
||||
e.preventDefault()
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(true);
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
function Button(props) {
|
||||
const { children, onClick } = props
|
||||
const [buttonIsDisabled, setButtonIsDisabled, handleClickOnce] = useFireOnce(onClick)
|
||||
return <button onClick={handleClickOnce} disabled={buttonIsDisabled}>{children}</button>
|
||||
}
|
||||
|
||||
// render the button
|
||||
const { getByRole } = render(
|
||||
<Button onClick={onClickWithPromise}>Click Me!</Button>
|
||||
);
|
||||
|
||||
//Grab the button from the DOM and confirm it initialized in an enabled state
|
||||
const button = getByRole('button', { name: 'Click Me!' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
// Simulate two click events in a row
|
||||
fireEvent.click(button);
|
||||
fireEvent.click(button);
|
||||
|
||||
// Confirm that it's disabled
|
||||
await waitFor(() => {
|
||||
expect(button).toBeDisabled();
|
||||
});
|
||||
|
||||
// Confirm it became enabled after the timeout and that the click event was only fired once
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(onClickWithPromise).toHaveBeenCalledTimes(1);
|
||||
},
|
||||
{ timeout: 100 }
|
||||
);
|
||||
});
|
||||
|
||||
test('it only fires once when form is submitted twice', async () => {
|
||||
// Mock a promise with a delay
|
||||
const onClickWithPromise = jest.fn((e) => {
|
||||
e.preventDefault()
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(true);
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
function Form(props) {
|
||||
const { onSubmit } = props
|
||||
const [buttonIsDisabled, setButtonIsDisabled, handleSubmitOnce] = useFireOnce(onSubmit)
|
||||
return (
|
||||
<form onSubmit={handleSubmitOnce}>
|
||||
<input type="text" defaultValue="Hell World" name="thing-to-say" />
|
||||
<button disabled={buttonIsDisabled}>Click Me!</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
// render the form
|
||||
const { getByRole } = render(
|
||||
<Form onSubmit={onClickWithPromise} />
|
||||
);
|
||||
|
||||
//Grab the button from the DOM and confirm it initialized in an enabled state
|
||||
const button = getByRole('button', { name: 'Click Me!' });
|
||||
expect(button).toBeEnabled();
|
||||
|
||||
// Simulate two click events in a row
|
||||
fireEvent.click(button);
|
||||
fireEvent.click(button);
|
||||
|
||||
// Confirm that it's disabled
|
||||
await waitFor(() => {
|
||||
expect(button).toBeDisabled();
|
||||
});
|
||||
|
||||
// Confirm it became enabled after the timeout and that the click event was only fired once
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(onClickWithPromise).toHaveBeenCalledTimes(1);
|
||||
},
|
||||
{ timeout: 100 }
|
||||
);
|
||||
});
|
||||
});
|
||||
17
webclient/src/hooks/useFireOnce/useFireOnce.ts
Normal file
17
webclient/src/hooks/useFireOnce/useFireOnce.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { useCallback, useState } from 'react';
|
||||
import { useReduxEffect } from 'hooks';
|
||||
import { ServerTypes } from 'store';
|
||||
|
||||
type UseFireOnceType = (...args: any) => any;
|
||||
|
||||
export function useFireOnce<T extends UseFireOnceType>(fn: T): [boolean, any, any] {
|
||||
const [actionIsInFlight, setActionIsInFlight] = useState(false)
|
||||
const handleFireOnce = useCallback((args) => {
|
||||
setActionIsInFlight(true);
|
||||
fn(args);
|
||||
}, [])
|
||||
function resetInFlightStatus() {
|
||||
setActionIsInFlight(false);
|
||||
}
|
||||
return [actionIsInFlight, resetInFlightStatus, handleFireOnce]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue