Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/atomic-layout-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export {
joinQueryList,
} from './utils/styles/createMediaQuery'
export { default as normalizeQuery } from './utils/styles/normalizeQuery'
export { staticMatchMedia } from './utils/styles/staticMatchMedia'

/* Breakpoints */
export { default as withBreakpoints } from './utils/breakpoints/withBreakpoints'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
import {
Numeric,
Breakpoint,
BreakpointBehavior,
} from '../../../const/defaultOptions'
import { Breakpoint, 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
Expand All @@ -31,31 +27,45 @@ 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,
({ displayName, value }) => {
return `(${displayName}:${String(transformNumeric(value))})`
},
)

return mediaQueryString
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,33 @@ 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',
displayName: 'height',
value: 100,
},
{
prefix: 'min',
name: 'width',
displayName: 'min-width',
value: 120,
},
{
prefix: 'max',
name: 'aspectRatio',
displayName: 'max-aspect-ratio',
value: '3/4',
},
])
})
})
})
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
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
displayName: 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<Numeric>(queryProps)
.filter(([_, propValue]) => isset(propValue))
.map<[string, Numeric]>(([propName, propValue]) => [
toDashedString(propName),
propValue,
])
breakpoint: Breakpoint,
): NormalizedQueryParam[] {
return Object.entries<Numeric>(breakpoint)
.filter(([_, value]) => isset(value))
.map(([propName, value]) => {
const [_, prefix, restName] = propName.match(/(min|max)?(.+)/)
const normalizedName = toLowerCaseFirst(restName)
const displayName = [prefix, toDashedString(normalizedName)]
.filter(Boolean)
.join('-')

return { prefix, name: normalizedName, displayName, value }
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './staticMatchMedia'
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/**
* @jest-environment node
*/
import { staticMatchMedia } from './staticMatchMedia'

describe('staticMatchMedia', () => {
describe('given "up" behavior', () => {
describe('and actual breakpoint matches', () => {
it('should return true', () => {
const result = staticMatchMedia(
{
behavior: 'up',
breakpoint: {
minWidth: 500,
maxWidth: 750,
},
},
{
width: 600,
},
)

expect(result).toHaveProperty('matches', true)
})
})

describe('and actual breakpoint does not match', () => {
it('should return false', () => {
const result = staticMatchMedia(
{
behavior: 'up',
breakpoint: {
minWidth: 500,
maxWidth: 750,
},
},
{
width: 499,
},
)

expect(result).toHaveProperty('matches', false)
})
})
})

describe('given "down" behavior', () => {
describe('and actual breakpoint matches', () => {
it('should return true', () => {
const result = staticMatchMedia(
{
behavior: 'down',
breakpoint: {
minWidth: 500,
maxWidth: 750,
},
},
{
width: 499,
},
)

expect(result).toHaveProperty('matches', true)
})
})

describe('and actual breakpoint does not match', () => {
it('should return false', () => {
const result = staticMatchMedia(
{
behavior: 'down',
breakpoint: {
minWidth: 500,
maxWidth: 750,
},
},
{
width: 751,
},
)

expect(result).toHaveProperty('matches', false)
})
})
})

describe('given "only" behavior', () => {
describe('and actual breakpoint matches', () => {
it('should return true', () => {
const result = staticMatchMedia(
{
behavior: 'only',
breakpoint: {
minWidth: 500,
maxWidth: 750,
},
},
{
width: 625,
},
)

expect(result).toHaveProperty('matches', true)
})
})

describe('and actual breakpoint is below expected', () => {
it('should return false', () => {
const result = staticMatchMedia(
{
behavior: 'only',
breakpoint: {
minWidth: 500,
maxWidth: 750,
},
},
{
width: 499,
},
)

expect(result).toHaveProperty('matches', false)
})
})

describe('and actual breakpoint is above expected', () => {
it('should return false', () => {
const result = staticMatchMedia(
{
behavior: 'only',
breakpoint: {
minWidth: 500,
maxWidth: 750,
},
},
{
width: 751,
},
)

expect(result).toHaveProperty('matches', false)
})
})
})
})
Loading