Skip to content

Commit

Permalink
M2-5096: eliminate 3rd-party dep useWindowFocus
Browse files Browse the repository at this point in the history
Implement custom version instead.
  • Loading branch information
farmerpaul committed Feb 6, 2024
1 parent 932918e commit b92ef06
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 27 deletions.
18 changes: 0 additions & 18 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@
"react-transition-group": "^4.4.5",
"stream-browserify": "^3.0.0",
"typescript": "^4.9.5",
"use-window-focus": "^1.4.2",
"uuid": "^9.0.1",
"web-vitals": "^3.5.0",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.0/xlsx-0.20.0.tgz",
Expand Down
14 changes: 8 additions & 6 deletions src/shared/components/Banners/Banner/Banner.test.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { render, screen, fireEvent } from '@testing-library/react';
import useWindowFocus from 'use-window-focus';

import { Banner } from './Banner';

const mockOnClose = jest.fn();
const mockUseWindowFocus = jest.fn();

const props = {
children: 'Test banner',
onClose: mockOnClose,
duration: 5000,
};

jest.mock('use-window-focus');
jest.mock('shared/hooks/useWindowFocus', () => ({
useWindowFocus: () => mockUseWindowFocus(),
}));

describe('Banner', () => {
afterEach(() => {
Expand All @@ -37,21 +39,21 @@ describe('Banner', () => {

test('banner auto-closes after 5 seconds if window in focus', () => {
jest.useFakeTimers();
(useWindowFocus as jest.Mock).mockReturnValue(true);
mockUseWindowFocus.mockReturnValue(true);

render(<Banner {...props} />);

jest.advanceTimersByTime(props.duration);
jest.advanceTimersByTime(props.duration + 1000);
expect(mockOnClose).toHaveBeenCalled();
});

test('banner does not auto-close if window unfocused', () => {
jest.useFakeTimers();
(useWindowFocus as jest.Mock).mockReturnValue(false);
mockUseWindowFocus.mockReturnValue(false);

render(<Banner {...props} />);

jest.advanceTimersByTime(props.duration);
jest.advanceTimersByTime(props.duration + 1000);
expect(mockOnClose).not.toHaveBeenCalled();
});
});
2 changes: 1 addition & 1 deletion src/shared/components/Banners/Banner/Banner.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Alert } from '@mui/material';
import { useEffect, useState } from 'react';
import useWindowFocus from 'use-window-focus';

import { Svg } from 'shared/components/Svg';
import { StyledClearedButton } from 'shared/styles';
import { useWindowFocus } from 'shared/hooks/useWindowFocus';

import { BannerProps } from './Banner.types';
import { BANNER_ICONS } from './Banner.const';
Expand Down
2 changes: 1 addition & 1 deletion src/shared/components/Banners/Banners.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe('Banners', () => {
const button = screen.getByRole('button');
fireEvent.click(button);

// Wait for Collpse transition to complete
// Wait for Collapse transition to complete
await waitFor(() => {
expect(screen.queryByText('You are using the new version of MindLogger!')).toBeNull();
});
Expand Down
1 change: 1 addition & 0 deletions src/shared/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ export * from './useIntersectionObserver';
export * from './useNetwork';
export * from './useCheckIfAppletHasNotFoundError';
export * from './useRespondentLabel';
export * from './useWindowFocus';
45 changes: 45 additions & 0 deletions src/shared/hooks/useWindowFocus.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { act, renderHook } from '@testing-library/react';

import { useWindowFocus } from './useWindowFocus';

const fireFocusEvent = () => {
global.window.dispatchEvent(new Event('focus'));
};

const fireBlurEvent = () => {
global.window.dispatchEvent(new Event('blur'));
};

describe('useWindowFocus)', () => {
test('should default to false', () => {
const { result } = renderHook(() => useWindowFocus());

expect(result.current).toBe(false);
});

test('should be true after focus event', () => {
const { result } = renderHook(() => useWindowFocus());

act(() => {
fireFocusEvent();
});

expect(result.current).toBe(true);
});

test('should be false after blur event', () => {
const { result } = renderHook(() => useWindowFocus());

act(() => {
fireFocusEvent();
});

expect(result.current).toBe(true);

act(() => {
fireBlurEvent();
});

expect(result.current).toBe(false);
});
});
27 changes: 27 additions & 0 deletions src/shared/hooks/useWindowFocus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useState, useEffect } from 'react';

const hasFocus = () => typeof document !== 'undefined' && document.hasFocus();

/**
* Monitors and returns boolean window focus state reactively.
*/
export const useWindowFocus = () => {
const [focused, setFocused] = useState(hasFocus);

useEffect(() => {
setFocused(hasFocus());

const onFocus = () => setFocused(true);
const onBlur = () => setFocused(false);

window.addEventListener('focus', onFocus);
window.addEventListener('blur', onBlur);

return () => {
window.removeEventListener('focus', onFocus);
window.removeEventListener('blur', onBlur);
};
}, []);

return focused;
};

0 comments on commit b92ef06

Please sign in to comment.