From 85c57d2d406d569647a0e830527b50f6c73faae2 Mon Sep 17 00:00:00 2001 From: Artem Zakharchenko Date: Mon, 13 Jan 2020 09:14:26 +0100 Subject: [PATCH] normalizeMediaQuery: Parses query into a workable object --- .../createMediaQuery/createMediaQuery.ts | 49 ++++++++++++------- .../normalizeQuery/normalizeQuery.spec.ts | 21 +++++++- .../styles/normalizeQuery/normalizeQuery.ts | 29 ++++++----- .../atomic-layout/src/hooks/useMediaQuery.ts | 35 +++++++------ 4 files changed, 85 insertions(+), 49 deletions(-) diff --git a/packages/atomic-layout-core/src/utils/styles/createMediaQuery/createMediaQuery.ts b/packages/atomic-layout-core/src/utils/styles/createMediaQuery/createMediaQuery.ts index 68740f02..1da9b690 100644 --- a/packages/atomic-layout-core/src/utils/styles/createMediaQuery/createMediaQuery.ts +++ b/packages/atomic-layout-core/src/utils/styles/createMediaQuery/createMediaQuery.ts @@ -4,21 +4,21 @@ import { BreakpointBehavior, } from '../../../const/defaultOptions' import transformNumeric from '../../math/transformNumeric' -import normalizeQuery from '../../styles/normalizeQuery' +import normalizeQuery, { + NormalizedQueryParam, +} from '../../styles/normalizeQuery' import compose from '../../functions/compose' -type MediaQueryPair = [string, Numeric] - /** * Determines whether a given media query param should be added * to the media query string based on a breakpoint's behavior. */ const shouldAppendProperty = ( - queryParam: string, + queryParam: NormalizedQueryParam, behavior: BreakpointBehavior, ): boolean => { - const [prefix, splitPropName] = queryParam.split('-') - const isDimensionalProp = ['height', 'width'].includes(splitPropName) + const { prefix, name } = queryParam + const isDimensionalProp = ['height', 'width'].includes(name) if (!isDimensionalProp) { return true @@ -31,31 +31,46 @@ const shouldAppendProperty = ( } const filterRelevantQueryParams = (behavior: BreakpointBehavior) => ( - queryList: MediaQueryPair[], -): MediaQueryPair[] => { - return queryList.filter(([queryParam]) => - shouldAppendProperty(queryParam, behavior), + queryList: NormalizedQueryParam[], +): NormalizedQueryParam[] => { + return queryList.filter((normalizedQueryParam) => + shouldAppendProperty(normalizedQueryParam, behavior), ) } /** * Joins a given media query params list with the given transformer function. */ -export const joinQueryList = (transformer: (pair: MediaQueryPair) => any) => ( - queryList: MediaQueryPair[], +export const joinQueryList = ( + queryList: NormalizedQueryParam[], + transformer: (pair: NormalizedQueryParam) => any, ) => { return queryList.map(transformer).join(' and ') } -export default function createMediaQuery( +export const createQueryList = ( breakpoint: Breakpoint, behavior: BreakpointBehavior, -): string { +): NormalizedQueryParam[] => { return compose( - joinQueryList(([dashedQueryProp, propValue]) => { - return `(${dashedQueryProp}:${String(transformNumeric(propValue))})` - }), filterRelevantQueryParams(behavior), normalizeQuery, )(breakpoint) } + +export default function createMediaQuery( + breakpoint: Breakpoint, + behavior: BreakpointBehavior, +): string { + const queryList = createQueryList(breakpoint, behavior) + + const mediaQueryString = joinQueryList( + queryList, + ({ prefix, name, value }) => { + const dashedQueryParamName = [prefix, name].filter(Boolean).join('-') + return `(${dashedQueryParamName}:${String(transformNumeric(value))})` + }, + ) + + return mediaQueryString +} diff --git a/packages/atomic-layout-core/src/utils/styles/normalizeQuery/normalizeQuery.spec.ts b/packages/atomic-layout-core/src/utils/styles/normalizeQuery/normalizeQuery.spec.ts index 171baf3b..25eb0da6 100644 --- a/packages/atomic-layout-core/src/utils/styles/normalizeQuery/normalizeQuery.spec.ts +++ b/packages/atomic-layout-core/src/utils/styles/normalizeQuery/normalizeQuery.spec.ts @@ -2,13 +2,30 @@ import normalizeQuery from './normalizeQuery' describe('normalizeQuery', () => { describe('given a media query Object', () => { - it('returns its [key, value] pairs', () => { + it('returns its { prefix, name, value } data', () => { expect( normalizeQuery({ + height: 100, minWidth: 120, maxAspectRatio: '3/4', }), - ).toEqual([['min-width', 120], ['max-aspect-ratio', '3/4']]) + ).toEqual([ + { + prefix: undefined, + name: 'height', + value: 100, + }, + { + prefix: 'min', + name: 'width', + value: 120, + }, + { + prefix: 'max', + name: 'aspectRatio', + value: '3/4', + }, + ]) }) }) }) diff --git a/packages/atomic-layout-core/src/utils/styles/normalizeQuery/normalizeQuery.ts b/packages/atomic-layout-core/src/utils/styles/normalizeQuery/normalizeQuery.ts index 2e1816c9..37a28cc3 100644 --- a/packages/atomic-layout-core/src/utils/styles/normalizeQuery/normalizeQuery.ts +++ b/packages/atomic-layout-core/src/utils/styles/normalizeQuery/normalizeQuery.ts @@ -1,20 +1,25 @@ import { Numeric, Breakpoint } from '../../../const/defaultOptions' import isset from '../../functions/isset' -import toDashedString from '../../strings/toDashedString' +import toLowerCaseFirst from '../../strings/toLowerCaseFirst' + +export interface NormalizedQueryParam { + prefix: string + name: string + value: Numeric +} /** * Normalizes given media query object to a list of [propName, propValue]. - * @example - * normalizeQuery({ minWidth: 120 }) - * // [['min-width', 120]] */ export default function normalizeQuery( - queryProps: Breakpoint, -): Array<[string, Numeric]> { - return Object.entries(queryProps) - .filter(([_, propValue]) => isset(propValue)) - .map<[string, Numeric]>(([propName, propValue]) => [ - toDashedString(propName), - propValue, - ]) + breakpoint: Breakpoint, +): NormalizedQueryParam[] { + return Object.entries(breakpoint) + .filter(([_, value]) => isset(value)) + .map(([propName, value]) => { + const [_, prefix, restName] = propName.match(/(min|max)?(.+)/) + const normalizedName = toLowerCaseFirst(restName) + + return { prefix, name: normalizedName, value } + }) } diff --git a/packages/atomic-layout/src/hooks/useMediaQuery.ts b/packages/atomic-layout/src/hooks/useMediaQuery.ts index 3821647a..66e80c9e 100644 --- a/packages/atomic-layout/src/hooks/useMediaQuery.ts +++ b/packages/atomic-layout/src/hooks/useMediaQuery.ts @@ -1,7 +1,6 @@ import { useState, useMemo, useLayoutEffect } from 'react' import { MediaQuery as MediaQueryParams, - compose, joinQueryList, normalizeQuery, transformNumeric, @@ -11,24 +10,24 @@ import { * Creates a media querty string based on the given params. */ const createMediaQuery = (queryParams: MediaQueryParams): string => { - return compose( - joinQueryList(([paramName, paramValue]) => { - /** - * Transform values that begin with a number to prevent - * transformations of "calc" expressions. - * Transformation of numerics is necessary when a simple - * number is used as a value (min-width: 750) is not valid. - * - * (min-width: 750) ==> (min-width: 750px) - */ - const resolvedParamValue = /^\d/.test(String(paramValue)) - ? transformNumeric(paramValue) - : paramValue + const queryList = normalizeQuery(queryParams) + const mediaQueryString = joinQueryList(queryList, ({ name, value }) => { + /** + * Transform values that begin with a number to prevent + * transformations of "calc" expressions. + * Transformation of numerics is necessary when a simple + * number is used as a value (min-width: 750) is not valid. + * + * (min-width: 750) ==> (min-width: 750px) + */ + const resolvedParamValue = /^\d/.test(String(value)) + ? transformNumeric(value) + : value - return `(${paramName}:${resolvedParamValue})` - }), - normalizeQuery, - )(queryParams) + return `(${name}:${resolvedParamValue})` + }) + + return mediaQueryString } type UseMediaQuery = (