Skip to content

Commit

Permalink
feat(asCol): add asCol HOC (#378)
Browse files Browse the repository at this point in the history
* feat(asCol): add asCol HOC
  • Loading branch information
arahansen authored Mar 23, 2018
1 parent 3b781f6 commit b92464a
Show file tree
Hide file tree
Showing 14 changed files with 184 additions and 88 deletions.
19 changes: 19 additions & 0 deletions src/asCol/__snapshots__/asCol.spec.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Col should override defaults when passing margin prop 1`] = `
Object {
"margin": 1,
}
`;

exports[`Col should render a Grid with col margins ([0, 1.5, 3]) 1`] = `
Object {
"className": "{\\"border\\":\\"0 transparent solid\\",\\"boxSizing\\":\\"border-box\\",\\"display\\":\\"flex\\",\\"flexFlow\\":\\"row wrap\\",\\"width\\":\\"100%\\"} {\\"flexGrow\\":1}",
"style": Object {
"borderBottomWidth": 24,
"borderLeftWidth": 12,
"borderRightWidth": 12,
"borderTopWidth": 0,
},
}
`;
42 changes: 42 additions & 0 deletions src/asCol/asCol.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react'
import { mount } from 'enzyme'
import log from '../log'
import asCol from './index'

describe('Col', () => {
let wrapper
const Col = asCol('div')

it('should render a Grid with col margins ([0, 1.5, 3])', () => {
wrapper = mount(<Col />)
const gridProps = wrapper.find('div').props()
expect(gridProps).toMatchSnapshot()
})

it('should override defaults when passing margin prop', () => {
wrapper = mount(<Col margin={1} />)
const gridProps = wrapper.find('Grid').props()
expect(gridProps).toMatchSnapshot()
})

it('should render custom elements', () => {
const StrongCol = asCol('strong')

wrapper = mount(<StrongCol />)
expect(wrapper.find('strong').length).toBe(1)
})

fit('should not error when passed valid props', () => {
spyOn(log, 'error')

wrapper = mount(<Col margin={0} />)

expect(log.error).not.toHaveBeenCalled()
})

afterEach(() => {
if (wrapper) {
wrapper.unmount()
}
})
})
24 changes: 24 additions & 0 deletions src/asCol/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// @flow
import * as React from 'react'
import asGrid from '../asGrid'
import type { OneResolution } from '../types'

function mapProps(props): OneResolution {
const defaultProps: OneResolution = {
marginTop: 0,
marginRight: 'gutter/2',
marginBottom: 'verticalGutter',
marginLeft: 'gutter/2',
}

return props.margin !== undefined
? props
: {
...defaultProps,
...props,
}
}

export default function asCol(Component: React.ComponentType<*> | string) {
return asGrid(Component, mapProps)
}
8 changes: 6 additions & 2 deletions src/asGrid/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
OneResolutionGrid,
ConfigProviderContext,
GridProps,
OneResolution,
} from '../types'
import { styles, getCol } from './grid.styles'
import { getValue } from '../utils'
Expand All @@ -14,7 +15,10 @@ import withResolution from '../withResolution'
const resolutionProperties = ['align', 'justify', 'size']

export default function asGrid(
Component: React.ComponentType<*> | string
Component: React.ComponentType<*> | string,
mapDefaultProps?: (
props: $Shape<OneResolution>
) => $Shape<OneResolution> = props => props
): React.ComponentType<GridProps> {
function Grid(
{
Expand All @@ -27,7 +31,7 @@ export default function asGrid(
}: OneResolutionGrid,
context: ConfigProviderContext
) {
const props = getCoreStyles(restProps, context)
const props = getCoreStyles(mapDefaultProps(restProps), context)
const classes = compact([
styles.grid,
getCol(size, getValue(context, 'columns')),
Expand Down
8 changes: 6 additions & 2 deletions src/asLayout/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
OneResolutionLayout,
ConfigProviderContext,
LayoutProps,
OneResolution,
} from '../types'
import getStyles from './layout.styles'
import getCoreStyles from '../core'
Expand All @@ -14,7 +15,10 @@ import withResolution from '../withResolution'
const resolutionProperties = ['fixed', 'height', 'overflow']

export default function asLayout(
Component: React.ComponentType<*> | string
Component: React.ComponentType<*> | string,
mapDefaultProps?: (
props: $Shape<OneResolution>
) => $Shape<OneResolution> = props => props
): React.ComponentType<LayoutProps> {
function Layout(
{
Expand All @@ -27,7 +31,7 @@ export default function asLayout(
}: OneResolutionLayout,
context: ConfigProviderContext
) {
const props = getCoreStyles(restProps, context)
const props = getCoreStyles(mapDefaultProps(restProps), context)
const styles = getStyles(getValues(context, restProps))
const classes = compact([
className,
Expand Down
14 changes: 14 additions & 0 deletions src/asLayout/layout.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ describe('asLayout', () => {
expect(spy).toHaveBeenCalledWith(wrapper.find('div').instance())
})

it('should allow custom defaults', () => {
const CustomLayout = asLayout('strong', () => ({
marginTop: 2,
marginBottom: 1.5,
}))

wrapper = mount(<CustomLayout />)

expect(wrapper.find('strong').props().style).toEqual({
borderBottomWidth: 12,
borderTopWidth: 16,
})
})

afterEach(() => {
if (wrapper) {
wrapper.unmount()
Expand Down
6 changes: 6 additions & 0 deletions src/col.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// @flow
import * as React from 'react'
import asCol from './asCol'
import type { GridProps } from './types'

export default (asCol('div'): React.ComponentType<GridProps>)
43 changes: 0 additions & 43 deletions src/col/col.spec.js

This file was deleted.

24 changes: 0 additions & 24 deletions src/col/index.js

This file was deleted.

15 changes: 12 additions & 3 deletions src/core/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// @flow
import { combineSpacing, getValue } from '../utils'
import { combineSpacing, getValues } from '../utils'
import type { ConfigProviderContext, OneResolution } from '../types'

export default function getCoreStyles(
Expand All @@ -22,6 +22,13 @@ export default function getCoreStyles(
...restProps
} = props

const {
gutter,
verticalGutter,
base: contextBase,
spacingAliases,
} = getValues(context, props)

const spacing = combineSpacing({
spacingProps: {
margin,
Expand All @@ -35,8 +42,10 @@ export default function getCoreStyles(
paddingBottom,
paddingLeft,
},
base: getValue(context, 'base', base),
spacingAliases: getValue(context, 'spacingAliases'),
base: base === undefined ? contextBase : base,
spacingAliases,
gutter,
verticalGutter,
})

const spacingStyles = { ...style, ...spacing }
Expand Down
1 change: 1 addition & 0 deletions src/gymnast.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export { default as Offset } from './offset'
export { default as Root } from './root'

// HOC
export { default as asCol } from './asCol'
export { default as asGrid } from './asGrid'
export { default as asLayout } from './asLayout'
export { default as withResolution } from './withResolution'
Expand Down
49 changes: 35 additions & 14 deletions src/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ export const kebabCase = (str: string) =>
.replace(/^[A-Z]/, match => match.toLowerCase())
.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`)

export function validateSpacingProps(props: SpacingProps) {
export function validateSpacingProps(
props: SpacingProps & {
marginArray?: Array<number>,
paddingArray?: Array<number>,
}
) {
if (process.env.NODE_ENV === 'production') {
return true
}
Expand Down Expand Up @@ -118,7 +123,7 @@ export function getCSS(

export function parseSpacing(
spacing: any,
spacingAliases?: SpacingAliases
spacingAliases: SpacingAliases
): number[] | void {
if (typeof spacing === 'undefined') {
return undefined
Expand All @@ -133,7 +138,6 @@ export function parseSpacing(
} else if (typeof spacing === 'string') {
spacingArray = spacing.split(splitPattern)
}

if (spacingArray) {
return replaceSpacingAliases(spacingArray, spacingAliases).map(parseFloat)
}
Expand All @@ -144,7 +148,7 @@ export function parseSpacing(

function replaceSpacingAlias(
value: SpacingValues,
spacingAliases: SpacingAliases = defaults.spacingAliases
spacingAliases: SpacingAliases
) {
if (spacingAliases && typeof value === 'string' && value in spacingAliases) {
return spacingAliases[value]
Expand All @@ -154,19 +158,22 @@ function replaceSpacingAlias(

export function replaceSpacingAliases(
spacingArray: Array<SpacingValues>,
spacingAliases?: SpacingAliases
spacingAliases: SpacingAliases
): Array<SpacingValues> {
return spacingArray.map(value => replaceSpacingAlias(value, spacingAliases))
}

function replaceSpacingAliasValues(
spacingObject: { [string]: SpacingValues },
spacingAliases?: SpacingAliases
): { [string]: SpacingValues } {
return Object.keys(spacingObject).reduce(
function replaceSpacingAliasValues({
props,
spacingAliases,
}: {
props: { [string]: SpacingValues },
spacingAliases: SpacingAliases,
}): { [string]: SpacingValues } {
return Object.keys(props).reduce(
(acc, key) => ({
...acc,
[key]: replaceSpacingAlias(spacingObject[key], spacingAliases),
[key]: replaceSpacingAlias(props[key], spacingAliases),
}),
{}
)
Expand All @@ -176,16 +183,27 @@ type CombineSpacingSettings = {
spacingProps: SpacingProps,
base: number,
spacingAliases?: SpacingAliases,
gutter: number,
verticalGutter: number,
}

export function combineSpacing({
spacingProps,
base,
spacingAliases,
gutter = defaults.gutter,
verticalGutter = defaults.verticalGutter,
}: CombineSpacingSettings) {
const combinedSpacingAliases = {
...defaults.spacingAliases,
...spacingAliases,
gutter,
'gutter/2': gutter / 2,
verticalGutter,
}
const { margin, padding, ...props } = spacingProps
const marginArray = parseSpacing(margin, spacingAliases)
const paddingArray = parseSpacing(padding, spacingAliases)
const marginArray = parseSpacing(margin, combinedSpacingAliases)
const paddingArray = parseSpacing(padding, combinedSpacingAliases)

if (
!validateSpacingProps({
Expand All @@ -198,7 +216,10 @@ export function combineSpacing({
}

const flatProps = {
...replaceSpacingAliasValues(props, spacingAliases),
...replaceSpacingAliasValues({
props,
spacingAliases: combinedSpacingAliases,
}),
...getSpacing(marginArray, 'margin'),
...getSpacing(paddingArray, 'padding'),
}
Expand Down
Loading

0 comments on commit b92464a

Please sign in to comment.