Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(theming): add componentStyles utility #1986

Merged
merged 14 commits into from
Dec 12, 2024
7 changes: 3 additions & 4 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ ensuring the details live up to expectations.
for an ideal example):
- "Base" properties: display, position, flex, transition, direction, etc
(anything NOT related to size or color)
- `${sizeStyles(props)}`: a function that contains all properties related
- `${sizeStyles}`: a function that contains all properties related
to component sizing (usually based on calculated relationships), i.e.
margin, padding, width, height, line-height, font-size – all grouped
ordering (including pseudos, children, etc) applies within the
Expand All @@ -52,7 +52,7 @@ ensuring the details live up to expectations.
- `:hover`
- `:focus`
- `:active`
- `${colorStyles(props)}`: a function that contains all properties related
- `${colorStyles}`: a function that contains all properties related
to component color, i.e. border-color, background-color, color, box-shadow
– including any color modifications based on pseudo-class states, in the
order shown above – all grouped ordering (including psuedos, children,
Expand All @@ -63,8 +63,7 @@ ensuring the details live up to expectations.
property groupings – note that children styled components should contain
all their CSS properties, when possible
- The last declaration in any view component is
`${retrieveComponentStyles(COMPONENT_ID, props)}` which allows an
implementer to leverage the
`${componentStyles}` which allows an implementer to leverage the
[`theme`](https://zendeskgarden.github.io/react-components/theming/)
"components" object to override specific component styles.
- The view component `defaultProps` must contain `theme: DEFAULT_THEME` for
Expand Down
1 change: 1 addition & 0 deletions packages/theming/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export { ThemeProvider } from './elements/ThemeProvider';
export { default as DEFAULT_THEME } from './elements/theme';
export { default as PALETTE } from './elements/palette';
export { default as retrieveComponentStyles } from './utils/retrieveComponentStyles';
export { componentStyles } from './utils/componentStyles';
export { getArrowPosition } from './utils/getArrowPosition';
export { getCheckeredBackground } from './utils/getCheckeredBackground';
export { getColor } from './utils/getColor';
Expand Down
42 changes: 42 additions & 0 deletions packages/theming/src/utils/componentStyles.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* 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 { componentStyles } from './componentStyles';

describe('componentStyles', () => {
const VALUE = 'content: "test";';

it('returns component styles from the theme as expected', () => {
const props = { 'data-garden-id': 'test', theme: { components: { test: VALUE } } } as any;
const result = componentStyles(props);

expect(result).toBe(VALUE);
});

it('returns undefined if no component styles are found', () => {
const props = { 'data-garden-id': 'test', theme: {} } as any;
const result = componentStyles(props);

expect(result).toBeUndefined();
});

it('handles component styles provided as a function', () => {
const fn = jest.fn().mockReturnValue(VALUE);
const props = { 'data-garden-id': 'test', theme: { components: { test: fn } } } as any;
const result = componentStyles(props);

expect(result).toBe(VALUE);
});

it('accepts a custom component ID', () => {
const componentId = 'custom';
const props = { theme: { components: { [componentId]: VALUE } } } as any;
const result = componentStyles({ ...props, componentId });

expect(result).toBe(VALUE);
});
});
35 changes: 35 additions & 0 deletions packages/theming/src/utils/componentStyles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* 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 { DataAttributes, DefaultTheme } from 'styled-components';

/**
* CSS for component customizations based on `theme.components[componentId]`.
*
* @param {Object} props.theme Provides `components` object use to resolve the given component ID
* @param {String} [props.componentId] Specifies the lookup id for * `theme.components` styles.
* The ID will be inferred from the `'data-garden-id'` attribute if not provided.
*
* @returns component CSS styles
*/
export const componentStyles = (props: { theme: DefaultTheme; componentId?: string }) => {
let retVal: string | undefined;
const components = props.theme.components;
const componentId = props.componentId || (props as unknown as DataAttributes)['data-garden-id'];

if (components && componentId) {
retVal = components[componentId];

if (typeof retVal === 'function') {
const fn = retVal as (p: { theme: DefaultTheme } & unknown) => string;

retVal = fn(props);
}
}

return retVal;
};
8 changes: 4 additions & 4 deletions packages/theming/src/utils/retrieveComponentStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
* found at http://www.apache.org/licenses/LICENSE-2.0.
*/

import { ThemeProps, DefaultTheme } from 'styled-components';
import { DefaultTheme } from 'styled-components';

/** @component */
/** @deprecated Use `componentStyles` instead. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is /** @component */ needed for the documentation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nah, just leftover from ancient Styleguidist notation

export default function retrieveComponentStyles(
componentId: string,
props: Partial<ThemeProps<Partial<DefaultTheme>>>
props: { theme?: Partial<DefaultTheme> }
) {
const components = props.theme && props.theme.components;
const components = props.theme?.components;

if (!components) {
return undefined;
Expand Down
Loading