Skip to content

Commit

Permalink
feat(theming): add new getCheckeredBackground utility (#1793)
Browse files Browse the repository at this point in the history
  • Loading branch information
jzempel committed Apr 22, 2024
1 parent 2190e56 commit a745bde
Show file tree
Hide file tree
Showing 10 changed files with 169 additions and 77 deletions.
16 changes: 8 additions & 8 deletions packages/colorpickers/src/styled/ColorPicker/StyledAlphaRange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
*/

import styled, { DefaultTheme, ThemeProps } from 'styled-components';
import { DEFAULT_THEME } from '@zendeskgarden/react-theming';
import { DEFAULT_THEME, getCheckeredBackground } from '@zendeskgarden/react-theming';
import { getTrackHeight, getTrackMargin, StyledRange } from '../common/StyledRange';
import { checkeredBackground } from '../common/checkeredBackground';
import { IRGBColor } from '../../types';

const COMPONENT_ID = 'colorpickers.colorpicker_alpha';
Expand All @@ -19,14 +18,15 @@ const background = (props: IRGBColor & ThemeProps<DefaultTheme>) => {
const toColor = `rgb(${props.red}, ${props.green}, ${props.blue})`;
const positionY = getTrackMargin(props);
const height = getTrackHeight(props);
const gradientBackground = `linear-gradient(${direction}, ${fromColor}, ${toColor}) 0 ${positionY}px / 100% ${height}px no-repeat`;
const overlay = `linear-gradient(${direction}, ${fromColor}, ${toColor}) 0 ${positionY}px / 100% ${height}px no-repeat`;

return `${gradientBackground}, ${checkeredBackground(
props.theme,
height,
return getCheckeredBackground({
theme: props.theme,
size: height,
positionY,
'repeat-x'
)}`;
repeat: 'repeat-x',
overlay
});
};

export const StyledAlphaRange = styled(StyledRange as 'input').attrs<IRGBColor>(props => ({
Expand Down
16 changes: 11 additions & 5 deletions packages/colorpickers/src/styled/ColorPicker/StyledPreview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@

import styled, { DefaultTheme, ThemeProps } from 'styled-components';
import { rgba } from 'polished';
import { retrieveComponentStyles, DEFAULT_THEME } from '@zendeskgarden/react-theming';
import { checkeredBackground } from '../common/checkeredBackground';
import {
retrieveComponentStyles,
DEFAULT_THEME,
getCheckeredBackground
} from '@zendeskgarden/react-theming';
import { IRGBColor } from '../../types';

const COMPONENT_ID = 'colorpickers.colorpicker_preview_box';
Expand All @@ -19,11 +22,14 @@ interface IStyledColorPreviewProps extends IRGBColor {

const background = (props: IStyledColorPreviewProps & ThemeProps<DefaultTheme>) => {
const alpha = props.alpha ? props.alpha / 100 : 0;
const color = `rgba(${props.red}, ${props.green}, ${props.blue}, ${alpha})`;
let retVal = `linear-gradient(${color}, ${color})`;
let retVal = `rgba(${props.red}, ${props.green}, ${props.blue}, ${alpha})`;

if (!props.isOpaque) {
retVal = `${retVal}, ${checkeredBackground(props.theme, 13)}`;
retVal = getCheckeredBackground({
theme: props.theme,
size: 13,
overlay: retVal
});
}

return retVal;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@

import styled, { ThemeProps, DefaultTheme } from 'styled-components';
import { rgba } from 'polished';
import { retrieveComponentStyles, DEFAULT_THEME } from '@zendeskgarden/react-theming';
import { checkeredBackground } from '../common/checkeredBackground';
import {
retrieveComponentStyles,
DEFAULT_THEME,
getCheckeredBackground
} from '@zendeskgarden/react-theming';
import { IColorPickerDialogProps } from '../../types';

const COMPONENT_ID = 'colorpickers.colordialog_preview';
Expand All @@ -19,24 +22,28 @@ export interface IStyleButtonPreviewProps extends ThemeProps<DefaultTheme> {

const background = (props: IStyleButtonPreviewProps) => {
const { backgroundColor } = props;
let color;
let retVal;

if (typeof backgroundColor === 'string') {
color = backgroundColor;
retVal = backgroundColor;
} else if (backgroundColor === undefined) {
color = props.theme.palette.white;
retVal = props.theme.palette.white as string;
} else {
const { red, green, blue, alpha = 1 } = backgroundColor;

color = `rgba(${red}, ${green}, ${blue}, ${alpha ? alpha / 100 : 0})`;
retVal = `rgba(${red}, ${green}, ${blue}, ${alpha ? alpha / 100 : 0})`;
}

return `linear-gradient(${color}, ${color})`;
return retVal;
};

export const StyledButtonPreview = styled.span.attrs<IStyleButtonPreviewProps>(props => ({
style: {
background: `${background(props)}, ${checkeredBackground(props.theme, 8)}`
background: getCheckeredBackground({
theme: props.theme,
size: 8,
overlay: background(props)
})
},
'data-garden-id': COMPONENT_ID,
'data-garden-version': PACKAGE_VERSION,
Expand Down
32 changes: 0 additions & 32 deletions packages/colorpickers/src/styled/common/checkeredBackground.ts

This file was deleted.

30 changes: 7 additions & 23 deletions packages/theming/demo/stories/GetColorStory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,10 @@

import React from 'react';
import { StoryFn } from '@storybook/react';
import styled, { DefaultTheme, useTheme } from 'styled-components';
import { IGardenTheme, getColor } from '@zendeskgarden/react-theming';
import styled, { useTheme } from 'styled-components';
import { IGardenTheme, getCheckeredBackground, getColor } from '@zendeskgarden/react-theming';
import { Tag } from '@zendeskgarden/react-tags';

const toBackground = (theme: DefaultTheme, backgroundColor: string) => {
const color = getColor({ hue: 'neutralHue', shade: 300, theme });
const size = 26;
const dimensions = `${size}px ${size}px`;
const positionX1 = theme.rtl ? '100%' : '0';
const positionX2 = theme.rtl ? `calc(100% - ${size / 2}px)` : `${size / 2}px`;
const position1 = `${positionX1} 0`;
const position2 = `${positionX2} ${size / 2}px`;
const position3 = `${positionX2} 0`;
const position4 = `${positionX1} ${size / -2}px`;

return `
linear-gradient(${backgroundColor}, ${backgroundColor}),
linear-gradient(45deg, ${color} 25%, transparent 25%) ${position1} / ${dimensions} repeat,
linear-gradient(45deg, transparent 75%, ${color} 75%) ${position2} / ${dimensions} repeat,
linear-gradient(135deg, ${color} 25%, transparent 25%) ${position3} / ${dimensions} repeat,
linear-gradient(135deg, transparent 75%, ${color} 75%) ${position4} / ${dimensions} repeat
`;
};

const StyledDiv = styled.div<{ background: string }>`
display: flex;
align-items: center;
Expand Down Expand Up @@ -66,7 +46,11 @@ const Color = ({ dark, hue, light, offset, shade, theme, transparency, variable
variable
});

background = toBackground(theme, backgroundColor);
background = getCheckeredBackground({
theme,
size: 26,
overlay: backgroundColor
});
tag = (
<Tag hue={getColor({ theme, variable: 'background.default' })} size="large">
{backgroundColor}
Expand Down
2 changes: 2 additions & 0 deletions packages/theming/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export { default as PALETTE } from './elements/palette';
export { default as PALETTE_V8 } from './elements/palette/v8';
export { default as retrieveComponentStyles } from './utils/retrieveComponentStyles';
export { getArrowPosition } from './utils/getArrowPosition';
export { getCheckeredBackground } from './utils/getCheckeredBackground';
export { getColor } from './utils/getColor';
export { getColorV8 } from './utils/getColorV8';
export { getFloatingPlacements } from './utils/getFloatingPlacements';
Expand All @@ -33,6 +34,7 @@ export {
type IGardenTheme,
type IThemeProviderProps,
type ArrowPosition,
type CheckeredBackgroundParameters,
type ColorParameters,
type FocusBoxShadowParameters,
type FocusStylesParameters,
Expand Down
8 changes: 8 additions & 0 deletions packages/theming/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ export const PLACEMENT = [

export type Placement = (typeof PLACEMENT)[number];

export type CheckeredBackgroundParameters = {
overlay?: string;
positionY?: number;
repeat?: 'repeat' | 'repeat-x';
size: number;
theme: IGardenTheme;
};

export type ColorParameters = {
dark?: {
hue?: string;
Expand Down
65 changes: 65 additions & 0 deletions packages/theming/src/utils/getCheckeredBackground.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* Copyright Zendesk, Inc.
*
* Use of this source code is governed under the Apache License, Version 2.0
* found at http://www.apache.org/licenses/LICENSE-2.0.
*/

import { getCheckeredBackground } from './getCheckeredBackground';
import DEFAULT_THEME from '../elements/theme';
import PALETTE from '../elements/palette';

describe('getCheckeredBackground', () => {
it('returns a valid background', () => {
const size = 10;
const result = getCheckeredBackground({ theme: DEFAULT_THEME, size });
const expected1 = `0 0px / ${size}px ${size}px repeat`;
const expected2 = `${size / 2}px ${size / 2}px / ${size}px ${size}px repeat`;

expect(result).toContain(expected1);
expect(result).toContain(expected2);
});

it('handles color overlay as expected', () => {
const expected = `${PALETTE.blue[700]}80`;
const result = getCheckeredBackground({ theme: DEFAULT_THEME, size: 10, overlay: expected });

expect(result).toContain(`linear-gradient(${expected}, ${expected})`);
});

it('handles linear-gradient overlay as expected', () => {
const expected = `linear-gradient(to right, ${PALETTE.white}, ${PALETTE.black})`;
const result = getCheckeredBackground({ theme: DEFAULT_THEME, size: 10, overlay: expected });

expect(result).toContain(`${expected},`);
});

it('handles vertical position as expected', () => {
const size = 10;
const positionY = DEFAULT_THEME.space.base;
const result = getCheckeredBackground({ theme: DEFAULT_THEME, size, positionY });
const expected1 = `0 ${positionY}px`;
const expected2 = `${size / 2}px ${size / 2 + positionY}px`;

expect(result).toContain(expected1);
expect(result).toContain(expected2);
});

it('handles repeat as expected', () => {
const repeat = 'repeat-x';
const result = getCheckeredBackground({ theme: DEFAULT_THEME, size: 10, repeat });

expect(result).toContain(repeat);
});

it('handles RTL as expected', () => {
const theme = { ...DEFAULT_THEME, rtl: true };
const size = 10;
const result = getCheckeredBackground({ theme, size });
const expected1 = `100% 0px / ${size}px ${size}px repeat`;
const expected2 = `calc(100% - ${size / 2}px) ${size / 2}px / ${size}px ${size}px repeat`;

expect(result).toContain(expected1);
expect(result).toContain(expected2);
});
});
52 changes: 52 additions & 0 deletions packages/theming/src/utils/getCheckeredBackground.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Copyright Zendesk, Inc.
*
* Use of this source code is governed under the Apache License, Version 2.0
* found at http://www.apache.org/licenses/LICENSE-2.0.
*/

import { CheckeredBackgroundParameters } from '../types';
import { getColor } from './getColor';

/**
* Get a checkered background image.
*
* @param {Object} options.theme Provides information for pattern color and LTR/RTL layout
* @param {number} options.size The pixel size of the checkered pattern
* @param {string} [options.overlay] Optional
* [color](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) with transparency or
* [`linear-gradient()`](https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient)
* overlay to apply on top of the checkered pattern
* @param {number} [options.positionY=0] Optional vertical position for starting the pattern (default `0`)
* @param {string} [options.repeat='repeat'] Optional repeat value for the pattern; either `'repeat'` or `'repeat-x'` (default `'repeat'`)
*/
export const getCheckeredBackground = ({
theme,
size,
overlay,
positionY = 0,
repeat = 'repeat'
}: CheckeredBackgroundParameters) => {
const color = getColor({ theme, variable: 'border.default' });
const dimensions = `${size}px ${size}px`;
const positionX1 = theme.rtl ? '100%' : '0';
const positionX2 = theme.rtl ? `calc(100% - ${size / 2}px)` : `${size / 2}px`;
const position1 = `${positionX1} ${positionY}px`;
const position2 = `${positionX2} ${size / 2 + positionY}px`;
const position3 = `${positionX2} ${positionY}px`;
const position4 = `${positionX1} ${size / -2 + positionY}px`;
let retVal = `
linear-gradient(45deg, ${color} 25%, transparent 25%) ${position1} / ${dimensions} ${repeat},
linear-gradient(45deg, transparent 75%, ${color} 75%) ${position2} / ${dimensions} ${repeat},
linear-gradient(135deg, ${color} 25%, transparent 25%) ${position3} / ${dimensions} ${repeat},
linear-gradient(135deg, transparent 75%, ${color} 75%) ${position4} / ${dimensions} ${repeat}
`;

if (overlay) {
retVal = overlay.startsWith('linear-gradient')
? `${overlay}, ${retVal}`
: `linear-gradient(${overlay}, ${overlay}), ${retVal}`;
}

return retVal;
};
2 changes: 1 addition & 1 deletion packages/theming/src/utils/getColor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ const toProperty = (object: object, path: string) => {
* `shade`, `offset`, and `transparency` are used as fallbacks to determine the
* color.
*
* @param {Object} [options.theme] Provides values used to resolve the desired color
* @param {Object} options.theme Provides values used to resolve the desired color
* @param {string} [options.variable] A variable key (i.e. `'background.default'`) used to resolve a color value for the theme color base
* @param {Object} [options.dark] An object with `hue`, `shade`, `offset`, and `transparency` values to be used in dark mode
* @param {Object} [options.light] An object with `hue`, `shade`, `offset`, and `transparency` values to be used in light mode
Expand Down

0 comments on commit a745bde

Please sign in to comment.