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

Emotion Native #759

Merged
merged 37 commits into from
Jul 28, 2018
Merged

Emotion Native #759

merged 37 commits into from
Jul 28, 2018

Conversation

nitin42
Copy link
Contributor

@nitin42 nitin42 commented Jul 9, 2018

What: Styling Native components using emotion and a new primitives-core package for shared utils.

Why:

How: This PR adds support for styling React Native components using emotion, adds a new package primitives-core for primitive shared utils and also refactors the primitives package.

Checklist:

  • ✅ Documentation
  • ✅ Tests
  • ✅ Code complete

Related #755

@nitin42 nitin42 changed the title Init commit Emotion Native Jul 9, 2018
@nitin42
Copy link
Contributor Author

nitin42 commented Jul 9, 2018

Almost done! Just need some work on flow types.

@nitin42
Copy link
Contributor Author

nitin42 commented Jul 9, 2018

I don't know why there are some flow types error in other packages (I haven't touch any). Plus, typescript tests seems to be broken.

Copy link
Member

@emmatown emmatown left a comment

Choose a reason for hiding this comment

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

This is looking great!!

package.json Outdated
"version": "7.0.13",
"scripts": {
"build:watch": "node scripts/build/watch",
"build": "node scripts/build",
Copy link
Member

@emmatown emmatown Jul 9, 2018

Choose a reason for hiding this comment

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

Would you be able to revert these formatting changes in the package.jsons?

@@ -0,0 +1,26 @@
{
"name": "primitives-core",
Copy link
Member

Choose a reason for hiding this comment

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

This should be @emotion/primitives-core and it should be imported as @emotion/primitives-core

"babel-plugin-emotion": "^9.2.5",
"css-to-react-native": "^2.2.0"
"primitives-core": "1.0.0-beta"
Copy link
Member

Choose a reason for hiding this comment

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

@emotion/primitives-core

@@ -1,5 +1,5 @@
// @flow
import { css } from '@emotion/primitives'
import { css } from '../src'
Copy link
Member

Choose a reason for hiding this comment

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

This should be @emotion/primitives, all package names are aliased to their src.

@@ -8,7 +8,7 @@ import reactPrimitives from 'react-primitives'
import { ThemeProvider } from 'emotion-theming'
import { render, unmountComponentAtNode } from 'react-dom'

import styled from '@emotion/primitives'
import styled from '../src'
Copy link
Member

Choose a reason for hiding this comment

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

Same as above

@@ -1,7 +1,7 @@
// @flow
import '../mock-primitives'
import * as React from 'react'
import styled, { css } from '@emotion/primitives'
import styled, { css } from '../../src'
Copy link
Member

Choose a reason for hiding this comment

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

Same as above

@@ -0,0 +1,23 @@
{
"name": "emotion-native",
Copy link
Member

Choose a reason for hiding this comment

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

@emotion/native

@nitin42 nitin42 added the WIP label Jul 13, 2018
Copy link
Member

@emmatown emmatown left a comment

Choose a reason for hiding this comment

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

🎉


const css = createCss(reactNative.StyleSheet)

const components = `ActivityIndicator ActivityIndicatorIOS ART Button DatePickerIOS DrawerLayoutAndroid
Copy link
Member

Choose a reason for hiding this comment

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

Could you make this into an array since it's easier to read and there's no need to split

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure! No issues.


let returnArguments = (...args) => args

describe('Emotion native css', () => {
Copy link
Member

Choose a reason for hiding this comment

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

I'm not really sure if we need all these tests duplicated, maybe it could just be a small smoke test to make sure css works

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was thinking of this too! I'll need some help over here so will ping you later when I comeback to this after fixing basic stuff.


## Introduction

This package contains some shared utilities which are consumed by `@emotion/primitives` and `@emotion/native` for styling and rendering components.
Copy link
Member

Choose a reason for hiding this comment

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

Could you add a note saying that most people won't need this package and should use @emotion/primitives and @emotion/native?


export function createStyled(
StyleSheet: Object,
getShouldForwardProp: Function = (cmp: React.ElementType): Function =>
Copy link
Member

Choose a reason for hiding this comment

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

This should be getShouldForwardProp: (cmp: React.ElementType) => (prop: string) => boolean = () => defaultPickTest and could you make this argument into an object so that if there needs to be more options in the future it's easier?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup! I'll fix it


function isPrimitiveComponent(component: React.ElementType) {
function shouldForwardProp(component: React.ElementType) {
Copy link
Member

Choose a reason for hiding this comment

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

Should be called getShouldForwardProp

// render(<Text innerRef={ref} id="something" />, rootNode)
// expect(ref.current).toBe(rootNode.querySelector('#something'))
// unmountComponentAtNode(rootNode)
// })
Copy link
Member

Choose a reason for hiding this comment

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

Any reason this was commented out?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm.. can't recall may be its related to emotion primitives PR work. Anyway, I'll uncomment and update the test.


### `createStyled(StyleSheet)`

`createStyled` also accepts a platform specific `StyleSheet` method for creating styles, and returns a styled component. You can assign primitives to it for example - `View`
Copy link
Member

Choose a reason for hiding this comment

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

"returns a styled component", createStyled returns a function that returns a function that returns a styled component right?


## Theming

Use `emotion-theming` for theming support.
Copy link
Member

Choose a reason for hiding this comment

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

An example maybe?

Copy link
Contributor Author

@nitin42 nitin42 Jul 13, 2018

Choose a reason for hiding this comment

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

Hm... That'd be great! I'll add it

AppRegistry.registerComponent('ExampleApp', () => App);
```

The API surface remains same for `@emotion/native` which means you can use all the API methods similar to `emotion`.
Copy link
Member

Choose a reason for hiding this comment

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

Reading this is confusing, the same as what? emotion? or @emotion/primitives? and you can't use everything that emotion has, e.g. keyframes and injectGlobal

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops! I missed those parts of emotion API. I'll fix it.

@nitin42
Copy link
Contributor Author

nitin42 commented Jul 13, 2018

I also noticed some flow types errors for CreateStyled in @emotion on running yarn flow.

@emmatown
Copy link
Member

if you merge from master, those flow errors should be fixed.

@nitin42
Copy link
Contributor Author

nitin42 commented Jul 14, 2018

I pulled latest changes from master and now getting this error on running yarn flow 😅

.flowconfig:14 Unsupported config section: "declarations"

Any ideas ?

Except this, all tests are updated and are passing.

@emmatown
Copy link
Member

@nitin42 run yarn install

@nitin42
Copy link
Contributor Author

nitin42 commented Jul 15, 2018

Still no luck! Getting that same error on running yarn install plus tests seems to be broken when I pulled the changes from master. Can you fix this and then I pull the updates ?

@nitin42
Copy link
Contributor Author

nitin42 commented Jul 17, 2018

Thanks @tkh44 for the review!

"dependencies": {
"@emotion/primitives-core": "1.0.0"
},
"peerDependencies": {
Copy link
Member

Choose a reason for hiding this comment

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

shouldnt a peer on react be here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops! Done ✅

color: hotpink;
`;

export default class App extends React.Component {
Copy link
Member

Choose a reason for hiding this comment

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

indentation is different here than in other examples (in this file), this could be enforced with prettier + lint-staged on precommit hook (with husky) as it can handle .md too, cc @mitchellhamilton

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That'd be great. This will also resolve all the formatting issues!

Copy link
Member

Choose a reason for hiding this comment

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

@Andarist If you could submit a PR for it, that would be great!

Copy link
Member

Choose a reason for hiding this comment

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

I'll try to remember to do that later :)


export { css }

export default /* #__PURE__ */ styled
Copy link
Member

Choose a reason for hiding this comment

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

this doesnt make much sense here, #__PURE__ comments are useful for call expressions, this is just exported identifier

Not sure what actual value it gives in context of react-native (it's not that much bytes-constrained as web), it certainly won't hurt, but would have to be used differently.

I would convert this to:

export default components.reduce((acc, comp) =>
  Object.defineProperty(acc, comp, {
    enumerable: true,
    configurable: false,
    get() {
      return styled(reactNative[comp])
    }
  })
, styled)

This would make it self contained expression that can be more safely annotated with PURE.

You could also use my plugin to insert those PURE annotations automatically - https://github.com/Andarist/babel-plugin-annotate-pure-calls , but ofc that's not obligatory 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That makes sense. I'll refactor the code to use your solution.


import { styled } from './styled'

const css = createCss(reactNative.StyleSheet)
Copy link
Member

Choose a reason for hiding this comment

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

this could be annotated with PURE

/**
* a function that returns a styled component which render styles in React Native
*/
let styled = createStyled(StyleSheet)
Copy link
Member

Choose a reason for hiding this comment

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

this is also a good candidate for PURE

Copy link
Contributor Author

Choose a reason for hiding this comment

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

are there any side-effects of using PURE here ?


### `createStyled(StyleSheet)`

`createStyled` also accepts a platform specific `StyleSheet` method for creating styles, and returns a function that returns a function that returns a styled component. You can assign primitives to it for example - `View`
Copy link
Member

Choose a reason for hiding this comment

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

and returns a function that returns a function that returns a styled component

while this is technically correct, it sounds like a gibberish 😅 could we rephrase it ?


export function createStyled(
StyleSheet: Object,
{ getShouldForwardProp = () => defaultPickTest }: options = {}
Copy link
Member

Choose a reason for hiding this comment

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

I've noticed that this API differs from web one. Regular emotion accepts shouldForwardProp - is there any reason why this is different here?

Copy link
Member

Choose a reason for hiding this comment

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

the whole createStyled also differs from regular create-emotion-styled API which accepts whole emotion (created upfront with create-emotion). It seems to me that react-native equivalent would be

const css = createCss(StyleSheet)
const styled = createStyled(css)

or

const css = createCss(StyleSheet)
const styled = createStyled({ css })

I'm not sure if native version has some other API than css/styled (web one has), so I'm not sure which one would be more appropriate here.

Copy link
Member

Choose a reason for hiding this comment

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

regarding shouldForwardProp - I think I've found the answer, this is used at 1 layer above than regular shouldForwardProp, right? would be good to support normal shouldForwardProp, but it can also be added later (maybe create some kind of TODO list of things that could be added to achieve parity with regular emotion)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The API here differs because, we may add some more options along with getShouldForwardProp in the future.

Copy link
Member

Choose a reason for hiding this comment

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

The API here differs because, we may add some more options along with getShouldForwardProp in the future.

Making it an object is ofc preferrable, but that was not the question. Normal emotion also accepts configuration objects - my question was about API differences.

And what about 1st arg to createStyled?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are no such API differences except we use StyleSheet constructor and a little refactored code taken from @emotion/primitives. The goal is to keep the signature of functions similar across the env. whether it's web, native or sketch. I liked your suggestion and will make the changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Your argument is valid here since having such API reduces the maintenance cost.

) {
const css = createCss(StyleSheet)

return function createEmotion(component: React.ElementType) {
Copy link
Member

@Andarist Andarist Jul 17, 2018

Choose a reason for hiding this comment

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

on web this is called createEmotionStyled, while returned function is called createStyled

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm... it's a little inconsistent with the nomenclature. I'll fix that later!


Styled.displayName = `emotion(${getDisplayName(component)})`

return Styled
Copy link
Member

Choose a reason for hiding this comment

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

would be cool to support component as selectors, not sure if possible though and can be added later if yes

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I haven't looked into it yet to check whether its possible or not. Let's save it up for later!

import { createStyled } from './styled'
import { styled as createStyled } from './styled'

const css = createCss(StyleSheet)
Copy link
Member

Choose a reason for hiding this comment

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

this is a good candidate for PURE

@nitin42
Copy link
Contributor Author

nitin42 commented Jul 18, 2018

I've made some changes. I am still not sure about annotating call expressions with PURE because it may have side-effects ? Still not sure. Let me know! 😅

Thanks for the review!

@emmatown
Copy link
Member

The pure comment tells uglify that a function call can be removed if the return value is not used so it doesn't really matter whether something has side effects or not but rather if they have side effects that are useful without the return value.

For example, on the web, emotion's css isn't pure since it inserts css rules and etc. but we add the pure comment in babel-plugin-emotion in front of css calls since if you're not using the class name from css, the call is pointless.

@nitin42
Copy link
Contributor Author

nitin42 commented Jul 26, 2018

I think we can merge this now. The remaining changes don't affect the API design!

@codecov
Copy link

codecov bot commented Jul 28, 2018

Codecov Report

Merging #759 into master will increase coverage by 0.01%.
The diff coverage is 100%.

Impacted Files Coverage Δ
packages/primitives-core/src/utils.js 100% <ø> (ø)
packages/primitives/src/index.js 100% <100%> (ø) ⬆️
packages/native/src/styled.js 100% <100%> (ø)
packages/primitives/src/styled.js 100% <100%> (ø) ⬆️
packages/native/src/index.js 100% <100%> (ø)
packages/primitives-core/src/styled.js 100% <100%> (ø)
packages/primitives-core/src/css.js 100% <100%> (ø)

@emmatown emmatown merged commit 8edf5d3 into emotion-js:master Jul 28, 2018
@nitin42
Copy link
Contributor Author

nitin42 commented Jul 31, 2018

@mitchellhamilton have you published the package ? Haha someone jumped in my Twitter DM to ask about our work on emotion native 😅

@emmatown
Copy link
Member

emmatown commented Aug 1, 2018

@nitin42 Yup, it’s published!

@nitin42 nitin42 deleted the emotion-native branch August 29, 2018 11:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants