Skip to content

Commit 45fbcb0

Browse files
committed
Abstracts "transformNumeric" to prevent circular dependency in Layout
1 parent 946a8fe commit 45fbcb0

File tree

13 files changed

+131
-100
lines changed

13 files changed

+131
-100
lines changed

examples/Core/Configuration/CustomUnit.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Layout from 'atomic-layout'
33
import Notch from '../Rendering/Notch'
44

55
export default class CustomUnit extends React.Component {
6-
componentWillMount() {
6+
componentDidMount() {
77
Layout.configure({
88
defaultUnit: 'rem',
99
})

src/Layout.spec.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,4 +159,34 @@ describe('Layout', () => {
159159
})
160160
})
161161
})
162+
163+
describe('transformNumeric', () => {
164+
describe('suffixes numeric value with measurement unit', () => {
165+
it('with default measurement unit', () => {
166+
expect(Layout.transformNumeric(5)).toBe('5px')
167+
})
168+
169+
it('with custom measurement unit', () => {
170+
Layout.configure({
171+
defaultUnit: 'rem',
172+
})
173+
174+
expect(Layout.transformNumeric(3)).toBe('3rem')
175+
})
176+
})
177+
178+
it('bypasses string value', () => {
179+
expect(Layout.transformNumeric('2vh')).toBe('2vh')
180+
})
181+
182+
it('handles explicit zero as a value and no suffix is attached', () => {
183+
expect(Layout.transformNumeric('0')).toBe('0')
184+
expect(Layout.transformNumeric(0)).toBe('0')
185+
})
186+
187+
it('returns empty string when no value provided', () => {
188+
expect(Layout.transformNumeric()).toBe('')
189+
expect(Layout.transformNumeric('')).toBe('')
190+
})
191+
})
162192
})

src/Layout.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@ import defaultOptions, {
44
Breakpoints,
55
MeasurementUnit,
66
BreakpointBehavior,
7+
Numeric,
78
} from './const/defaultOptions'
9+
import { PropAliases } from './const/propAliases'
810
import invariant from './utils/invariant'
11+
import isset from './utils/functions/isset'
912

1013
class Layout {
14+
public propAliases: PropAliases = defaultOptions.propAliases
1115
public defaultUnit: MeasurementUnit = defaultOptions.defaultUnit
1216
public defaultBehavior: BreakpointBehavior = defaultOptions.defaultBehavior
1317
public breakpoints: Breakpoints = defaultOptions.breakpoints
@@ -82,6 +86,27 @@ class Layout {
8286
public getBreakpoint(breakpointName: string): Breakpoint | undefined {
8387
return this.breakpoints[breakpointName]
8488
}
89+
90+
/**
91+
* Transforms given string or number into a CSS value
92+
* taking "defaultUnit" into account.
93+
*/
94+
public transformNumeric(value?: Numeric): string {
95+
if (!isset(value)) {
96+
return ''
97+
}
98+
99+
// tslint:disable-next-line
100+
if (value == '0') {
101+
return String(value)
102+
}
103+
104+
// Suffix numeric value with the default unit.
105+
// Accept explicit (string) value as-is.
106+
const suffix = typeof value === 'number' ? this.defaultUnit : ''
107+
108+
return `${value}${suffix}`
109+
}
85110
}
86111

87112
export default new Layout()

src/components/MediaQuery.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import * as React from 'react'
2+
import Layout from '@src/Layout'
23
import { MediaQuery as MediaQueryParams } from '@const/defaultOptions'
34
import { joinQueryList } from '@utils/styles/createMediaQuery'
45
import normalizeQuery from '@src/utils/styles/normalizeQuery'
5-
import transformNumeric from '@utils/math/transformNumeric'
6+
// import transformNumeric from '@utils/math/transformNumeric'
67
import compose from '@src/utils/functions/compose'
78

89
interface Props extends MediaQueryParams {
@@ -22,7 +23,7 @@ const createMediaQuery = (queryParams: MediaQueryParams): string => {
2223
* (min-width: 750) ==> (min-width: 750px)
2324
*/
2425
const resolvedParamValue = /^\d/.test(String(paramValue))
25-
? transformNumeric(paramValue)
26+
? Layout.transformNumeric(paramValue)
2627
: paramValue
2728
return `(${paramName}:${resolvedParamValue})`
2829
}),
@@ -32,7 +33,9 @@ const createMediaQuery = (queryParams: MediaQueryParams): string => {
3233

3334
const MediaQuery = (props: Props): JSX.Element => {
3435
const { children, ...queryParams } = props
35-
const query = React.useMemo(() => createMediaQuery(queryParams), [queryParams])
36+
const query = React.useMemo(() => createMediaQuery(queryParams), [
37+
queryParams,
38+
])
3639
const [matches, setMatches] = React.useState(false)
3740

3841
const handleMediaQueryChange = (

src/const/defaultOptions.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import propAliases, { PropAliases } from './propAliases'
2+
13
type AbsoluteUnits = 'cm' | 'mm' | 'in' | 'px' | 'pt' | 'pc'
24
type RelativeUnits =
35
| '%'
@@ -16,13 +18,15 @@ export type BreakpointBehavior = 'up' | 'down' | 'only'
1618
export interface Breakpoints {
1719
[breakpointName: string]: Breakpoint
1820
}
21+
1922
export interface LayoutOptions {
23+
propAliases: PropAliases
2024
/**
2125
* Measurement unit that suffixes numeric prop values.
2226
* @default "px"
2327
* @example
2428
* <Box padding={10} />
25-
* // "padding: 10px"
29+
* @returns "padding: 10px"
2630
*/
2731
defaultUnit: MeasurementUnit
2832
/**
@@ -54,11 +58,12 @@ export interface MediaQuery {
5458
}
5559

5660
export interface Breakpoint extends MediaQuery {
57-
/* Index signature for dynamic breakpoint composition */
61+
// Index signature for dynamic breakpoint composition
5862
[propName: string]: any
5963
}
6064

6165
const defaultOptions: LayoutOptions = {
66+
propAliases,
6267
defaultUnit: 'px',
6368
defaultBehavior: 'up',
6469
defaultBreakpointName: 'xs',

src/const/propAliases.spec.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import 'jest-dom/extend-expect'
12
import React from 'react'
3+
import { render, cleanup, getByTestId } from 'react-testing-library'
4+
import Layout from '@src/Layout'
25
import propAliases from './propAliases'
36
import { Composition } from '..'
4-
import { render, cleanup, getByTestId } from 'react-testing-library'
5-
import 'jest-dom/extend-expect'
67

78
const defaultValue = 10
89
const explicitValues = {
@@ -57,7 +58,7 @@ describe('Prop aliases', () => {
5758
/* Assertion */
5859
const { output, transformValue } = propAliases[propAliasName]
5960
const expectedValue = transformValue
60-
? transformValue(propValue)
61+
? transformValue(propValue, Layout)
6162
: propValue
6263

6364
output.forEach((cssPropName) => {

src/const/propAliases.ts

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { Numeric } from '@const/defaultOptions'
22
import compose from '@utils/functions/compose'
3-
import transformNumeric from '@utils/math/transformNumeric'
43
import sanitizeTemplateArea from '@utils/strings/sanitizeTemplateArea'
54

6-
export type ValueTransformer<Input, Return> = (value: Input) => Return
5+
export type ValueTransformer<Input, Return> = (
6+
value: Input,
7+
Layout: any,
8+
) => Return
79

810
interface PropAlias {
911
output: string[]
@@ -14,6 +16,13 @@ export interface PropAliases {
1416
[aliasName: string]: PropAlias
1517
}
1618

19+
const deferredTransformNumeric: ValueTransformer<Numeric, string> = (
20+
value,
21+
Layout,
22+
) => {
23+
return Layout.transformNumeric(value)
24+
}
25+
1726
type TransformTemplateString = (template: string) => string
1827
const transformTemplateString: TransformTemplateString = compose(
1928
(areas: string[]) => areas.join('\n'),
@@ -67,35 +76,35 @@ const propAliases: PropAliases = {
6776
},
6877
gap: {
6978
output: ['grid-gap'],
70-
transformValue: transformNumeric,
79+
transformValue: deferredTransformNumeric,
7180
},
7281
gapCol: {
7382
output: ['grid-column-gap'],
74-
transformValue: transformNumeric,
83+
transformValue: deferredTransformNumeric,
7584
},
7685
gapRow: {
7786
output: ['grid-row-gap'],
78-
transformValue: transformNumeric,
87+
transformValue: deferredTransformNumeric,
7988
},
8089
gutter: {
8190
output: ['grid-gap'],
82-
transformValue: transformNumeric,
91+
transformValue: deferredTransformNumeric,
8392
},
8493
gutterCol: {
8594
output: ['grid-column-gap'],
86-
transformValue: transformNumeric,
95+
transformValue: deferredTransformNumeric,
8796
},
8897
gutterRow: {
8998
output: ['grid-row-gap'],
90-
transformValue: transformNumeric,
99+
transformValue: deferredTransformNumeric,
91100
},
92101
autoRows: {
93102
output: ['grid-auto-rows'],
94-
transformValue: transformNumeric,
103+
transformValue: deferredTransformNumeric,
95104
},
96105
autoCols: {
97106
output: ['grid-auto-columns'],
98-
transformValue: transformNumeric,
107+
transformValue: deferredTransformNumeric,
99108
},
100109
autoFlow: {
101110
output: ['grid-auto-flow'],
@@ -142,86 +151,86 @@ const propAliases: PropAliases = {
142151
/* Dimensions */
143152
height: {
144153
output: ['height'],
145-
transformValue: transformNumeric,
154+
transformValue: deferredTransformNumeric,
146155
},
147156
minHeight: {
148157
output: ['min-height'],
149-
transformValue: transformNumeric,
158+
transformValue: deferredTransformNumeric,
150159
},
151160
maxHeight: {
152161
output: ['max-height'],
153-
transformValue: transformNumeric,
162+
transformValue: deferredTransformNumeric,
154163
},
155164
width: {
156165
output: ['width'],
157-
transformValue: transformNumeric,
166+
transformValue: deferredTransformNumeric,
158167
},
159168
minWidth: {
160169
output: ['min-width'],
161-
transformValue: transformNumeric,
170+
transformValue: deferredTransformNumeric,
162171
},
163172
maxWidth: {
164173
output: ['max-width'],
165-
transformValue: transformNumeric,
174+
transformValue: deferredTransformNumeric,
166175
},
167176

168177
/* Spacing */
169178
margin: {
170179
output: ['margin'],
171-
transformValue: transformNumeric,
180+
transformValue: deferredTransformNumeric,
172181
},
173182
marginTop: {
174183
output: ['margin-top'],
175-
transformValue: transformNumeric,
184+
transformValue: deferredTransformNumeric,
176185
},
177186
marginRight: {
178187
output: ['margin-right'],
179-
transformValue: transformNumeric,
188+
transformValue: deferredTransformNumeric,
180189
},
181190
marginBottom: {
182191
output: ['margin-bottom'],
183-
transformValue: transformNumeric,
192+
transformValue: deferredTransformNumeric,
184193
},
185194
marginLeft: {
186195
output: ['margin-left'],
187-
transformValue: transformNumeric,
196+
transformValue: deferredTransformNumeric,
188197
},
189198
marginVertical: {
190199
output: ['margin-top', 'margin-bottom'],
191-
transformValue: transformNumeric,
200+
transformValue: deferredTransformNumeric,
192201
},
193202
marginHorizontal: {
194203
output: ['margin-right', 'margin-left'],
195-
transformValue: transformNumeric,
204+
transformValue: deferredTransformNumeric,
196205
},
197206

198207
padding: {
199208
output: ['padding'],
200-
transformValue: transformNumeric,
209+
transformValue: deferredTransformNumeric,
201210
},
202211
paddingTop: {
203212
output: ['padding-top'],
204-
transformValue: transformNumeric,
213+
transformValue: deferredTransformNumeric,
205214
},
206215
paddingRight: {
207216
output: ['padding-right'],
208-
transformValue: transformNumeric,
217+
transformValue: deferredTransformNumeric,
209218
},
210219
paddingBottom: {
211220
output: ['padding-bottom'],
212-
transformValue: transformNumeric,
221+
transformValue: deferredTransformNumeric,
213222
},
214223
paddingLeft: {
215224
output: ['padding-left'],
216-
transformValue: transformNumeric,
225+
transformValue: deferredTransformNumeric,
217226
},
218227
paddingVertical: {
219228
output: ['padding-top', 'padding-bottom'],
220-
transformValue: transformNumeric,
229+
transformValue: deferredTransformNumeric,
221230
},
222231
paddingHorizontal: {
223232
output: ['padding-right', 'padding-left'],
224-
transformValue: transformNumeric,
233+
transformValue: deferredTransformNumeric,
225234
},
226235
}
227236

src/utils/breakpoints/getAreaBreakpoints/getAreaBreakpoints.spec.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import Layout from '../../../Layout'
2-
import transformNumeric from '../../math/transformNumeric'
32
import getAreasList from '../../templates/getAreasList'
43
import getAreaBreakpoints from './getAreaBreakpoints'
54

@@ -38,7 +37,9 @@ describe('getAreaBreakpoints', () => {
3837
{
3938
behavior: 'down',
4039
minWidth: breakpointMd.minWidth,
41-
maxWidth: `calc(${transformNumeric(breakpointXl.minWidth)} - 1px)`,
40+
maxWidth: `calc(${Layout.transformNumeric(
41+
breakpointXl.minWidth,
42+
)} - 1px)`,
4243
},
4344
null,
4445
])
@@ -60,7 +61,9 @@ describe('getAreaBreakpoints', () => {
6061
{
6162
behavior: 'down',
6263
minWidth: breakpointXs.minWidth,
63-
maxWidth: `calc(${transformNumeric(breakpointMd.minWidth)} - 1px)`,
64+
maxWidth: `calc(${Layout.transformNumeric(
65+
breakpointMd.minWidth,
66+
)} - 1px)`,
6467
},
6568
null,
6669
{
@@ -87,7 +90,9 @@ describe('getAreaBreakpoints', () => {
8790
{
8891
behavior: 'down',
8992
minWidth: undefined,
90-
maxWidth: `calc(${transformNumeric(breakpointSm.minWidth)} - 1px)`,
93+
maxWidth: `calc(${Layout.transformNumeric(
94+
breakpointSm.minWidth,
95+
)} - 1px)`,
9196
},
9297
null,
9398
{

0 commit comments

Comments
 (0)