diff --git a/src/frontend/apps/impress/jest.setup.ts b/src/frontend/apps/impress/jest.setup.ts index 3aa7aec9..564d8a6d 100644 --- a/src/frontend/apps/impress/jest.setup.ts +++ b/src/frontend/apps/impress/jest.setup.ts @@ -1,3 +1,4 @@ +import '@testing-library/jest-dom'; import * as dotenv from 'dotenv'; dotenv.config({ path: './.env.test' }); diff --git a/src/frontend/apps/impress/src/components/Box.tsx b/src/frontend/apps/impress/src/components/Box.tsx index cd18c359..dc5e2bb2 100644 --- a/src/frontend/apps/impress/src/components/Box.tsx +++ b/src/frontend/apps/impress/src/components/Box.tsx @@ -2,6 +2,12 @@ import { ComponentPropsWithRef, ReactHTML } from 'react'; import styled from 'styled-components'; import { CSSProperties } from 'styled-components/dist/types'; +import { + MarginPadding, + stylesMargin, + stylesPadding, +} from '@/utils/styleBuilder'; + import { hideEffect, showEffect } from './Effect'; export interface BoxProps { @@ -18,11 +24,13 @@ export interface BoxProps { $height?: CSSProperties['height']; $justify?: CSSProperties['justifyContent']; $overflow?: CSSProperties['overflow']; + $margin?: MarginPadding; + $maxWidth?: CSSProperties['maxWidth']; + $minWidth?: CSSProperties['minWidth']; + $padding?: MarginPadding; $position?: CSSProperties['position']; $radius?: CSSProperties['borderRadius']; $width?: CSSProperties['width']; - $maxWidth?: CSSProperties['maxWidth']; - $minWidth?: CSSProperties['minWidth']; } export type BoxType = ComponentPropsWithRef; @@ -39,12 +47,14 @@ export const Box = styled('div')` ${({ $gap }) => $gap && `gap: ${$gap};`} ${({ $height }) => $height && `height: ${$height};`} ${({ $justify }) => $justify && `justify-content: ${$justify};`} + ${({ $margin }) => $margin && stylesMargin($margin)} + ${({ $maxWidth }) => $maxWidth && `max-width: ${$maxWidth};`} + ${({ $minWidth }) => $minWidth && `min-width: ${$minWidth};`} ${({ $overflow }) => $overflow && `overflow: ${$overflow};`} + ${({ $padding }) => $padding && stylesPadding($padding)} ${({ $position }) => $position && `position: ${$position};`} ${({ $radius }) => $radius && `border-radius: ${$radius};`} ${({ $width }) => $width && `width: ${$width};`} - ${({ $maxWidth }) => $maxWidth && `max-width: ${$maxWidth};`} - ${({ $minWidth }) => $minWidth && `min-width: ${$minWidth};`} ${({ $css }) => $css && `${$css};`} ${({ $effect }) => { let effect; diff --git a/src/frontend/apps/impress/src/components/__tests__/Box.spec.tsx b/src/frontend/apps/impress/src/components/__tests__/Box.spec.tsx new file mode 100644 index 00000000..e1e27ce6 --- /dev/null +++ b/src/frontend/apps/impress/src/components/__tests__/Box.spec.tsx @@ -0,0 +1,51 @@ +import { render, screen } from '@testing-library/react'; + +import { Box } from '../Box'; + +describe('', () => { + it('has the padding from prop', () => { + const { unmount } = render(My Box); + + expect(screen.getByText('My Box')).toHaveStyle('padding: 10px'); + + unmount(); + + render( + + My Box + , + ); + + expect(screen.getByText('My Box')).toHaveStyle(` + padding-left: 4rem; + padding-right: 4rem; + padding-top: 3rem; + padding-bottom: 0.5rem;`); + }); + + it('has the margin from prop', () => { + const { unmount } = render(My Box); + expect(screen.getByText('My Box')).toHaveStyle('margin: 10px'); + + unmount(); + + render( + + My Box + , + ); + + expect(screen.getByText('My Box')).toHaveStyle(` + margin-left: auto; + margin-right: auto; + margin-top: 1.625rem; + margin-bottom: 100%;`); + }); +}); diff --git a/src/frontend/apps/impress/src/utils/styleBuilder.ts b/src/frontend/apps/impress/src/utils/styleBuilder.ts new file mode 100644 index 00000000..cab11c9b --- /dev/null +++ b/src/frontend/apps/impress/src/utils/styleBuilder.ts @@ -0,0 +1,79 @@ +import { tokens } from '@/cunningham/cunningham-tokens'; + +/* eslint-disable @typescript-eslint/no-unused-vars */ +const { + '0': _0, + st, + t, + s, + b, + bx, + l, + ...spacingsLight +} = tokens.themes.default.theme.spacings; +/* eslint-enable @typescript-eslint/no-unused-vars */ + +const spacings = { + xtiny: tokens.themes.default.theme.spacings['st'], + tiny: tokens.themes.default.theme.spacings['t'], + small: tokens.themes.default.theme.spacings['s'], + big: tokens.themes.default.theme.spacings['b'], + xbig: tokens.themes.default.theme.spacings['bx'], + large: tokens.themes.default.theme.spacings['l'], + ...spacingsLight, +}; + +type SpacingsKey = keyof typeof spacings; +// eslint-disable-next-line @typescript-eslint/ban-types +export type Spacings = SpacingsKey | (string & {}); + +export const spacingValue = (value?: Spacings) => + value && value in spacings ? spacings[value as SpacingsKey] : value; + +export type MarginPadding = + | Spacings + | { + vertical?: Spacings; + horizontal?: Spacings; + top?: Spacings; + bottom?: Spacings; + left?: Spacings; + right?: Spacings; + all?: Spacings; + }; + +export const stylesPadding = (pad: MarginPadding) => { + if (typeof pad === 'object') { + return { + 'padding-top': spacingValue(pad.top || pad.vertical || pad.all), + 'padding-bottom': spacingValue(pad.bottom || pad.vertical || pad.all), + 'padding-left': spacingValue(pad.left || pad.horizontal || pad.all), + 'padding-right': spacingValue(pad.right || pad.horizontal || pad.all), + }; + } else { + return { + padding: spacingValue(pad), + }; + } +}; + +export const stylesMargin = (margin: MarginPadding) => { + if (typeof margin === 'object') { + return { + 'margin-top': spacingValue(margin.top || margin.vertical || margin.all), + 'margin-bottom': spacingValue( + margin.bottom || margin.vertical || margin.all, + ), + 'margin-left': spacingValue( + margin.left || margin.horizontal || margin.all, + ), + 'margin-right': spacingValue( + margin.right || margin.horizontal || margin.all, + ), + }; + } else { + return { + margin: spacingValue(margin), + }; + } +};