From 7513b278cea0c28ef6b38357cafb4c0777e9d29e Mon Sep 17 00:00:00 2001 From: Artem Zakharchenko Date: Mon, 6 Apr 2020 18:43:47 +0200 Subject: [PATCH] Adds "useCurrentBreakpoints" hook --- examples/hooks/UseCurrentBreakpoints.jsx | 15 +++++++++ examples/hooks/useCurrentBreakpoints.test.js | 29 ++++++++++++++++ examples/index.js | 2 ++ packages/atomic-layout-emotion/src/index.ts | 1 + .../src/hooks/useCurrentBreakpoints.ts | 33 +++++++++++++++++++ packages/atomic-layout/src/index.ts | 1 + 6 files changed, 81 insertions(+) create mode 100644 examples/hooks/UseCurrentBreakpoints.jsx create mode 100644 examples/hooks/useCurrentBreakpoints.test.js create mode 100644 packages/atomic-layout/src/hooks/useCurrentBreakpoints.ts diff --git a/examples/hooks/UseCurrentBreakpoints.jsx b/examples/hooks/UseCurrentBreakpoints.jsx new file mode 100644 index 00000000..d0bac88a --- /dev/null +++ b/examples/hooks/UseCurrentBreakpoints.jsx @@ -0,0 +1,15 @@ +import React from 'react' +import { useCurrentBreakpoints } from 'atomic-layout' + +const UseCurrentBreakpointsScenario = () => { + const breakpoints = useCurrentBreakpoints() + + return ( +

+ Current breakpoint:{' '} + {breakpoints.join()} +

+ ) +} + +export default UseCurrentBreakpointsScenario diff --git a/examples/hooks/useCurrentBreakpoints.test.js b/examples/hooks/useCurrentBreakpoints.test.js new file mode 100644 index 00000000..a533ceaf --- /dev/null +++ b/examples/hooks/useCurrentBreakpoints.test.js @@ -0,0 +1,29 @@ +describe('useCurrentBreakpoints', () => { + before(() => { + cy.loadStory(['hooks'], ['usecurrentbreakpoints']) + }) + + it('Returns the default breakpoint on initial render', () => { + cy.get('[data-test-id="current-breakpoints"]').should('have.text', 'xs') + }) + + it('Returns "sm" breakpoint name on "sm" breakpoint', () => { + cy.setBreakpoint('sm') + cy.get('[data-test-id="current-breakpoints"]').should('have.text', 'sm') + }) + + it('Returns "md" breakpoint name on "md" breakpoint', () => { + cy.setBreakpoint('md') + cy.get('[data-test-id="current-breakpoints"]').should('have.text', 'md') + }) + + it('Returns "lg" breakpoint name on "lg" breakpoint', () => { + cy.setBreakpoint('lg') + cy.get('[data-test-id="current-breakpoints"]').should('have.text', 'lg') + }) + + it('Returns "xl" breakpoint name on "xl" breakpoint', () => { + cy.setBreakpoint('xl') + cy.get('[data-test-id="current-breakpoints"]').should('have.text', 'xl') + }) +}) diff --git a/examples/index.js b/examples/index.js index 58f0d268..004d3dee 100644 --- a/examples/index.js +++ b/examples/index.js @@ -93,12 +93,14 @@ storiesOf('Components|Visible', module).add( import UseViewportChange from './hooks/UseViewportChange' import UseResponsiveValue from './hooks/UseResponsiveValue' import UseBreakpointChange from './hooks/UseBreakpointChange' +import UseCurrentBreakpoints from './hooks/UseCurrentBreakpoints' import UseResponsiveProps from './hooks/UseResponsiveProps' storiesOf('Hooks', module) .add('useViewportChange', () => ) .add('useResponsiveValue', () => ) .add('useBreakpointChange', () => ) + .add('useCurrentBreakpoints', () => ) .add('useResponsiveProps', () => ) /** diff --git a/packages/atomic-layout-emotion/src/index.ts b/packages/atomic-layout-emotion/src/index.ts index e1376bb6..05ca3684 100644 --- a/packages/atomic-layout-emotion/src/index.ts +++ b/packages/atomic-layout-emotion/src/index.ts @@ -11,6 +11,7 @@ export { useMediaQuery, useViewportChange, useBreakpointChange, + useCurrentBreakpoints, useResponsiveValue, useResponsiveProps, useResponsiveQuery, diff --git a/packages/atomic-layout/src/hooks/useCurrentBreakpoints.ts b/packages/atomic-layout/src/hooks/useCurrentBreakpoints.ts new file mode 100644 index 00000000..8a9dc8b5 --- /dev/null +++ b/packages/atomic-layout/src/hooks/useCurrentBreakpoints.ts @@ -0,0 +1,33 @@ +import { useState } from 'react' +import useViewportChange from './useViewportChange' +import { Layout, createMediaQuery } from '@atomic-layout/core' + +/** + * Returns a list of breakpoints that match the current state of the viewport. + */ +export default function useCurrentBreakpoints(): string[] { + const [currentBreakpoints, setCurrentBreakpoints] = useState([ + Layout.defaultBreakpointName, + ]) + + useViewportChange(() => { + const matchingBreakpoints = Object.keys(Layout.breakpoints).filter( + (breakpointName) => { + const mediaQueryObject = Layout.breakpoints[breakpointName] + /** + * @fixme Move the media query composition and matching logic + * into the `Layout` class. + * @reason It's expensive and redundant to compose strings from + * breakpoints on each viewport change. Breakpoints never change + * on runtime. + */ + const mediaQuery = createMediaQuery(mediaQueryObject, 'only') + return matchMedia(mediaQuery).matches + }, + ) + + setCurrentBreakpoints(matchingBreakpoints) + }) + + return currentBreakpoints +} diff --git a/packages/atomic-layout/src/index.ts b/packages/atomic-layout/src/index.ts index 2f45bba4..45202b8b 100644 --- a/packages/atomic-layout/src/index.ts +++ b/packages/atomic-layout/src/index.ts @@ -11,6 +11,7 @@ export { default as Visible } from './components/Visible' export { useMediaQuery } from './hooks/useMediaQuery' export { default as useViewportChange } from './hooks/useViewportChange' export { default as useBreakpointChange } from './hooks/useBreakpointChange' +export { default as useCurrentBreakpoints } from './hooks/useCurrentBreakpoints' export { default as useResponsiveValue } from './hooks/useResponsiveValue' export { default as useResponsiveProps } from './hooks/useResponsiveProps' export { default as useResponsiveQuery } from './hooks/useResponsiveQuery'