Skip to content

Commit

Permalink
Feat(web-react): Additional parameter for styleProps and Box refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
curdaj committed Dec 27, 2024
1 parent 4241e5b commit 5d781e9
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 122 deletions.
3 changes: 2 additions & 1 deletion packages/web-react/src/components/Box/Box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import classNames from 'classnames';
import React, { ElementType } from 'react';
import { PaddingStyleProps } from '../../constants';
import { useStyleProps } from '../../hooks';
import { SpiritBoxProps } from '../../types';
import { useBoxStyleProps } from './useBoxStyleProps';
Expand All @@ -15,7 +16,7 @@ const Box = <T extends ElementType = 'div'>(props: SpiritBoxProps<T>) => {
const { elementType: ElementTag = 'div', children, ...restProps } = propsWithDefaults;

const { classProps, props: modifiedProps, styleProps: boxStyle } = useBoxStyleProps(restProps);
const { styleProps, props: otherProps } = useStyleProps(modifiedProps);
const { styleProps, props: otherProps } = useStyleProps(modifiedProps, PaddingStyleProps);

const boxStyleProps = {
style: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,78 +19,6 @@ describe('useBoxStyleProps', () => {
expect(result.current.classProps).toBe('bg-secondary');
});

it('should return padding classProps', () => {
const props: SpiritBoxProps = {
padding: 'space-400',
};
const { result } = renderHook(() => useBoxStyleProps(props));

expect(result.current.classProps).toBe('p-400');
});

it('should return paddingX classProps', () => {
const props: SpiritBoxProps = {
paddingX: 'space-400',
};
const { result } = renderHook(() => useBoxStyleProps(props));

expect(result.current.classProps).toBe('px-400');
});

it('should return paddingY classProps', () => {
const props: SpiritBoxProps = {
paddingY: 'space-400',
};
const { result } = renderHook(() => useBoxStyleProps(props));

expect(result.current.classProps).toBe('py-400');
});

it('should return paddingTop classProps', () => {
const props: SpiritBoxProps = {
paddingTop: 'space-400',
};
const { result } = renderHook(() => useBoxStyleProps(props));

expect(result.current.classProps).toBe('pt-400');
});

it('should return paddingBottom classProps', () => {
const props: SpiritBoxProps = {
paddingBottom: 'space-400',
};
const { result } = renderHook(() => useBoxStyleProps(props));

expect(result.current.classProps).toBe('pb-400');
});

it('should return paddingLeft classProps', () => {
const props: SpiritBoxProps = {
paddingLeft: 'space-400',
};
const { result } = renderHook(() => useBoxStyleProps(props));

expect(result.current.classProps).toBe('pl-400');
});

it('should return paddingRight classProps', () => {
const props: SpiritBoxProps = {
paddingRight: 'space-400',
};
const { result } = renderHook(() => useBoxStyleProps(props));

expect(result.current.classProps).toBe('pr-400');
});

it('should return responsive padding classProps', () => {
const props: SpiritBoxProps = {
padding: { mobile: 'space-400', tablet: 'space-500', desktop: 'space-600' },
};
const { result } = renderHook(() => useBoxStyleProps(props));

expect(result.current.classProps).toBe('p-400 p-tablet-500 p-desktop-600');
});

it('should return border radius classProps', () => {
const props: SpiritBoxProps = {
borderRadius: '200',
Expand Down
48 changes: 3 additions & 45 deletions packages/web-react/src/components/Box/useBoxStyleProps.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import classNames from 'classnames';
import { CSSProperties, ElementType } from 'react';
import { BorderColors } from '../../constants';
import { BreakpointToken, SpaceToken, SpiritBoxProps } from '../../types';
import { SpiritBoxProps } from '../../types';

interface BoxCSSProperties extends CSSProperties {
[key: string]: string | undefined | number;
Expand All @@ -16,42 +16,10 @@ export interface UseBoxStyleProps<T> {
styleProps: BoxCSSProperties;
}

export const generateResponsiveUtilityClasses = (
prefix: string,
propValue: SpaceToken | Partial<Record<BreakpointToken, SpaceToken>> | undefined,
): string[] => {
if (propValue && typeof propValue === 'object') {
return Object.entries(propValue).map(([breakpoint, value]) => {
const classValue = value?.replace('space-', '');

return breakpoint === 'mobile' ? `${prefix}-${classValue}` : `${prefix}-${breakpoint}-${classValue}`;
});
}

if (propValue && typeof propValue !== 'object') {
return [`${prefix}-${propValue.replace('space-', '')}`];
}

return [];
};

export const useBoxStyleProps = (
props: Partial<SpiritBoxProps<ElementType>>,
): UseBoxStyleProps<Partial<SpiritBoxProps<ElementType>>> => {
const {
backgroundColor,
borderColor,
borderRadius,
borderWidth,
padding,
paddingBottom,
paddingLeft,
paddingRight,
paddingTop,
paddingX,
paddingY,
...restProps
} = props || {};
const { backgroundColor, borderColor, borderRadius, borderWidth, ...restProps } = props || {};
const boxStyle: BoxCSSProperties = {};

const boxBackgroundColor = backgroundColor ? `bg-${backgroundColor}` : '';
Expand All @@ -67,17 +35,7 @@ export const useBoxStyleProps = (
}
}

const paddingClasses = [
...generateResponsiveUtilityClasses('p', padding),
...generateResponsiveUtilityClasses('pb', paddingBottom),
...generateResponsiveUtilityClasses('pl', paddingLeft),
...generateResponsiveUtilityClasses('pr', paddingRight),
...generateResponsiveUtilityClasses('pt', paddingTop),
...generateResponsiveUtilityClasses('px', paddingX),
...generateResponsiveUtilityClasses('py', paddingY),
];

const boxClasses = classNames(boxBackgroundColor, boxBorderColor, boxBorderRadius, boxBorderWidth, ...paddingClasses);
const boxClasses = classNames(boxBackgroundColor, boxBorderColor, boxBorderRadius, boxBorderWidth);

return {
classProps: boxClasses,
Expand Down
10 changes: 10 additions & 0 deletions packages/web-react/src/constants/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,13 @@ export const SpacingStyleProp = {
marginX: 'mx',
marginY: 'my',
} as const;

export const PaddingStyleProps = {
padding: 'p',
paddingBottom: 'pb',
paddingLeft: 'pl',
paddingRight: 'pr',
paddingTop: 'pt',
paddingX: 'px',
paddingY: 'py',
} as const;
42 changes: 42 additions & 0 deletions packages/web-react/src/hooks/__tests__/styleProps.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,46 @@ describe('styleProps', () => {
expect(result.current.styleProps).toEqual(expected);
});
});

it('should process style props with additional utilities', () => {
const mockProps = {
margin: 'space-100',
marginX: 'space-200',
marginY: 'space-400',
padding: 'space-500',
paddingX: 'space-600',
paddingY: 'space-700',
};
const additionalUtilities = {
padding: 'p',
paddingX: 'px',
paddingY: 'py',
};

const { result } = renderHook(() => useStyleProps(mockProps as StyleProps, additionalUtilities));

expect(result.current.styleProps).toEqual({
className: 'm-100 mx-200 my-400 p-500 px-600 py-700',
style: undefined,
});
});

it('should process style props with responsive additional utilities', () => {
const mockProps = {
margin: 'space-100',
marginX: 'space-200',
marginY: 'space-400',
padding: { mobile: 'space-500', tablet: 'space-600', desktop: 'space-700' },
};
const additionalUtilities = {
padding: 'p',
};

const { result } = renderHook(() => useStyleProps(mockProps as StyleProps, additionalUtilities));

expect(result.current.styleProps).toEqual({
className: 'm-100 mx-200 my-400 p-500 p-tablet-600 p-desktop-700',
style: undefined,
});
});
});
101 changes: 101 additions & 0 deletions packages/web-react/src/hooks/__tests__/useStyleUtilities.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,105 @@ describe('useStyleUtilities hook', () => {
expect(result.current.styleUtilities).toEqual(['m-100', 'mx-200', 'mx-tablet-auto', 'mx-desktop-300']);
expect(result.current.props).toEqual({});
});

it('should process style utilities correctly with responsive values', () => {
const mockProps = {
margin: { mobile: 'space-100', tablet: 'space-200', desktop: 'space-300' },
marginX: { mobile: 'space-200', tablet: 'space-200', desktop: 'space-300' },
};
const mockPrefix = 'test-prefix';

const { result } = renderHook(() => useStyleUtilities(mockProps as StyleProps, mockPrefix));

expect(result.current.styleUtilities).toEqual([
'test-prefix-m-100',
'test-prefix-m-tablet-200',
'test-prefix-m-desktop-300',
'test-prefix-mx-200',
'test-prefix-mx-tablet-200',
'test-prefix-mx-desktop-300',
]);
expect(result.current.props).toEqual({});
});

it('should process style utilities correctly with responsive values without prefix', () => {
const mockProps = {
margin: { mobile: 'space-100', tablet: 'space-200', desktop: 'space-300' },
marginX: { mobile: 'space-200', tablet: 'space-200', desktop: 'space-300' },
};

const { result } = renderHook(() => useStyleUtilities(mockProps as StyleProps));

expect(result.current.styleUtilities).toEqual([
'm-100',
'm-tablet-200',
'm-desktop-300',
'mx-200',
'mx-tablet-200',
'mx-desktop-300',
]);
expect(result.current.props).toEqual({});
});

it('should process style utilities with additional spacing props', () => {
const mockProps = {
margin: 'space-100',
marginX: 'space-200',
marginY: 'space-400',
padding: 'space-500',
paddingX: 'space-600',
paddingY: 'space-700',
};
const additionalSpacingProps = {
padding: 'p',
paddingX: 'px',
paddingY: 'py',
};

const { result } = renderHook(() =>
useStyleUtilities(mockProps as StyleProps, 'test-prefix', additionalSpacingProps),
);

expect(result.current.styleUtilities).toEqual([
'test-prefix-m-100',
'test-prefix-mx-200',
'test-prefix-my-400',
'test-prefix-p-500',
'test-prefix-px-600',
'test-prefix-py-700',
]);
});

it('should process style utilities with responsive additional spacing props', () => {
const mockProps = {
margin: 'space-100',
marginX: 'space-200',
marginY: 'space-400',
padding: { mobile: 'space-500', tablet: 'space-600', desktop: 'space-700' },
paddingX: { mobile: 'space-600', tablet: 'space-700', desktop: 'space-800' },
paddingY: { mobile: 'space-700', tablet: 'space-800', desktop: 'space-900' },
};
const additionalSpacingProps = {
padding: 'p',
paddingX: 'px',
paddingY: 'py',
};

const { result } = renderHook(() => useStyleUtilities(mockProps as StyleProps, '', additionalSpacingProps));

expect(result.current.styleUtilities).toEqual([
'm-100',
'mx-200',
'my-400',
'p-500',
'p-tablet-600',
'p-desktop-700',
'px-600',
'px-tablet-700',
'px-desktop-800',
'py-700',
'py-tablet-800',
'py-desktop-900',
]);
});
});
7 changes: 5 additions & 2 deletions packages/web-react/src/hooks/styleProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ export type StylePropsResult = {
props: HTMLAttributes<HTMLElement>;
};

export function useStyleProps<T extends StyleProps>(props: T): StylePropsResult {
export function useStyleProps<T extends StyleProps>(
props: T,
additionalUtilities?: Record<string, string>,
): StylePropsResult {
const classNamePrefix = useContext(ClassNamePrefixContext);
const { UNSAFE_className, UNSAFE_style, ...otherProps } = props;
const { styleUtilities, props: modifiedProps } = useStyleUtilities(otherProps, classNamePrefix);
const { styleUtilities, props: modifiedProps } = useStyleUtilities(otherProps, classNamePrefix, additionalUtilities);

const style: CSSProperties = { ...UNSAFE_style };

Expand Down
10 changes: 8 additions & 2 deletions packages/web-react/src/hooks/useStyleUtilities.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SpacingStyleProp } from '../constants';
import { SpacingStyleProp as DefaultSpacingStyleProp } from '../constants';
import {
BREAKPOINT_MOBILE,
BreakpointToken,
Expand Down Expand Up @@ -37,7 +37,13 @@ const processBreakpointProperties = (
return accumulatedBreakpointUtilities;
}, accumulatedUtilities);

export function useStyleUtilities(props: StyleProps, prefix: string | null | undefined = ''): StyleUtilitiesResult {
export function useStyleUtilities(
props: StyleProps,
prefix: string | null | undefined = '',
additionalSpacingProps: Record<string, string> = {},
): StyleUtilitiesResult {
const SpacingStyleProp = { ...DefaultSpacingStyleProp, ...additionalSpacingProps };

const propEntries = Object.entries(props);
const styleUtilities = propEntries.reduce((accumulatedUtilities: string[], [key, propValue]) => {
if (Object.keys(SpacingStyleProp).includes(key)) {
Expand Down

0 comments on commit 5d781e9

Please sign in to comment.