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

[Core] Implements prepareStyles as composition of functions in muiTheme #2986

Merged
merged 1 commit into from
Feb 1, 2016

Conversation

newoga
Copy link
Contributor

@newoga newoga commented Jan 20, 2016

This commit implements the prepareStyles function as a composition of functions that can be configured in muiTheme. It switches the ./mixin/style-propable.js implementation to use the prepareStyles stored in context.

@oliviertassinari @alitaheri
This is an initial implementation of the approach of storing prepareStyles in context. I cleaned up the code a bit, however like I said, I don't think we should document this until we've confirmed we're happy and the API is final (I already have some ideas of how to improve it, but it shouldn't have any real immediate benefit). It's also very well possible this will all go away when we adopt a new styling framework.

Note: One minor difference I made is that the getMuiTheme function actually checks the computed theme for isRtl before composing the functions. If it's false, it doesn't even bother including the ensureDirections() (renamed to rtl()) method. Edit: It now does this for the autoprefixer() method based on the userAgent property

Right now, the list of transformers can be overridden by adding the following property to the muiTheme. The following configuration would essentially disable the default behavior of always autoprefixing and calling rtl() when isRtl is equal to true. Removed customizing of transformers in this PR, other approaches have since been implemented.

Remaining tasks:

  • Move implementation of "double call warning" out of rtl() into something that runs before all style transformations are executed. Done
  • Change rtl() implementation to not care is isRtl No longer necessary

Future PR:

  • Convert components not using ./mixins/style-propable.js to get prepareStyles() from context, not from importing it from ./utils/style.js.
  • Deprecate and/or remove ./utils/style.js

@newoga
Copy link
Contributor Author

newoga commented Jan 20, 2016

Related to #2316, #2530, #2852.

@alitaheri
Copy link
Member

@newoga Awesome work indeed 👍 👍 Thank you 😄

@@ -0,0 +1,3 @@
import flowRight from 'lodash.flowright';
Copy link
Member

Choose a reason for hiding this comment

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

Do we need these 2 files? I mean it's gonna look like "API surface" to me. We can just require these whenever we want. and I'm not seeing a day we wouldn't use lodash, you know, since it's awesome 😁

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@alitaheri I had the same though and I'm open to removing these. The only library I thought we may one day replace these with is recompose, which ironically also uses lodash's compose for its implementation.

Either way I'm going to take it out compose.js until we start using it in several places.

I wanted to talk to you two about merge.js though. I'm thinking we should replace all mergeStyles with one of the three:

  1. merge
  2. Object.assign
  3. destructuring

Copy link
Member

Choose a reason for hiding this comment

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

how about lodash.assign? It uses Object.assign if it's available. and it's faster than extend or merge imo. @oliviertassinari Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For the record I'd be happy with that 👍

@alitaheri
Copy link
Member

I believe we should use assign for every style merging too. 👍 Thanks @newoga

}

// Left to right is the default. No need to flip anything.
if (!muiTheme || !muiTheme.isRtl) return style;
Copy link
Member

Choose a reason for hiding this comment

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

I think that we don't need this line.
More generally, do we really need to provide the muiTheme to the transformers?

Copy link
Member

Choose a reason for hiding this comment

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

@oliviertassinari Let's not limit ourselves for now. We might want to add other things, or if this is gonna be documented, others might like to have access to the theme in their transformers. the impacts of having it is likely none. but excluding it completely might make it hard to put back (or maybe even impossible). besides! we might even be able to use this to pass down userAgent down the context and use it in the prefixer. Just saying 😁

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@oliviertassinari I agree we don't need the line but also agree with @alitaheri that we should possibly leave the muiTheme in the transformers for now.

The reason why the muiTheme was there in the first place is because this ensureDirections now names (rtl) was left untouched minus the name and merge implementation. I believe the rtl check could be removed and the warning show be moved top level too (these are currently listed as remaining task at the top of the PR)

@newoga newoga force-pushed the style-refactor-experiment branch 3 times, most recently from 7a409c9 to dbb7bb5 Compare January 24, 2016 06:54
@newoga
Copy link
Contributor Author

newoga commented Jan 24, 2016

@oliviertassinari @alitaheri

I made some additional changes to this and rebased onto master so that it includes @oliviertassinari's auto-prefix changes. I decided to take passing in custom transformers out of the API for now. I think having that feature is now less urgent because the the two existing transformers (prefixing and RTL) can be disabled through muiTheme configuration (userAgent set to false and isRtl set to false disables both).

If we want to explore making this publicly accessible, I think we can explore options in a new PR.

The two key changes I made were the following:

Moved the check for running prepareStyle() twice out of the rtl() function

I decided to not to make the check a transformer that was part of the chain, because there would be no way to cancel the rest of the chain from running. I decided instead to wrap the chained function with the check so that it gets called first and exits if it sees if it was ran already.

In addition, I changed the check from using a object key to a Symbol. I think this is safer so that developers can't set the property to "trick" the check into running and it also gets the nasty did-flip keys out of the style objects in the DOM.

The end result is that this check is ran in development AND production, just that it only logs a warning in development. I think this is the better approach though.

Changed mergeStyles() implementation to Object.assign().

The first reason is because I think it's simpler, but also Object.assign() supports assigning Symbol keys and out previous merge implementation did not. This change was required for the first step above. I think we were all leaning towards moving towards either Object.assign() or lodash's assign anyway.

@alitaheri
Copy link
Member

@newoga I see you have brainstormed the crap out of it 👍 👍 Really great job. I really like your approaches 👍 👍 🎉

Also, Does the polyfill also assign Symbols?

if (!objB) return objA;
return update(objA, {$merge: objB});
}
export const mergeStyles = (...args) => Object.assign({}, ...args);
Copy link
Member

Choose a reason for hiding this comment

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

Do we really need to clone? I think it's an unnecessary allocation of an extra object. @oliviertassinari Thoughts?

Debatable though. I understand immutability, but since this is part of a chain, maybe it won't be all that necessary. Although I think the implications of cloning are a lot less than not. But still...

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 wish we didn't need this but it was breaking things when we weren't cloning. I think we have some situations in source where we may be passing in an undefined style, but I haven't been able to pinpoint everywhere it's happening yet. I just know calling simply Object.assign broke the docs site and the tests.

I think it's worth going back and investigating though! I think the ideal would be that we shouldn't need to do this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh and one more point. the mergeStyles is really going to be deprecated/removed when style-propable is removed. This is kind of temporary.

I'm recommending we just reimplement the components to use the prepareStyles from context directly, not from the style-propable mixin, and anywhere we were calling mergeStyles() we replace with Object.assign. After that this can go away.

Copy link
Member

Choose a reason for hiding this comment

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

Well, not having it is dangerous. Let it be, With memoization and proper optimization this doesn't matter at. So we better keep it like this.

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 agree, sounds good 👍

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 recommending we just reimplement the components to use the prepareStyles from context directly, not from the style-propable mixin, and anywhere we were calling mergeStyles() we replace with Object.assign. After that this can go away.

Oh, right. So all the more reason to ignore what I said :D :D This is great as it is 😁

Copy link
Member

Choose a reason for hiding this comment

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

Do we really need to clone?

I think that we don't have the choice regarding how we use the merge method in the source code.
I guess we could work on it but that out of the scope of this PR. It's perfect like this for now 👍.

@newoga
Copy link
Contributor Author

newoga commented Jan 24, 2016

Also, Does the polyfill also assign Symbols?

That's an intersting question. I assumed so but assuming can be dangerous 😛. I'll have to investigate this more...

@alitaheri
Copy link
Member

This is the polyfill. unfortunately it doesn't support Symbols. but I think this wouldn't matter because if Object.assign is not supported then so isn't Symbol. They both are parts of the same version 😆 😆

var _extends = Object.assign || function (target) {
  for (var i = 1; i < arguments.length; i++) {
    var source = arguments[i];
    for (var key in source) {
      if (Object.prototype.hasOwnProperty.call(source, key)) {
        target[key] = source[key];
      }
    }
  }
  return target;
};

import compose from 'lodash.flowright';
import warning from 'warning';

const CALLED_ONCE = Symbol('prepareStyles/calledOnce');
Copy link
Member

Choose a reason for hiding this comment

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

Introducing a Symbol will have some implication, have a look at the issues that this PR solve #2904.
I think that it's a breaking change.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Mm, interesting. What do you and @alitaheri prefer? Use a regular string key or polyfill?

I wonder we're inevitably going to keep running into Symbol issues because developers often assume it's "included".

Copy link
Member

Choose a reason for hiding this comment

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

I think we should avoid polyfills for the time being. es6 has to become mainstream before we can use it libraries. I use every feature i can get my hands on in my application. but libraries are different 😁

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay did-flip from before made more sense in the context of ensureDirection. Do you have a preference to what key we use? prepared: true?

Copy link
Member

Choose a reason for hiding this comment

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

What about mui-prepared: true?
We should consider using a Symbol again with v0.15.0.

@newoga
Copy link
Contributor Author

newoga commented Jan 24, 2016

@oliviertassinari @alitaheri Alright guys, I think we're close 😣

This has the following changes:

  1. Moved all the "transformer inclusion" checks into the transformer definitions themselves. They will be filtered out if they don't return a truthy value.
  2. Since we decided that there are performance implications with the callOnce, I moved it out to a transformer like @oliviertassinari originally suggested. It returns early and is filtered out if it's in aaproduction environment.
  3. Replaced the Symbol with just a string for the callOnce check (to avoid polyfill).

@oliviertassinari
Copy link
Member

@newoga I think we have it! I like the current approach 👍. We will have to refactor a lot of code once it's merged.

@newoga newoga changed the title Implements prepareStyles as composition of functions in muiTheme [Core] Implements prepareStyles as composition of functions in muiTheme Feb 1, 2016
@newoga
Copy link
Contributor Author

newoga commented Feb 1, 2016

@oliviertassinari @alitaheri

FYI just rebased onto master so this didn't get too far behind. I also decided to remove the re-implementation/migration of mergeStyles to Object.assign from this PR. The feature presented in this PR (composing style transformers) works just fine with the existing merge implementation. I still think we should switch to Object.assign but I decided to present that in a new PR #3124 so we can finish that discussion there.

I think both PRs will be easier to understand when they're separated and these changes don't rely on one another so they don't need to be merged together.

const CALLED_ONCE = 'muiPrepared';

export default function callOnce() {
if (process.env.NODE_ENV === 'production') return;
Copy link
Member

Choose a reason for hiding this comment

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

What about englobing the all code inside the if statement?
I'm not sure uglify is smart enough to remove unreachable code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can do!

@oliviertassinari
Copy link
Member

@newoga Yes, let's merge this 🎉

@newoga
Copy link
Contributor Author

newoga commented Feb 1, 2016

@oliviertassinari Sounds good! @alitaheri Do you want to give this a last look?

@oliviertassinari
Copy link
Member

@alitaheri or @mbrookes feel free to merge if you are happy with it.

@alitaheri
Copy link
Member

All good and awesome 👍 👍 JUst fix those 2 things that one thing pointed out by @oliviertassinari (unreachable-code) and do the merge. Really nice work @newoga 👍 👍

Signed-off-by: Neil Gabbadon <neil.gabbadon@emikra.com>
@newoga
Copy link
Contributor Author

newoga commented Feb 1, 2016

Thanks @alitaheri, much appreciated 😄

If all is green, this should be good to go.

oliviertassinari added a commit that referenced this pull request Feb 1, 2016
[Core] Implements prepareStyles as composition of functions in muiTheme
@oliviertassinari oliviertassinari merged commit 04b7c23 into mui:master Feb 1, 2016
@oliviertassinari
Copy link
Member

@newoga Awesome work, 133 messages in this PR 🚀!

@newoga
Copy link
Contributor Author

newoga commented Feb 1, 2016

I know what a long one! Thanks guys! Big group effort 🎉 🎈

@alitaheri
Copy link
Member

Really nice work. this was a big step indeed 👍 👍

newoga added a commit to newoga/material-ui that referenced this pull request Feb 3, 2016
The commit allows us to start using `Object.assign` for merging styles instead of this `mergeStyles` utility method. Chrome's currently has a bug in which its `Object.assign` implementation does not consistently or keys of merged objects. This sometimes led to styling bugs because some style properties rendered to the DOM in the wrong order (see mui#2986). This commit includes a babel transformation step that replaces all uses of `Object.assign` with the implementation at `simple-assign` and ignores the browser's native Object.assign implementation.

Signed-off-by: Neil Gabbadon <neil.gabbadon@emikra.com>
newoga added a commit to newoga/material-ui that referenced this pull request Feb 4, 2016
The commit allows us to start using `Object.assign` for merging styles instead of this `mergeStyles` utility method. Chrome's currently has a bug in which its `Object.assign` implementation does not consistently or keys of merged objects. This sometimes led to styling bugs because some style properties rendered to the DOM in the wrong order (see mui#2986). This commit includes a babel transformation step that replaces all uses of `Object.assign` with the implementation at `simple-assign` and ignores the browser's native Object.assign implementation.

Signed-off-by: Neil Gabbadon <neil.gabbadon@emikra.com>
heetvachhani pushed a commit to heetvachhani/material-ui that referenced this pull request Feb 4, 2016
The commit allows us to start using `Object.assign` for merging styles instead of this `mergeStyles` utility method. Chrome's currently has a bug in which its `Object.assign` implementation does not consistently or keys of merged objects. This sometimes led to styling bugs because some style properties rendered to the DOM in the wrong order (see mui#2986). This commit includes a babel transformation step that replaces all uses of `Object.assign` with the implementation at `simple-assign` and ignores the browser's native Object.assign implementation.

Signed-off-by: Neil Gabbadon <neil.gabbadon@emikra.com>
heetvachhani pushed a commit to heetvachhani/material-ui that referenced this pull request Feb 6, 2016
The commit allows us to start using `Object.assign` for merging styles instead of this `mergeStyles` utility method. Chrome's currently has a bug in which its `Object.assign` implementation does not consistently or keys of merged objects. This sometimes led to styling bugs because some style properties rendered to the DOM in the wrong order (see mui#2986). This commit includes a babel transformation step that replaces all uses of `Object.assign` with the implementation at `simple-assign` and ignores the browser's native Object.assign implementation.

Signed-off-by: Neil Gabbadon <neil.gabbadon@emikra.com>
@newoga newoga deleted the style-refactor-experiment branch February 6, 2016 23:28
@zannager zannager added the core Infrastructure work going on behind the scenes label Mar 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core Infrastructure work going on behind the scenes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants