Skip to content

Commit

Permalink
Styled API (#1094)
Browse files Browse the repository at this point in the history
* wip styled

* more tests, lint

* add styled system tests

* switch back to style-system v3, until v4 changes the format back

* switch back to style-system v3, until v4 changes the format back

* switch back to style-system v3, until v4 changes the format back, better types

* upgrade flow and react

* upgrade theming

* flow types

* migrate to hooks

* Fix tests

* Update size snapshot

* more tests

* accept multiple static and dynamic style declarations

* implement shouldForwardProp

* merge the styles arguments passed to styled

* add test for empty values returned from function rules

* optimize perf, better types

* support label

* Update changelog.md

* skip the tests for now on the ci
  • Loading branch information
kof committed Jun 12, 2019
1 parent ffd64fa commit 38267b1
Show file tree
Hide file tree
Showing 16 changed files with 763 additions and 57 deletions.
4 changes: 3 additions & 1 deletion .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@

[options]
include_warnings=true
esproposal.export_star_as=enable
esproposal.export_star_as=enable
suppress_comment= \\(.\\|\n\\)*\\$FlowFixMe
suppress_comment= \\(.\\|\n\\)*\\$FlowIgnore
6 changes: 6 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
## Next

### Features

- [react-jss] New experimental styled API (undocumented intentionally)([#1094](https://github.com/cssinjs/jss/pull/1094))

### Bug fixes

- [jss] After attempting to insert an invalid rule, JSS is now able to insert a valid one ([#1123](https://github.com/cssinjs/jss/pull/1123))
- [react-jss] Fix TS type optional `theming` property ([#1121](https://github.com/cssinjs/jss/pull/1121))
- [react-jss] Export useTheme in TypeScript declaration ([#1124](https://github.com/cssinjs/jss/pull/1124))

## 10.0.0-alpha.17 (2019-6-7)

Expand Down
2 changes: 2 additions & 0 deletions flow-typed/mocha.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare function describe(string, Function): void
declare function it(string, Function): void
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"eslint-config-jss": "^5.0.1",
"eslint-config-prettier": "^2.9.0",
"expect.js": "^0.3.1",
"flow-bin": "^0.94.0",
"flow-bin": "^0.98.0",
"json-loader": "^0.5.4",
"karma": "^1.3.0",
"karma-benchmark": "^0.6.0",
Expand Down
30 changes: 15 additions & 15 deletions packages/react-jss/.size-snapshot.json
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
{
"dist/react-jss.js": {
"bundled": 128515,
"minified": 44371,
"gzipped": 13873
"bundled": 163005,
"minified": 56827,
"gzipped": 18448
},
"dist/react-jss.min.js": {
"bundled": 96128,
"minified": 34478,
"gzipped": 11089
"bundled": 106313,
"minified": 40197,
"gzipped": 13484
},
"dist/react-jss.cjs.js": {
"bundled": 20600,
"minified": 9694,
"gzipped": 3131
"bundled": 23951,
"minified": 10720,
"gzipped": 3514
},
"dist/react-jss.esm.js": {
"bundled": 19744,
"minified": 8949,
"gzipped": 3011,
"bundled": 23029,
"minified": 9919,
"gzipped": 3388,
"treeshaked": {
"rollup": {
"code": 1899,
"import_statements": 491
"code": 1930,
"import_statements": 522
},
"webpack": {
"code": 3363
"code": 3428
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion packages/react-jss/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
},
"dependencies": {
"@babel/runtime": "^7.3.1",
"@emotion/is-prop-valid": "^0.7.3",
"hoist-non-react-statics": "^3.2.0",
"is-in-browser": "^1.1.3",
"jss": "10.0.0-alpha.17",
Expand All @@ -52,6 +53,7 @@
"tiny-warning": "^1.0.2"
},
"devDependencies": {
"@types/react": "^16.7.20"
"@types/react": "^16.8.16",
"styled-system": "4.2.0-0"
}
}
3 changes: 2 additions & 1 deletion packages/react-jss/src/createUseStyles.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ const createUseStyles = <Theme: {}>(styles: Styles<Theme>, options?: HookOptions

useEffectOrLayoutEffect(
() => {
if (state.sheet && state.dynamicRules) {
// We only need to update the rules on a subsequent update and not in the first mount
if (state.sheet && state.dynamicRules && !isFirstMount.current) {
updateDynamicRules(data, state.sheet, state.dynamicRules)
}
},
Expand Down
12 changes: 3 additions & 9 deletions packages/react-jss/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,9 @@ import {
SheetsRegistry,
Styles,
StyleSheetFactoryOptions,
CreateGenerateIdOptions,
CreateGenerateIdOptions
} from 'jss'
import {
createTheming,
useTheme,
withTheme,
ThemeProvider,
Theming,
} from 'theming'
import {createTheming, useTheme, withTheme, ThemeProvider, Theming} from 'theming'

declare const jss: Jss

Expand Down Expand Up @@ -74,7 +68,7 @@ export {
withTheme,
createTheming,
useTheme,
JssContext,
JssContext
}

export default withStyles
1 change: 1 addition & 0 deletions packages/react-jss/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export {default as JssProvider} from './JssProvider'
export {default as jss} from './jss'
export {SheetsRegistry, createGenerateId} from 'jss'
export {default as JssContext} from './JssContext'
export {default as styled} from './styled'
export {withStyles}

// Kept for backwards compatibility.
Expand Down
111 changes: 111 additions & 0 deletions packages/react-jss/src/styled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// @flow
/* eslint-disable react/prop-types, react/require-default-props */

import React from 'react'
import isPropValid from '@emotion/is-prop-valid'
import type {StatelessFunctionalComponent, ComponentType} from 'react'
import {ThemeContext as DefaultThemeContext} from 'theming'

import createUseStyles from './createUseStyles'
import type {HookOptions, StaticStyle, DynamicStyle} from './types'

type StyledProps = {
className?: string,
as?: string
}

type StyleArg<Theme> = StaticStyle | DynamicStyle<Theme> | null | void | ''

const parseStyles = <Theme>(args: {[string]: StyleArg<Theme>}) => {
const dynamicStyles = []
let staticStyle
let label = 'css'

// Not using ...rest to optimize perf.
for (const key in args) {
const style = args[key]
if (!style) continue
if (typeof style === 'function') {
dynamicStyles.push(style)
} else {
if (!staticStyle) staticStyle = {}
Object.assign(staticStyle, style)
if ('label' in staticStyle) {
// Label could potentially be defined in each style object,
// so we take the first one and ignore every subsequent one.
if (label === 'css') label = staticStyle.label
// Label should not leak to the core.
delete staticStyle.label
}
}
}

const styles = {}

if (staticStyle) styles[label] = staticStyle

// When there is only one function rule, we don't need to wrap it.
if (dynamicStyles.length === 1) {
styles.cssd = dynamicStyles[0]
}

// We create a new function rule which will call all other function rules
// and merge the styles they return.
if (dynamicStyles.length > 1) {
styles.cssd = props => {
const merged = {}
for (let i = 0; i < dynamicStyles.length; i++) {
const dynamicStyle = dynamicStyles[i](props)
if (dynamicStyle) Object.assign(merged, dynamicStyle)
}
return merged
}
}

return {styles, label}
}

type StyledOptions<Theme> = HookOptions<Theme> & {
shouldForwardProp?: string => boolean
}

export default <Theme: {}>(
type: string | StatelessFunctionalComponent<StyledProps> | ComponentType<StyledProps>,
options?: StyledOptions<Theme> = {}
) => {
const {theming, shouldForwardProp} = options
const isTagName = typeof type === 'string'
const ThemeContext = theming ? theming.context : DefaultThemeContext

return function StyledFactory(/* :: ...args: StyleArg<Theme>[] */): StatelessFunctionalComponent<
StyledProps
> {
// eslint-disable-next-line prefer-rest-params
const {styles, label} = parseStyles(arguments)

const useStyles = createUseStyles(styles, label ? {...options, name: label} : options)

const Styled = (props: StyledProps) => {
const {as, className} = props
// $FlowFixMe theming ThemeContext types need to be fixed.
const theme = React.useContext(ThemeContext)
const classes = useStyles({...props, theme})
const childProps: StyledProps = ({}: any)

for (const prop in props) {
// We don't want to pass non-dom props to the DOM,
// but we still want to forward them to a users component.
if (isTagName && !isPropValid(prop)) continue
if (shouldForwardProp && shouldForwardProp(prop) === false) continue
childProps[prop] = props[prop]
}
// $FlowIgnore we don't care label might not exist in classes.
const classNames = `${classes[label] || classes.css || ''} ${classes.cssd || ''}`.trim()
childProps.className = className ? `${className} ${classNames}` : classNames

return React.createElement(as || type, childProps)
}

return Styled
}
}

0 comments on commit 38267b1

Please sign in to comment.