Skip to content
This repository has been archived by the owner on Apr 25, 2021. It is now read-only.

Commit

Permalink
Merge pull request #75 from AlecAivazis/feat/stylesheet-props
Browse files Browse the repository at this point in the history
stylesheet HoC can accept a function of the component props
  • Loading branch information
AlecAivazis authored Apr 24, 2017
2 parents 962cde4 + b6660bb commit 1773b85
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 63 deletions.
27 changes: 16 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

A redux reducer for managing the responsive state of your application.

# Example
## Example

```js
// MyComponent.js
Expand Down Expand Up @@ -46,7 +46,7 @@ class MyComponent extends React.Component {
}
```

# Why Use a Flux Store for Responsive Behavior?
## Why Use a Flux Store for Responsive Behavior?

redux-responsive **does not require that you use React as your view library**. However, since that is what is commonly used alongside redux, this documentation employs common React patterns.

Expand All @@ -55,7 +55,7 @@ There are many solutions for cleanly handling responsive designs in React applic
Using a specialized store not only reduces the overall noise in a component, but also guarantees that only a single event listener is listening for resize.


# Setup
## Setup

First, add the reducer to the root of your reducer tree (you can name it whatever you want).

Expand Down Expand Up @@ -178,7 +178,8 @@ export default combineReducers({



## The Infinity Media Type
### The Infinity Media Type

When the browser is wider than the largest breakpoint, it's `mediaType` value is `infinity`. In order to
change this value, add the `infinity` field to the object pass as a second argument to `createResponsiveStateReducer`:

Expand All @@ -199,6 +200,7 @@ export default combineReducers({


## Adding custom/computed fields to the responsive state

In some cases, you may want to add computed fields to the responsive state. For example,
an application may frequently need to know when the browser is `greaterThanOrEqual` to
a particular breakpoint. In order to support this, `redux-responsive` lets you pass a
Expand Down Expand Up @@ -227,6 +229,7 @@ export default combineReducers({
```

### Tracking window attributes

In some cases, you may want to have a `window` attributes tracked in your responsive state (for example, `width`).
To accomplish this, the first step is to add the custom field as described above.

Expand Down Expand Up @@ -263,7 +266,7 @@ window.addEventListener('resize', () => store.dispatch(calculateResponsiveState(
```


# Server-side Rendering
## Server-side Rendering

Isomorphic applications must make sure that the sever-rendered markup matches the
DOM rendered by the client. Setting the `calculateInitialState` option in the
Expand Down Expand Up @@ -310,7 +313,7 @@ ReactDOM.render(
store.dispatch(calculateResponsiveState(window))
```

## Setting the initial media type
### Setting the initial media type

If you know the initial media type for your application (by doing something like looking at
the user-agent) you can set the initial media type with the `initialMediaType` key to the
Expand All @@ -320,7 +323,7 @@ reducer factory:
const reducer = createResponsiveStateReducer(null, {initialMediaType: 'small'})
```

# Higher-Order Components
## Higher-Order Components

When building responsive applications in react, it's common to
implement styles for each breakpoint and then apply them like so:
Expand All @@ -345,27 +348,29 @@ const styles = {
```

However this becomes very repetitive rather quickly. To help, redux-responsive
provides a higher-order component for managing these styles. The follow is
provides a higher-order component for managing these styles. The `StyleSheet`
higher-order component takes a function of two arguments, the current state of the
responsive reducer, and any props passed to the component. The follow is
equivalent to the logic above:

```jsx
import {StyleSheet} from 'redux-responsive/react'

const stylesheet = {
const stylesheet = (browser, props) => ({
element: {
color: 'blue',
_lessThan_medium: {
color: 'black',
}
}
}
})

const component = StyleSheet(stylesheet)(({styles}) => (
<div style={styles.element} />
))
```


# Versioning
## Versioning

[Semver](http://semver.org/) is followed as closely as possible. For updates and migration instructions, see the [changelog](https://github.com/AlecAivazis/redux-responsive/wiki/Changelog).
30 changes: 14 additions & 16 deletions src/react/components/stylesheet.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,27 +113,25 @@ export const transformStyle = browser => style => {

// this function calculates the current stylesheet based on the responsive
// state of the reducer
export const mapStateToPropsFactory = (stylesheet, {reducerName}) => state => (
// the stylesheet only differs by values of
{styles: mapValues(stylesheet, transformStyle(state[reducerName]))}
)
export const mapStateToPropsFactory = (stylesheet, {reducerName} = defaultOptions) => (state, props) => {
// find the relevant state in the reducer
const browser = state[reducerName]

// if we are passed a functional stylesheet, hand it the component props, otherwise just use the object
const sheet = typeof stylesheet === 'function' ? stylesheet(browser, props) : stylesheet

// the stylesheet only differs by values of
return {styles: mapValues(sheet, transformStyle(browser))}
}

// the default options
const defaultOptions = {
reducerName: 'browser',
}



// export a higher order component
export default (stylesheet, opts) => (component) => {
// if we are passed a functional stylesheet, hand it the component props, otherwise just use the object
// const sheet = typeof stylesheet === 'function' ? stylesheet(props) : stylesheet

return (
require('react-redux').connect( // eslint-disable-line no-undef
mapStateToPropsFactory(stylesheet, {...defaultOptions, ...opts})
)(component)
)
}
export default (stylesheet, opts) => (component) => (
require('react-redux').connect( // eslint-disable-line no-undef
mapStateToPropsFactory(stylesheet, {...defaultOptions, ...opts})
)(component)
)
36 changes: 0 additions & 36 deletions src/react/components/stylesheet.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,40 +103,4 @@ describe('ReactStyleSheet', function () {
// make sure the stylesheet is what we expect
expect(computedStyle['border']).toBe(lessThanValue)
})


it.skip("handles functional stylesheets", function() {
// the mocked browser state
const browser = {
greaterThan: {
medium: true,
large: false,
},
lessThan: {
medium: false,
large: true,
},
mediaType: 'large',
breakpoints: ['medium', 'large']
}
// the stylesheet
const baseValue = 'black'
const greaterThanValue = 'blue'
const lessThanValue = 'green'

const stylesheet = () => ({
'border': baseValue,
'_greaterThan_medium': {
'border': greaterThanValue,
},
'_lessThan_large': {
'border': lessThanValue,
}
})
// the tranformer takes the browser state and returns a function that
// takes the responsive stylesheet and returns the final one
const computedStyle = transformStyle(browser)(stylesheet)
// make sure the stylesheet is what we expect
expect(computedStyle['border']).toBe(lessThanValue)
})
})

0 comments on commit 1773b85

Please sign in to comment.