Skip to content

Commit

Permalink
feat: [FX-4259] add new spacing values to picasso (#3835)
Browse files Browse the repository at this point in the history
* feat: add new spacing values to picasso

* chore: enhance spacings

* chore: update spacings

* chore: add unit tests for spacings

* chore: update changeset

* chore: fix story
  • Loading branch information
dmaklygin authored and sashuk committed Sep 25, 2023
1 parent c715a0a commit 2c38c49
Show file tree
Hide file tree
Showing 13 changed files with 238 additions and 29 deletions.
7 changes: 7 additions & 0 deletions .changeset/lovely-socks-cheat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@toptal/picasso-provider': minor
'@toptal/picasso': minor
'@toptal/picasso-shared': minor
---

- add new spacing values to picasso's theme and to `picasso-shared`. See RFC: https://github.com/toptal/picasso/blob/master/docs/decisions/18-spacings.md
2 changes: 2 additions & 0 deletions packages/picasso-provider/src/Picasso/PicassoProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
typography,
sizes,
shadows,
spacings,
} from './config'

const picasso: ThemeOptions = {
Expand Down Expand Up @@ -40,6 +41,7 @@ const picasso: ThemeOptions = {
notched: false,
},
},
spacings,
}

class Provider {
Expand Down
2 changes: 2 additions & 0 deletions packages/picasso-provider/src/Picasso/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ export {
} from './breakpoints'
export { default as layout } from './layout'
export { default as shadows } from './shadows'
export { default as spacings, SpacingEnum } from './spacings'
export type { Sizes, SizeType, SpacingType, PicassoSpacing } from './spacings'
75 changes: 75 additions & 0 deletions packages/picasso-provider/src/Picasso/config/spacings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// BASE-aligned spacing values in "rem" units
type PicassoSpacingValues = 0 | 0.25 | 0.5 | 0.75 | 1 | 1.5 | 2 | 2.5 | 3

export type Sizes =
| 'xxsmall'
| 'xsmall'
| 'small'
| 'medium'
| 'large'
| 'xlarge'

export type SizeType<T extends Sizes> = T

/** @deprecated **/
type DeprecatedSpacingType =
| number
| SizeType<'xsmall' | 'small' | 'medium' | 'large' | 'xlarge'>

export type SpacingType = PicassoSpacing | DeprecatedSpacingType

export enum SpacingEnum {
xsmall = 0.5,
small = 1,
medium = 1.5,
large = 2,
xlarge = 2.5,
}

class PicassoSpacing {
#value: PicassoSpacingValues

private constructor(value: PicassoSpacingValues) {
this.#value = value
}

static create(value: PicassoSpacingValues): PicassoSpacing {
return new PicassoSpacing(value)
}

valueOf(): PicassoSpacingValues {
return this.#value
}

toString(): string {
return this.#value.toString()
}
}

export type { PicassoSpacing }

export const isPicassoSpacing = (
spacing: SpacingType
): spacing is PicassoSpacing => spacing instanceof PicassoSpacing

export const SPACING_0 = PicassoSpacing.create(0)
export const SPACING_1 = PicassoSpacing.create(0.25)
export const SPACING_2 = PicassoSpacing.create(0.5)
export const SPACING_3 = PicassoSpacing.create(0.75)
export const SPACING_4 = PicassoSpacing.create(1)
export const SPACING_6 = PicassoSpacing.create(1.5)
export const SPACING_8 = PicassoSpacing.create(2)
export const SPACING_10 = PicassoSpacing.create(2.5)
export const SPACING_12 = PicassoSpacing.create(3)

export default {
SPACING_0,
SPACING_1,
SPACING_2,
SPACING_3,
SPACING_4,
SPACING_6,
SPACING_8,
SPACING_10,
SPACING_12,
}
10 changes: 10 additions & 0 deletions packages/picasso-provider/src/Picasso/config/theme.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import type { BreakpointKeys } from './breakpoints'
import type { Layout } from './layout'
import type { Sizes } from './sizes'
import type spacings from './spacings'
import type { PicassoSpacing } from './spacings'

declare module '@material-ui/core/styles' {
interface Theme {
layout: Layout
sizes: Sizes
screens: (...sizes: BreakpointKeys[]) => string
spacings: Record<keyof typeof spacings, PicassoSpacing>
}

interface ThemeOptions {
layout?: Layout
sizes?: Sizes
screens?: (...sizes: BreakpointKeys[]) => string
spacings?: Record<keyof typeof spacings, PicassoSpacing>
}

interface ThemeOptions {
Expand Down
1 change: 1 addition & 0 deletions packages/picasso-provider/src/Picasso/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './generate-random-string'
export * from './get-serverside-stylesheets'
export * from './spacings'
74 changes: 74 additions & 0 deletions packages/picasso-provider/src/Picasso/utils/spacings.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { isNumericSpacing, spacingToRem } from './spacings'
import {
SPACING_0,
SPACING_1,
SPACING_2,
SPACING_3,
SPACING_4,
SPACING_6,
SPACING_8,
SPACING_10,
SPACING_12,
} from '../config/spacings'

describe('spacingUtils', () => {
describe('isNumericSpacing', () => {
describe('when spacing is undefined', () => {
it('returns false', () => {
expect(isNumericSpacing(undefined)).toBe(false)
})
})

describe('when spacing is a number', () => {
it('returns true', () => {
expect(isNumericSpacing(1)).toBe(true)
expect(isNumericSpacing(2.5)).toBe(true)
})
})

describe('when spacing is a new Picasso spacing', () => {
it('returns true', () => {
expect(isNumericSpacing(SPACING_0)).toBe(true)
})
})

describe('when spacing is a string Picasso spacing', () => {
it('returns false', () => {
expect(isNumericSpacing('small')).toBe(false)
})
})
})

describe('spacingToRem', () => {
describe('when spacing is a number', () => {
it('converts to rem', () => {
expect(spacingToRem(1)).toBe('1rem')
expect(spacingToRem(2.5)).toBe('2.5rem')
})
})

describe('when spacing is a valid Picasso spacing', () => {
it('converts to rem', () => {
expect(spacingToRem(SPACING_0)).toBe('0rem')
expect(spacingToRem(SPACING_1)).toBe('0.25rem')
expect(spacingToRem(SPACING_2)).toBe('0.5rem')
expect(spacingToRem(SPACING_3)).toBe('0.75rem')
expect(spacingToRem(SPACING_4)).toBe('1rem')
expect(spacingToRem(SPACING_6)).toBe('1.5rem')
expect(spacingToRem(SPACING_8)).toBe('2rem')
expect(spacingToRem(SPACING_10)).toBe('2.5rem')
expect(spacingToRem(SPACING_12)).toBe('3rem')
})
})

describe('when spacing is a string Picasso spacing', () => {
it('converts to rem', () => {
expect(spacingToRem('xsmall')).toBe('0.5rem')
expect(spacingToRem('small')).toBe('1rem')
expect(spacingToRem('medium')).toBe('1.5rem')
expect(spacingToRem('large')).toBe('2rem')
expect(spacingToRem('xlarge')).toBe('2.5rem')
})
})
})
})
19 changes: 19 additions & 0 deletions packages/picasso-provider/src/Picasso/utils/spacings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { SizeType, SpacingType } from '../config/spacings'
import { SpacingEnum, isPicassoSpacing } from '../config/spacings'

const isNumericSpacing = (
spacing: SpacingType | undefined
): spacing is number | SpacingType => {
const isNotNull = spacing != null
const isNumber = typeof spacing == 'number'

return isNotNull && (isNumber || isPicassoSpacing(spacing))
}

const spacingToRem = (spacing: SpacingType): string => {
return isNumericSpacing(spacing)
? `${spacing}rem`
: `${SpacingEnum[spacing as SizeType<any>]}rem`
}

export { isNumericSpacing, spacingToRem }
11 changes: 11 additions & 0 deletions packages/picasso-provider/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ export {
shadows,
PicassoBreakpoints,
BreakpointKeys,
spacings,
SpacingEnum,
} from './Picasso/config'

export type {
Sizes,
SizeType,
SpacingType,
PicassoSpacing,
} from './Picasso/config'

export { default as PicassoProvider } from './Picasso/PicassoProvider'
Expand All @@ -44,6 +53,8 @@ export {
generateRandomString,
generateRandomStringOrGetEmptyInTest,
getServersideStylesheets,
isNumericSpacing,
spacingToRem,
} from './Picasso/utils'

export { default as Favicon } from './Favicon'
14 changes: 7 additions & 7 deletions packages/picasso/src/Container/Container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { makeStyles } from '@material-ui/core/styles'
import type { PropTypes } from '@material-ui/core'
import cx from 'classnames'
import type { StandardProps, SpacingType } from '@toptal/picasso-shared'
import { spacingToRem } from '@toptal/picasso-shared'
import { spacingToRem, isNumericSpacing } from '@toptal/picasso-shared'

import type { AlignItemsType, JustifyContentType, VariantType } from './styles'
import styles from './styles'
Expand Down Expand Up @@ -101,12 +101,12 @@ export const Container = documentable(
const classes = useStyles(props)

const margins = {
...(typeof top === 'number' && { marginTop: spacingToRem(top) }),
...(typeof bottom === 'number' && {
...(isNumericSpacing(top) && { marginTop: spacingToRem(top) }),
...(isNumericSpacing(bottom) && {
marginBottom: spacingToRem(bottom),
}),
...(typeof left === 'number' && { marginLeft: spacingToRem(left) }),
...(typeof right === 'number' && { marginRight: spacingToRem(right) }),
...(isNumericSpacing(left) && { marginLeft: spacingToRem(left) }),
...(isNumericSpacing(right) && { marginRight: spacingToRem(right) }),
}

return (
Expand Down Expand Up @@ -144,10 +144,10 @@ export const Container = documentable(
)}
style={{
...margins,
...(typeof padded === 'number' && {
...(isNumericSpacing(padded) && {
padding: spacingToRem(padded),
}),
...(typeof gap === 'number' && { gap: spacingToRem(gap) }),
...(isNumericSpacing(gap) && { gap: spacingToRem(gap) }),
...style,
}}
>
Expand Down
5 changes: 3 additions & 2 deletions packages/picasso/src/Container/story/Default.example.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React from 'react'
import { Container } from '@toptal/picasso'
import { SPACING_4, SPACING_8 } from '@toptal/picasso/utils'

const Example = () => (
<div>
<Container bottom='large'>Some text</Container>
<Container left='small'>Some more text with a small margin</Container>
<Container bottom={SPACING_8}>Some text</Container>
<Container left={SPACING_4}>Some more text with a small margin</Container>
</div>
)

Expand Down
14 changes: 13 additions & 1 deletion packages/picasso/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { alpha, lighten, darken } from '@toptal/picasso-shared'
import { alpha, lighten, darken, spacings } from '@toptal/picasso-shared'

import * as TransitionUtils from './Transitions'

Expand Down Expand Up @@ -66,3 +66,15 @@ export {
useDeprecationWarning,
usePropDeprecationWarning,
} from './use-deprecation-warnings'

export const {
SPACING_0,
SPACING_1,
SPACING_2,
SPACING_3,
SPACING_4,
SPACING_6,
SPACING_8,
SPACING_10,
SPACING_12,
} = spacings
33 changes: 14 additions & 19 deletions packages/shared/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@ import type {

import type { Classes } from './styles'

export {
spacings,
SpacingEnum,
isNumericSpacing,
spacingToRem,
} from '@toptal/picasso-provider'

export type {
Sizes,
SizeType,
SpacingType,
PicassoSpacing,
} from '@toptal/picasso-provider'

export interface BaseProps {
/** Classnames applied to root element */
className?: string
Expand Down Expand Up @@ -52,8 +66,6 @@ export interface OverridableComponent<P = {}> extends NamedComponent<P> {
): JSX.Element | null
}

type Sizes = 'xxsmall' | 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge'

type BaseEnvironments = 'development' | 'staging' | 'production'
type Environments = BaseEnvironments | 'temploy' | 'test'

Expand All @@ -62,23 +74,6 @@ export type EnvironmentType<T extends Environments = BaseEnvironments> =
| T
| BaseEnvironments

export type SizeType<T extends Sizes> = T

export type SpacingType =
| number
| SizeType<'xsmall' | 'small' | 'medium' | 'large' | 'xlarge'>

export enum SpacingEnum {
xsmall = 0.5,
small = 1,
medium = 1.5,
large = 2,
xlarge = 2.5,
}

export const spacingToRem = (spacing: SpacingType) =>
typeof spacing === 'number' ? `${spacing}rem` : `${SpacingEnum[spacing]}rem`

export type ButtonOrAnchorProps = AnchorHTMLAttributes<HTMLAnchorElement> &
ButtonHTMLAttributes<HTMLButtonElement>

Expand Down

0 comments on commit 2c38c49

Please sign in to comment.