-
Notifications
You must be signed in to change notification settings - Fork 208
Conversation
Codecov Report
@@ Coverage Diff @@
## master #149 +/- ##
=====================================
Coverage 100% 100%
=====================================
Files 10 10
Lines 138 148 +10
Branches 35 37 +2
=====================================
+ Hits 138 148 +10
Continue to review full report at Codecov.
|
ac72282
to
8f5e395
Compare
Would love some 👀 from @jfrolich too! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks really neat! Just a few comments. Would love the opinion of other maintainers. I think this is good though!
src/__tests__/index.js
Outdated
@@ -433,3 +433,29 @@ test('should accept user defined contextTypes', () => { | |||
const props = {} | |||
expect(dynamicStyles).toHaveBeenCalledWith(props, theme, context) | |||
}) | |||
|
|||
it('should compose a component withProps', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer test('composes a component...
src/create-glamorous.js
Outdated
* props and the return value is used. | ||
* @return {Function} the glamorousComponentFactory function. | ||
*/ | ||
glamorousComponentFactory.withProps = (...outerProps) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer function declarations for all functions. Then above we can do:
glamorousComponentFactory.withProps = withProps
return glamorousComponentFactory
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great point!
src/create-glamorous.js
Outdated
*/ | ||
glamorousComponentFactory.withProps = (...outerProps) => { | ||
// This is a higher order function of `glamorousComponentFactory` | ||
return (...styles) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use a function declaration here too. That way this function can have a name which will be used in stack traces 😀
686e740
to
d1c175c
Compare
src/create-glamorous.js
Outdated
const evaluated = typeof prop === 'function' ? | ||
prop(innerProps) : | ||
prop | ||
return {...props, ...evaluated} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we attempt to spread evaluated
, and it evaluated as null
or undefined
(or any non object, really) -- that would raise an exception wouldn't it?
We should probably ensure this is an object, and perhaps test that it's a non-empty object to avoid the unnecessary operation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The values of evaluated and props should always be objects. But we should do a falsy check here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, but just like the dynamic styles, it sounds good to safeguard scenarios where the user may have a conditional return value, which could result in null
glamorous.input(props => {
if (props.foo) {
return {color: 'blue'}
}
return null
})
glamorous.input.withProps(props => {
if (props.foo) {
return {type: 'number'}
}
return null
})
That's technically valid, isn't it? Having an escape hatch where there might be an edge for the user where they might say "if the prop isn't x
, then we have nothing to do here"
Kinda like
render() {
const {foo, ...rest} = this.props
if (foo) {
return <Input {...rest} type="number" />
}
return <Input {..rest} />
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, by "should always be an object", I realize you mean more that it shouldn't be an array, string, etc -- not saying that null
isn't a valid return value 😃
allows composing components with predefined static or dynamic properties Reference #133
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implementation looks great! Let's get some docs 👍
c3151f8
to
a978804
Compare
I've added the docs as a subsection to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a couple of thoughts...
@@ -299,6 +299,39 @@ const MyStyledDiv = glamorous.div( | |||
<MyStyledDiv /> // styles applied: {padding-top: 1, padding-right: 2, padding-bottom: 3, padding-left: 4} and anything coming from `extra-thing`. | |||
``` | |||
|
|||
##### withProps |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just had a thought. Do you think that people may get confused and try to do:
const MyStyledInputWithProps = glamorous.input({
padding: 10
}).withProps({type: 'text'})
// or maybe:
const MyStyledInput = glamorous.input({
padding: 10
})
const MyStyledInputWithProps = MyStyledInput.withProps({type: 'text'})
In interested of that, perhaps we should support both?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think supporting both would be convenient - would your concern in #145 (comment) apply here though?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I've been thinking about it more and I think that I'm ok with doing this after all...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does follow familiar patterns in libraries like ImmutableJS, where you use the core API to create the subject, and you can build changes by working against the subject's API itself, and not necessarily on the core's factory API
var map = Immutable.Map({foo: 'bar'}) // factory API
var map2 = map.set('bar', 'foo') // subject API
So, similarly:
var Component = glamorous.div() // factory API
var Component2 = Component.withProps({foo: 'bar'}) // subject API
I do like this, as it opens the door for us to work with both in the same manner:
var Component = glamorous.div({backgroundColor: 'blue'})
var Component2 = Component.withProps({foo: 'bar'})
var Component3 = Component2.withStyle({color: 'red'})
Giving a consistent behavior to with*
APIs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still want to think about this before committing, maybe bring in a few people to vote.
I like both, but they introduce different convention. There's some pluses and minuses I see for both. I'm going to pause on this and write up my thoughts comparing both approaches to share in the main thread of this PR later tonight or tomorrow.
README.md
Outdated
// styles applied: {padding: 10} | ||
|
||
<MyStyledValidatedInput creditCard /> | ||
// renders: <Input type="credit-card" pattern="/([0-9]{4}){4}/" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should render <input
rather than <Input
, also I'm pretty sure that credit-card
is not a valid type
. Which is why the pattern is good :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ha good catch
I think we're going to opt for an integration example with |
I agree that the right move here is to promote the usage of I'm going to close this 👍 |
Ok, with #175 done, I think we're ready to give this another go. Based on discussion in #133, I'd like to support this API: import glamorous from 'glamorous'
const withPropsObject = {propA: 'a', propB: 'b'}
// withProps functions accept the same arguments that glamorous style functions accept.
const withPropsFunction = (props, theme, context) => ({propG: 'g', propH: 'h'})
// we'll call these withPropsThing, meaning when you see withPropsThing it could be either a function or object
const Div1 = glamorous.div({}).withProps(withPropsThing)
<Div1 /> // renders div with the props, filters out invalid `div` props like we do currently. What do we need to do to make this happen? How can I help you @ajwhite? |
The most actionable item here is to expose |
How can we help get this finished? I'd love to release this with the next major version |
I'm going to go ahead and finish this 👍 |
This is superseded by #255 |
Thanks for bringing this across the finish line, been a busy week and was on vaca! |
Sure thing! Thanks for all the work you've done! |
What: Adds
withProps
factory as discussed in #133Why: A handy API for composing more than styles on a component 😃
How:
glamorousComponentFactory
comes with awithProps
property on it. This allows you to recompose the factory with propertiesNote: these properties take a lower priority than properties defined on the
GlamorousComponent
by the user. Just like the styles.Feedback Requested
Before finalizing documentation, I'd like to get a sense of the approach is what we'd like!
Checklist:
Added myself to contributors table