From a0c2cfddec4b5cffb5b6c43502c1f25097cb7cf6 Mon Sep 17 00:00:00 2001 From: Martin Malfertheiner Date: Thu, 4 Nov 2021 16:52:52 +0100 Subject: [PATCH 1/2] create match media query hook --- package.json | 2 + .../__test__/useMatchMediaQuery.test.tsx | 46 +++++++++++++++++++ src/useMatchMediaQuery/useMatchMediaQuery.ts | 24 ++++++++++ 3 files changed, 72 insertions(+) create mode 100644 src/useMatchMediaQuery/__test__/useMatchMediaQuery.test.tsx create mode 100644 src/useMatchMediaQuery/useMatchMediaQuery.ts diff --git a/package.json b/package.json index 0ed56a6..fb113ba 100644 --- a/package.json +++ b/package.json @@ -45,10 +45,12 @@ "@testing-library/jest-dom": "^5.11.10", "@testing-library/react": "^11.2.6", "@testing-library/react-hooks": "^5.1.1", + "@types/css-mediaquery": "^0.1.1", "@types/jest": "^26.0.22", "@types/react": "^17.0.3", "@typescript-eslint/eslint-plugin": "^4.21.0", "@typescript-eslint/parser": "^4.21.0", + "css-mediaquery": "^0.1.2", "eslint": "^7.23.0", "eslint-config-prettier": "^8.1.0", "eslint-plugin-import": "^2.22.1", diff --git a/src/useMatchMediaQuery/__test__/useMatchMediaQuery.test.tsx b/src/useMatchMediaQuery/__test__/useMatchMediaQuery.test.tsx new file mode 100644 index 0000000..2d128fe --- /dev/null +++ b/src/useMatchMediaQuery/__test__/useMatchMediaQuery.test.tsx @@ -0,0 +1,46 @@ +import React from 'react' +import isMatching from 'css-mediaquery' +import { render, RenderResult } from '@testing-library/react' +import useMatchMediaQuery from '../useMatchMediaQuery' + +beforeEach(() => { + // mock window.matchMedia (use 'css-mediaquery') + window.matchMedia = jest.fn().mockImplementation((query) => { + return { + addListener: jest.fn(), + media: query, + matches: isMatching.match(query, { + width: 500, + }), + } + }) +}) + +const TestComponent = ({ query }: { query: string }) => { + const isMatched = useMatchMediaQuery(query) + if (isMatched) return
visible
+ return
hidden
+} + +function renderTestComponent(query: string): RenderResult { + return render() +} + +describe('useReactSimpleMatchMedia', () => { + it('should be visible when min width matches', () => { + const { getByText } = renderTestComponent('(min-width : 500px)') + expect(getByText(/visible/i)).toBeInTheDocument() + }) + + it('should not be visible min does not match', () => { + const { getByText } = renderTestComponent('(min-width : 600px)') + expect(getByText(/hidden/i)).toBeInTheDocument() + }) + + it('should be visible if between matches', () => { + const { getByText } = renderTestComponent( + '(min-width : 500px) and (max-width: 600px)' + ) + expect(getByText(/visible/i)).toBeInTheDocument() + }) +}) diff --git a/src/useMatchMediaQuery/useMatchMediaQuery.ts b/src/useMatchMediaQuery/useMatchMediaQuery.ts new file mode 100644 index 0000000..9779693 --- /dev/null +++ b/src/useMatchMediaQuery/useMatchMediaQuery.ts @@ -0,0 +1,24 @@ +import { useState, useEffect } from 'react' + +export default function (query: string): boolean | undefined { + if (typeof window !== 'object' || !window.matchMedia) return + + const [matches, setMatches] = useState(window.matchMedia(query).matches) + + useEffect(() => { + const media = window.matchMedia(query) + + if (media.matches !== matches) setMatches(media.matches) + + const listener = () => setMatches(media.matches) + + if (media.addEventListener) { + media.addEventListener('change', listener) + } + + return () => + media.removeEventListener && media.removeEventListener('change', listener) + }, [matches, query]) + + return matches +} From 37c9cdb3a7fabf9531551f0ca9b35e639587675b Mon Sep 17 00:00:00 2001 From: Martin Malfertheiner Date: Thu, 4 Nov 2021 16:58:42 +0100 Subject: [PATCH 2/2] document useMatchMediaQuery hook --- readme.md | 15 +++++++++++++++ src/index.ts | 2 ++ .../__test__/useMatchMediaQuery.test.tsx | 2 +- src/useMatchMediaQuery/useMatchMediaQuery.ts | 4 +++- 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index a65f43e..b5efe7c 100644 --- a/readme.md +++ b/readme.md @@ -12,6 +12,7 @@ This package includes different tools that support you with common tasks. - [useInterval](#useinterval) - [Async Data](#async-data) - [LocationProvider](#locationprovider) + - [useMatchMediaQuery](#usematchmediaquery) - [Build & Publish](#build--publish) - [Information](#information) @@ -149,6 +150,20 @@ const MyComponent = () => { } ``` +### useMatchMediaQuery + +This hook is based on the `window.matchQuery` API and can be used to find out if a certain media query matches the current window. + +```tsx +import { useMatchMediaQuery } from '@aboutbits/react-toolbox' + +const TestComponent = () => { + const matches = useMatchMediaQuery('(min-width : 500px)') + if (matches) return
visible
+ return null +} +``` + ## Build & Publish To publish the package commit all changes and push them to main. Then run one of the following commands locally: diff --git a/src/index.ts b/src/index.ts index cfec293..dd89387 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ import { LocationContext, LocationContextValue, } from './location-provider/LocationProvider' +import { useMatchMediaQuery } from './useMatchMediaQuery/useMatchMediaQuery' export { useInterval, @@ -15,4 +16,5 @@ export { LocationProvider, LocationContext, LocationContextValue, + useMatchMediaQuery, } diff --git a/src/useMatchMediaQuery/__test__/useMatchMediaQuery.test.tsx b/src/useMatchMediaQuery/__test__/useMatchMediaQuery.test.tsx index 2d128fe..d377555 100644 --- a/src/useMatchMediaQuery/__test__/useMatchMediaQuery.test.tsx +++ b/src/useMatchMediaQuery/__test__/useMatchMediaQuery.test.tsx @@ -1,7 +1,7 @@ import React from 'react' import isMatching from 'css-mediaquery' import { render, RenderResult } from '@testing-library/react' -import useMatchMediaQuery from '../useMatchMediaQuery' +import { useMatchMediaQuery } from '../useMatchMediaQuery' beforeEach(() => { // mock window.matchMedia (use 'css-mediaquery') diff --git a/src/useMatchMediaQuery/useMatchMediaQuery.ts b/src/useMatchMediaQuery/useMatchMediaQuery.ts index 9779693..eb2c205 100644 --- a/src/useMatchMediaQuery/useMatchMediaQuery.ts +++ b/src/useMatchMediaQuery/useMatchMediaQuery.ts @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react' -export default function (query: string): boolean | undefined { +function useMatchMediaQuery(query: string): boolean | undefined { if (typeof window !== 'object' || !window.matchMedia) return const [matches, setMatches] = useState(window.matchMedia(query).matches) @@ -22,3 +22,5 @@ export default function (query: string): boolean | undefined { return matches } + +export { useMatchMediaQuery }