-
Notifications
You must be signed in to change notification settings - Fork 94
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement new hook -
useScreenOrientation
Checks if screen is in `portrait` or `landscape` orientation and automatically re-renders on orientation change. #
- Loading branch information
Showing
9 changed files
with
165 additions
and
0 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
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
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,17 @@ | ||
import * as React from 'react'; | ||
import { useScreenOrientation } from '../..'; | ||
|
||
export const Example: React.FC = () => { | ||
const orientation = useScreenOrientation(); | ||
|
||
return ( | ||
<div> | ||
<div> | ||
Orientation: <code>{orientation}</code> | ||
</div> | ||
<div> | ||
Render time: <code>{new Date().toLocaleString()}</code> | ||
</div> | ||
</div> | ||
); | ||
}; |
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,41 @@ | ||
import { Canvas, Meta, Story } from '@storybook/addon-docs/blocks'; | ||
import { Example } from './example.stories'; | ||
import { ImportPath } from '../../storybookUtil/ImportPath'; | ||
|
||
<Meta title="Sensor/useScreenOrientation" component={Example} /> | ||
|
||
# useScreenOrientation | ||
|
||
Checks if screen is in `portrait` or `landscape` orientation and automatically re-renders on | ||
orientation change. | ||
|
||
As [Screen Orientation API](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Orientation_API#browser_compatibility) | ||
is still experimental and not supported by Safari, this hook uses CSS3 `orientation` media-query to | ||
check screen orientation. | ||
|
||
- Automatically re-renders on screen orientation change. | ||
- Works in sync with CSS, as it uses media-query. | ||
- All hooks uses single media query - therefore it is extremely performant. | ||
- SSR-friendly. | ||
|
||
#### Example | ||
|
||
<Canvas> | ||
<Story story={Example} inline /> | ||
</Canvas> | ||
|
||
## Reference | ||
|
||
```ts | ||
type ScreenOrientation = 'portrait' | 'landscape'; | ||
|
||
function useScreenOrientation(): ScreenOrientation; | ||
``` | ||
|
||
#### Importing | ||
|
||
<ImportPath /> | ||
|
||
#### Return | ||
|
||
A string representing screen orientation |
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,71 @@ | ||
import { act, renderHook } from '@testing-library/react-hooks/dom'; | ||
import { useScreenOrientation } from '../..'; | ||
|
||
describe('useScreenOrientation', () => { | ||
// have to copy implementation as jsdom lacks of it | ||
type IMutableMediaQueryList = { | ||
matches: boolean; | ||
media: string; | ||
onchange: null; | ||
addListener: jest.Mock; // Deprecated | ||
removeListener: jest.Mock; // Deprecated | ||
addEventListener: jest.Mock; | ||
removeEventListener: jest.Mock; | ||
dispatchEvent: jest.Mock; | ||
}; | ||
|
||
const matchMediaMock = jest.fn(); | ||
let initialMatchMedia: typeof window.matchMedia; | ||
|
||
beforeAll(() => { | ||
initialMatchMedia = window.matchMedia; | ||
Object.defineProperty(window, 'matchMedia', { | ||
writable: true, | ||
value: matchMediaMock, | ||
}); | ||
}); | ||
|
||
afterAll(() => { | ||
window.matchMedia = initialMatchMedia; | ||
}); | ||
|
||
beforeEach(() => { | ||
matchMediaMock.mockImplementation((query) => ({ | ||
matches: false, | ||
media: query, | ||
onchange: null, | ||
addListener: jest.fn(), // Deprecated | ||
removeListener: jest.fn(), // Deprecated | ||
addEventListener: jest.fn(), | ||
removeEventListener: jest.fn(), | ||
dispatchEvent: jest.fn(), | ||
})); | ||
}); | ||
|
||
afterEach(() => { | ||
matchMediaMock.mockClear(); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(useScreenOrientation).toBeDefined(); | ||
}); | ||
|
||
it('should render', () => { | ||
const { result } = renderHook(() => useScreenOrientation()); | ||
expect(result.error).toBeUndefined(); | ||
}); | ||
|
||
it('should return `portrait` in case media query matches and `landscape` otherwise', () => { | ||
const { result } = renderHook(() => useScreenOrientation()); | ||
expect(result.current).toBe('landscape'); | ||
|
||
const mql = matchMediaMock.mock.results[0].value as IMutableMediaQueryList; | ||
mql.matches = true; | ||
|
||
act(() => { | ||
mql.addEventListener.mock.calls[0][1](); | ||
}); | ||
|
||
expect(result.current).toBe('portrait'); | ||
}); | ||
}); |
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,13 @@ | ||
import { renderHook } from '@testing-library/react-hooks/server'; | ||
import { useScreenOrientation } from '../..'; | ||
|
||
describe('useScreenOrientation', () => { | ||
it('should be defined', () => { | ||
expect(useScreenOrientation).toBeDefined(); | ||
}); | ||
|
||
it('should render', () => { | ||
const { result } = renderHook(() => useScreenOrientation()); | ||
expect(result.error).toBeUndefined(); | ||
}); | ||
}); |
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,13 @@ | ||
import { useMediaQuery } from '..'; | ||
|
||
export type ScreenOrientation = 'portrait' | 'landscape'; | ||
|
||
/** | ||
* Checks if screen is in `portrait` or `landscape` orientation. | ||
* | ||
* As `Screen Orientation API` is still experimental and not supported by Safari, this | ||
* hook uses CSS3 `orientation` media-query to check screen orientation. | ||
*/ | ||
export function useScreenOrientation(): ScreenOrientation { | ||
return useMediaQuery('(orientation: portrait)') ? 'portrait' : 'landscape'; | ||
} |
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