Skip to content

Latest commit

 

History

History
217 lines (161 loc) · 5.07 KB

theme.md

File metadata and controls

217 lines (161 loc) · 5.07 KB

Theme

Allows to define theme structure for feature or component. It is used for classNames or style calculating. Support custom themes provided by global CSS or inline styles and other libraries:

What is theme?

Simple styling manager for the React components.

Theme structure is defined at propTypes and default values defined at defaultProps. So theme is the component prop.

<Component theme={theme} />

Why theme is important for features?

A lot of features usually provides new UI elements for component. So - they should/may be customized.

Forgekit theme api

Theme structure should be defined at the propTypes.

Default theme values should be defined at defaultProps.

All defined themes are merged in the ForgedComponent. Each feature or component will receive only defined theme structure.

import { ThemeProp } from 'forgekit';

Feature1.propTypes = {
  theme: ThemeProp({
    foo: PropTypes.string,
    bar: PropTypes.string
  })
};

Feature2.propTypes = {
  theme: ThemeProp({
    baz: PropTypes.string
  })
};

const ForgedComponent = forge(Feature1, Feature2)(Component);

expect(ForgedComponent.propTypes.theme).toEqual({
  ...Feature1.propTypes.theme,
  ...Feature2.propTypes.theme,
});

What is the ThemeProp?

ThemeProp is same as PropTypes.shape but with attribute that contains all defined keys. Forgekit needs it for theme merging.

PropTypes.shape cannot be used directly because it is a bound function and there are no access to defined shape attributes.

const ThemeProp = theme => {
  const themeShape = PropTypes.shape(theme);
  themeShape.themeKeys = Object.keys(theme);

  return themeShape;
}

I have created React feature request to fill name attribute for all propTypes. If it is approved - ThemeProp will be removed.

Theme className example

For better experience suggest to use classnames library.

  • Define component theme structure
import { ThemeProp } from 'forgekit';

import classnames from 'classnames';
import buttonStyles from './style.css';

const Button = ({theme, children, className, ...props}) => {
  const classList = classnames(className, theme.base, theme.design);
  return <button className={classList} {...props}>{children}<button/>
};

Button.propTypes = {
  theme: ThemeProp({
    // "base" determine default button rules such as cursor, white-space, overflow, align etc.
    base: PropTypes.string,
    // "design" determine how button looks
    design: PropTypes.string,
  }),
};

Button.defaultProps = {
  theme: {
    base: buttonStyles.base,
  },
};
  • Define feature theme structure
import styles from './alert.css';

const Alert = ({alert, className, ...props}) => {
  return {
    ...props,
    className: classnames(className, {
      theme.alert: alert
    });
  }
};

Alert.propTypes = {
  theme: TemeProp({
    alert: PropTypes.string
  })
};

Alert.defaultprops = {
  theme: {
    alert: syles.alert
  }
};
  • Override theme values while forging
import materialButtonStyles from './material-button.css';

const MaterialButton = fogrkit(Alert)(Button, 'MaterialButton', {
  theme: {
    // could override any default theme
    design: materialButtonStyles.materialButton,
    alert: materialButtonStyles.materialAlert
  }
});
  • Or Forge and use theme prop
import materialButtonStyles from './material-button.css';

const MeterialComponent = fogrkit(Alert)(Button);

const theme = {
  design: materialButtonStyles.materialButton,
  alert: materialButtonStyles.materialAlert
};

<MaterialButton theme={theme}>Hello</MaterialButton>

Theme styles example

It is same as example with classNames expect:

  • Object.assign or object spread instead of classnames
  • Define props as PropTypes.object or even PropTypes.shape({...}) instead of PropTypes.string
import { ThemeProp } from 'forgekit';

import classnames from 'classnames';

const buttonStyles = {
  base: {
    position: 'relative',
    overflow: 'hidden',
    // ...
  }
};

const Button = ({theme, children, style, ...props}) => {
  const completeStyle = {
    ...style,
    ...theme.base,
    ...theme.design
  };

  return <button style={completeStyle} {...props}>{children}<button/>
};

Button.propTypes = {
  theme: ThemeProp({
    // "base" determine default button rules such as cursor, white-space, overflow, align etc.
    base: PropTypes.object,
    // "design" determine how button looks
    design: PropTypes.object,
  }),
};

Button.defaultProps = {
  theme: {
    base: buttonStyles.base,
  },
};