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

[WIP] update documentation #10

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 48 additions & 145 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,15 @@

> Unified CSSinJS theming solution for React

* `ThemeProvider` allows you to pass, update, merge and augment `theme` through context down react tree.
* `withTheme` allows you to receive theme and its updates in your components as a `theme` prop.
* `createTheming` allows you to integrate `theming` into your CSSinJS library with custom `channel` (if you need custom one).

See [Motivation](#motivation) for details.
This package provides users and library authors with the necessary tooling to implement theming in React apps. Theming in React provides something of a challenge because your deeply nested components need access to the theme (and its changes) in order to know which styles to apply to themselves. You could use React's Context API, but it's unstable and passing changes down the components tree is unreliable.

Enter `theming`. The library provides a `withTheme` HOC which makes sure your theme is always available (and up to date) to the component it wraps. It does not matter how far down the components tree it is, nor does it matter if you use `PureComponent` or other ways of update blocking in between, the theme will always be available and up to date.

## Table of Contents

* [Install](#install)
* [Usage](#usage)
* [Basic usage](#basic-usage)
* [Playground demo](#playground-demo)
* [Motivation](#motivation)
* [API](#api)
* [channel](#channel)
* [ThemeProvider](#themeprovider)
Expand All @@ -38,39 +34,45 @@ See [Motivation](#motivation) for details.
yarn add theming


## Usage
## Basic usage

To get the most basic use-case working, you need two things from this package:


In your components
* __`ThemeProvider` component__: pass your theme to this component and it will provide the theme to all sub-components that are wrapped with `withTheme`.
* __`withTheme` HOC__: wrap your components with this HOC and they will receive the `theme` prop that you provided to `ThemeProvider`.

### Example of a "themed" component

Note: this component i will use later to show what theme you will get

```jsx
import React from 'react';
import { withTheme } from 'theming';

const DemoBox = props => {
console.log(props.theme);
return (<div />);
const Button = props => {
// props.theme was injected by "withTheme"
// it affects the button's background color
const style = { backgroundColor: props.theme.buttonColor };
return (<button style={style}>Click me</button>);
}

export default withTheme(DemoBox);
export default withTheme(Button);
```

In your app
### Using it in your app

```jsx
import React from 'react';
import { ThemeProvider } from 'theming';
import DemoBox from './components/DemoBox'
import Button from './components/Button'

const theme = {
color: 'black',
background: 'white',
buttonColor: 'brown'
};

const App = () => (
<ThemeProvider theme={theme}>
<DemoBox /> {/* { color: 'black', background: 'white' } */}
<Button />
</ThemeProvider>
)

Expand All @@ -84,42 +86,11 @@ Be our guest, play with `theming` in this webpackbin:

![theming playground demo](https://user-images.githubusercontent.com/559321/27082371-ba194816-5044-11e7-8f06-6cbdbdefb602.gif)

## Motivation

These components are enabling seamless theming for your react applications. And as far as you dont want to pass theme object to each and every component. Thats why you want to use context. But as far context feature is _experimental API and it is likely to break in future releases of React_ you don't want to use it directly. Here `theming` comes to play.

> If you insist on using context despite these warnings, try to isolate your use of context to a small area and avoid using the context API directly when possible so that it's easier to upgrade when the API changes.
>
> If you insist on using context despite these warnings, try to isolate your use of context to a small area and avoid using the context API directly when possible so that it's easier to upgrade when the API changes.
> — [Context, React documentation](https://facebook.github.io/react/docs/context.html)

Regarding _isolation your use of context to a small area_ and _small areas__ in particular our very own react prophet Dan Abramov have a thing to say:

> Should I use React unstable “context” feature?
> <img src="https://pbs.twimg.com/media/CmeGPNcVYAAM7TR.jpg" alt="![context application areas]" height="300" />
> — [Dan Abramov @dan_abramov on Twitter](https://twitter.com/dan_abramov/status/749715530454622208?lang=en)

So you are fine to use context for theming. `theming` package provides everything you need to do that:
* `ThemeProvider` allows you to pass and update `theme` through context down react tree.
* `withTheme` allows you to receive theme and its updates in your components as a `theme` prop.
* `createTheming` allows you to integrate `theming` into your CSSinJS library with custom `channel` (if you need custom one).


## API

### channel

Theming package by default uses this string as a name of the field in context (hence `contextTypes` and `childContextTypes`). If you want to build your own components on top of theming, it might be a good idea to not rely on hard coded value, but instead import this value from the package.

```js
import { channel } from 'theming';

console.log(channel); '__THEMING__';
```

### ThemeProvider

React High-Order component, which passes theme object down the react tree by context.
React High-Order component, which provides the theme object to descendants down the React tree.

```jsx
import { ThemeProvider } from 'theming';
Expand All @@ -130,132 +101,64 @@ const theme = { /*…*/ };
</ThemeProvider>
```

#### props
`ThemeProvider` components can also be nested. In this case the theme objects will get merged.

##### props.theme

*Required*
Type: `Object`, `Function`

If its `Object` and its root `ThemeProvider` then its intact and being passed down the react tree.

```jsx
const theme = { themed: true };
const theme = { foo: 'foo', bar: 'bar' };
const subTheme = { bar: 'overwrite!', baz: 'baz' };

<ThemeProvider theme={theme}>
<DemoBox /> {/* { themed: true } */}
<ThemeProvider theme={subTheme}>
<App />
</ThemeProvider>
</ThemeProvider>
```

If its `Object` and its nested `ThemeProvider` then its being merged with theme from parent `ThemeProvider` and passed down to the react tree.
In the example above, App and its children will receive the following theme object: `{ foo: 'foo', bar: 'overwrite!', baz: 'addbaz }`

```jsx
const theme = { themed: true };
const patch = { merged: true };
#### props.theme
*Required*

Type: `PropTypes.object`, `PropTypes.function`

<ThemeProvider theme={theme}>
<ThemeProvider theme={patch}>
<DemoBox /> {/* { themed: true, merged: true } */}
</ThemeProvider>
</ThemeProvider>
```
This is the theme that the `ThemeProvider`'s children will receive.

If its `Function` and its nested `ThemeProvider` then its being applied to the theme from parent `ThemeProvider`. if result is an `Object` it will be passed down to the react tree, throws otherwise.
In case it's a function, the function will get called __WHEN?????__ and must return an object that contains the theme. If you do not return an object, `ThemeProvider` will throw an error.

For nested `ThemeProvider` instances that provide a function property as `theme`, the function will be called with the parent theme as first and only argument.

```jsx
const theme = { themed: true };
const augment = outerTheme =>
Object.assign({}, outerTheme, { augmented: true });
const augment = parentTheme =>
Object.assign({}, parentTheme, { augmented: true });

<ThemeProvider theme={theme}>
<ThemeProvider theme={augment}>
<DemoBox /> {/* { themed: true, augmented: true } */}
<App /> {/* { themed: true, augmented: true } */}
</ThemeProvider>
</ThemeProvider>
```

##### props.children

#### props.children
*Required*
Type: `PropTypes.element`

ThemeProvider uses [`React.Children.only`](https://facebook.github.io/react/docs/react-api.html#react.children.only) in render, which returns the only child in children. Throws otherwise.

### withTheme(component)

React High-Order component, which maps context to theme prop.

#### component

*Required*
Type: `PropTypes.element`

You need to have `ThemeProvider` with a theme somewhere upper the react tree, after that wrap your component in `withTheme` and your component will get theme as a prop. `withTheme` will handle initial theme object as well as theme updates.

PS. It doesnt break if you have `PureComponent` somewhere in between your ThemeProvider and withTheme (i have tests for that).

Usage with Component:

```jsx
import React from 'react';
import { withTheme } from 'theming';

const DemoBox = props => {
console.log(props.theme);
return (<div />);
}

export default withTheme(DemoBox);
```

In the app:

```jsx
import React from 'react';
import { ThemeProvider } from 'theming';
import DemoBox from './components/DemoBox'

const theme = {
color: 'black',
background: 'white',
};
`ThemeProvider` expects to receive a single child. It will throw an error if you pass more than one child.

const App = () => (
<ThemeProvider theme={theme}>
<DemoBox /> {/* { color: 'black', background: 'white' } */}
</ThemeProvider>
)
### withTheme

export default App;
```
__[TODO]__

### createTheming(customChannel)
### createTheming

Function to create `ThemeProvider` and `withTheme` with custom context channel.
__[TODO]__

#### customChannel

Type: `String`
Default: `__THEMING__`
Result: `Object { channel, withTheme, ThemeProvider }`

`withTheme` and `ThemeProvider` are the same as default ones, but with overwritten context channel.

`channel` is `customChannel` to track what is context channel.

```js
import { createTheming } from 'theming';

const theming = createTheming('__styled-components__');

const { channel, withTheme, ThemeProvider } = theming;
### channel

export default {
channel,
withTheme,
ThemeProvider,
};
```
__[TODO]__

## Credits

Expand Down