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

[Slider] Remove style-propable mixin and react-dom #3332

Merged
merged 4 commits into from
Feb 15, 2016

Conversation

felipethome
Copy link
Contributor

This PR removes the style-propable mixin and react-dom from the slider source.

It, also, fixes the line:

const calcDisabledSpacing = this.props.disabled ? ` -${disabledGutter}px` : '';

There should be a space between '-' and '${disabledGutter}px', otherwise it will not be valid CSS.

Just to point it out, the following statement changed from:

style={prepareStyles(this.props.style)}

to:

style={prepareStyles(Object.assign({}, this.props.style))}

Since, the reference to this.props.style is kept outside the render() method would start to produce the warning from callOnce everytime we call the render method for styles that use this.props.style

[Slider] Fix css calc for calcDisabledSpacing

[Slider] Fix css calc for calcDisabledSpacing

[Slider] Copy this.props.style directly in the div that uses it
@@ -199,7 +193,7 @@ const Slider = React.createClass({
getStyles() {
const fillGutter = this.getTheme().handleSize / 2;
const disabledGutter = this.getTheme().trackSize + this.getTheme().handleSizeDisabled / 2;
const calcDisabledSpacing = this.props.disabled ? ` -${disabledGutter}px` : '';
const calcDisabledSpacing = this.props.disabled ? ` - ${disabledGutter}px` : '';
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder, when this is not disabled, does it matter if it's undefined vs ''. If not, we could write this as

this.props.disabled && `-{disabledGutter}px`

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It actually matters because the result of:

this.props.disabled && ` - {disabledGutter}px`

when this.props.disabled is false will be false. So in the string concatenation that happens after:

width: `calc(${(this.state.percent * 100)}%${calcDisabledSpacing})`

we would have the following string for example: 'calc(75% - false)'
I could change the code to behave as expected for that kind of assignment, but I would prefer not, because the advantage of a ternary is that we have the possibility to have a variable with the same type always (like in a functional approach), in that case a string. While, in the other case sometimes we have false and sometimes a string.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh good catch! Thanks!

@newoga
Copy link
Contributor

newoga commented Feb 14, 2016

Good job @felipethome!

Just to point it out, the following statement changed from:
style={prepareStyles(this.props.style)}
to:
style={prepareStyles(Object.assign({}, this.props.style))}

Good catch, yes we've been careful to make sure to not prepareStyles directly from passed in props but to compute a new object from it. 👍

I left some comments that were unrelated to your changes that might improve code quality in other areas but so far this looks good.

I forget to ask before, can you also move the getStyles() outside of the class definition like this. We're trying to move away from imperative methods and trying to makes these getStyles() functions purely a result from props and state. Doing so you'll have to remove the getTheme() calls from the getStyles() method (after which you can also delete that method).

@felipethome
Copy link
Contributor Author

Thanks @newoga . And I will gladly remove the getTheme() function. I want to do it for a long time now 😄 .

I have a question, the slider has a lot of styles, some of them can be removed and I will do it soon, but most of them can't. A lot of these styles will me merged depending of state and prop variables. Right now, most of this merge computations are made in the render() method, but two of these merges are made in the getStyles(). I would like to have all these merge computations that depend of state or props ideally in one place. What do you think? (Note: right now it is easier if I make getStyles() free of state and props)

@mbrookes mbrookes added PR: needs revision The pull request can't be merged. More details is available on the code review or fails in the CI and removed PR: Needs Review labels Feb 14, 2016
@felipethome
Copy link
Contributor Author

I just realize that what I asked is not a good thing for the code. So, we don't need to change that. I will do the changes we talked about and remove getStyles to outside the class as you said, thanks!

@newoga
Copy link
Contributor

newoga commented Feb 14, 2016

@felipethome Yeah give it a shot. The general patter we've been following is construct the style object in getStyles(), merge with custom styles provided by props in render. Eventually many of these components can be written as functional components (which is pretty much what getStyles() is, except that it's not returning JSX).

@felipethome
Copy link
Contributor Author

@newoga can you give a look? I am sorry I removed the clearValue function, I thought it was dead code since it is not being used inside the component and it doesn't seem to be in the docs, but maybe is some legacy code so I put the declaration back to its place.'

Another thing, we are spreading all the props inside the root element:

const {...others} = this.props;
...
<div {...others} style={prepareStyles(Object.assign({}, this.props.style))}>

Should I fix that to spread only the ones left? I mean, the ones the component doesn't explicitly use.

Another thought, we call of "handle" the element that we drag, but it is really common to use the word handle in functions that are manipulating events, what do you think about change the name "handle" to "knob" or "thumb"?

@newoga
Copy link
Contributor

newoga commented Feb 15, 2016

Looking good!

@newoga can you give a look? I am sorry I removed the clearValue function, I thought it was dead code since it is not being used inside the component and it doesn't seem to be in the docs, but maybe is some legacy code so I put the declaration back to its place.'

Yeah, it's probably better to leave it in for now. That being said, we should consider deprecating those methods for the next release since this component is controlled with value and onChange.

Should I fix that to spread only the ones left? I mean, the ones the component doesn't explicitly use.

Yeah I think that's a good idea, let's add description, disabled, style etc. to the destructure. Though moving the {...other} destructure within the JSX to the left was a good idea too. 👍

Another thought, we call of "handle" the element that we drag, but it is really common to use the word handle in functions that are manipulating events, what do you think about change the name "handle" to "knob" or "thumb"?

Yeah I wondered the same thing, that's a good idea. Thumb seems like a good name based on the slider spec.

@felipethome
Copy link
Contributor Author

Unfortunately, I think we will need to accept the name "handle", because there are variables in the muiTheme that use this name like: handleSize or handleFillColor, so if I change in the end we would end up with two names ("handle" and "thumb").

@newoga
Copy link
Contributor

newoga commented Feb 15, 2016

because there are variables in the muiTheme that use this name like: handleSize or handleFillColor

Yikes, yes you're right. That makes me said 😞. @alitaheri @oliviertassinari thoughts on this?

@felipethome
Copy link
Contributor Author

It makes me sad too! Look this:
handleMouseDown(e) // handle here means handling the event
_onHandleMouseDown(e) // handle here means the element we drag
_dragHandler(e) // we also have the word handler to help

overflow: 'visible',
outline: 'none',
},
handleWhenDisabled: {
Copy link
Contributor

Choose a reason for hiding this comment

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

I see what you mean with these:
handleWhenDisabled, handledWhenPercentZero,handleWhenPercentZeroAndDisabled`, etc. it would be nice if we could replace all of these wit ha single handle key so that the we didn't need Object.assign call below.

This can probably be solved by ternary for some keys or if there's too much, something like what we did with badge should work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They were already in the code actually. I will try hard to change that, but it will take some time because there are a lot of cases that need to be handled (pun intended 😄)

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah I know! Not your fault. 😄

of cases that need to be handled (pun intended 😄)

😆

@newoga
Copy link
Contributor

newoga commented Feb 15, 2016

Yeah that's brutal. I really would like to fix that. Let me think about options...

@felipethome
Copy link
Contributor Author

@newoga do you think the following code is too difficult to read?

    handle: {
        boxSizing: props.disabled ? 'content-box' : 'border-box',
        position: 'absolute',
        cursor: props.disabled ? 'not-allowed' : 'pointer',
        pointerEvents: 'inherit',
        top: '0%',
        left: state.percent === 0 ? '0%' : `${(state.percent * 100)}%`,
        zIndex: 1,
        margin: `${(slider.trackSize / 2)}px 0 0 0`,
        width: props.disabled ? slider.handleSizeDisabled :
          state.active ? slider.handleSizeActive : slider.handleSize,
        height: props.disabled ? slider.handleSizeDisabled :
          state.active ? slider.handleSizeActive : slider.handleSize,
        backgroundColor: props.disabled ? slider.trackColor :
          state.percent === 0 ? slider.handleFillColor : slider.selectionColor,
        backgroundClip: 'padding-box',
        borderRadius: '50%',
        borderStyle: 'solid',
        borderColor: (state.focused || state.hovered) ? slider.trackColorSelected : slider.handleColorZero,
        borderWidth: state.percent === 0 && !props.disabled ? `${slider.trackSize}px` : 0,
        transform: 'translate(-50%, -50%)',
        transition:
          `${Transitions.easeOut('450ms', 'background')}, ${
          Transitions.easeOut('450ms', 'border-color')}, ${
          Transitions.easeOut('450ms', 'width')}, ${
          Transitions.easeOut('450ms', 'height')}`,
        overflow: 'visible',
        outline: 'none',
    },

The slider can have a lot of different states: active, hovered, focused, disabled, percent zero. That is why I am thinking about ternaries instead of if-else generalizing all the states. Maybe the original way is more readable?

@newoga newoga added PR: Needs Review and removed PR: needs revision The pull request can't be merged. More details is available on the code review or fails in the CI labels Feb 15, 2016
@newoga
Copy link
Contributor

newoga commented Feb 15, 2016

Wow, you're not kidding 😆 Yeah maybe there's just too much state here to use ternarys for. Alright, let's leave it alone for now. I think there might be a way to break it up a bit still but let's see if we can get this merged now since we got style-prop and reactdom out.

@newoga
Copy link
Contributor

newoga commented Feb 15, 2016

@oliviertassinari @alitaheri Can you take a look at this when you get a chance?

@felipethome
Copy link
Contributor Author

That is a pity, but yes..would be too much for this PR. Can I at least move this piece of code:

{
    left: `${(percent * 100)}%`,
}

To inside of the getStyles method()? Because then we get a render() free of CSS properties.

Also, just something to point:

const sliderStyles = Object.assign({}, styles.root, this.props.style);
...
<div {...others} style={prepareStyles(Object.assign({}, this.props.style))}>
    <span>{this.props.description}</span>
    <span>{this.props.error}</span>
    <div
      style={prepareStyles(sliderStyles)}
      ...

The this.props.styles are being applied two times, and should be just in the root element, right? But we probably would break some pages out there changing that right now.

@newoga
Copy link
Contributor

newoga commented Feb 15, 2016

Can I at least move this piece of code:

Yeah definitely, that makes sense

The this.props.styles are being applied two times, and should be just in the root element, right? But we probably would break some pages out there changing that right now.

I didn't even catch that, that's a bug in my opinion. We ran into that with the AutoComplete component recently too. The style prop should really only be used for the root element. I want to fix this too...

Looking at the code more closely, I wonder if the sliderStyles div should really be the root, and managing the error text and description should be handled outside of this component. This component doesn't really offer much flexibility in customizing the style for the description and error and I wonder if that functionality should be moved to a wrapper component. That way this component could simplify too and really be just the slider (not the slider control including label, etc.). (Don't do this now, just brainstorming)

@felipethome
Copy link
Contributor Author

It doesn't offer anything at all for error and description. I think the slider component is more complex than what it seems when you just think about it. The material design specification also doesn't help us here because it has a lot of details about the design of this component.

That way this component could simplify too and really be just the slider (not the slider control including label, etc.)

I agree and If we had a directory just for the slider component, we would be able to break the implementation into pieces like: handle/thumb, track, errors, root. And that would simplify a lot..

Another awesome thing, would be a test case. Unfortunately, this test case isn't simple to write because of the amount of properties the user can set (that is the price we pay for customization).

@newoga
Copy link
Contributor

newoga commented Feb 15, 2016

I agree and If we had a directory just for the slider component, we would be able to break the implementation into pieces like: handle/thumb, track, errors, root. And that would simplify a lot..

I agree, I think splitting this up is a good idea (and agree there is a lot more complexity to this than it seems, including functionality in the spec that we haven't implemented yet). Soon all the components will be put in directories (#3212) and we should be able to split it up. 😓 I wouldn't be totally opposed to starting to split of the thumb into a SliderThumb component from now.

@felipethome
Copy link
Contributor Author

Soon all the components will be put in directories (#3212)

Awesome!

I wouldn't be totally opposed to starting to split of the thumb into a SliderThumb component from now.

I would gladly be part of the discussion of how to split it, once you decide is time to do it.

I added the last changes we talked about, now I think it is done.

@newoga
Copy link
Contributor

newoga commented Feb 15, 2016

Okay awesome @felipethome, let's get some feedback from the other guys and we'll see what next steps are. Thanks so much, this is a huge help 👍

@oliviertassinari
Copy link
Member

@felipethome That looks good 👍.

Side note: I have noticed that autoPrefix.set(body.style, 'userSelect', value, this.state.muiTheme);
doesn't work correctly. That's kind of expected, the autoPrefix.set method is broken.

@felipethome
Copy link
Contributor Author

Thanks @oliviertassinari ! The PR #3237 will solve this problem if you accept it.

@newoga
Copy link
Contributor

newoga commented Feb 15, 2016

@felipethome Could you squash when you get a chance?

@alitaheri If all is green for you, feel free to merge. We'll get the autoPrefix and other related improvements in future PRs.

@alitaheri
Copy link
Member

Actually only squash f392585 The rest of the commits are good for the history 👍 I'll review in a moment 👍

@alitaheri
Copy link
Member

Side note: I have noticed that autoPrefix.set(body.style, 'userSelect', value, this.state.muiTheme);
doesn't work correctly. That's kind of expected, the autoPrefix.set method is broken.

Let's continue this on the other PR.

@oliviertassinari Feel free to merge, this looks good to me 👍

oliviertassinari added a commit that referenced this pull request Feb 15, 2016
[Slider] Remove style-propable mixin and react-dom
@oliviertassinari oliviertassinari merged commit b442dc0 into mui:master Feb 15, 2016
@oliviertassinari
Copy link
Member

@felipethome Thanks! 🎉

@alitaheri
Copy link
Member

Awesome, thanks a lot @felipethome 👍 👍 👍

@newoga
Copy link
Contributor

newoga commented Feb 15, 2016

Great job! @felipethome 😄

@felipethome
Copy link
Contributor Author

Thank all of you too! 😄

@zannager zannager added the component: slider This is the name of the generic UI component, not the React module! label Mar 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: slider This is the name of the generic UI component, not the React module!
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants