-
Notifications
You must be signed in to change notification settings - Fork 53
Conversation
0eb4db4
to
327b86c
Compare
I fixed some bugs regarding merging the component's variables and styles. The values that we receive from the theme are in the following format:
so I introduced some helpers methods for converting these lists to one array of callables and another one for merging the callables. The variables and the styles can now be overridden when using the components by providing the variables and styles props. Let me know if you have any feedback or need some more help with this. |
418e947
to
c6298fe
Compare
Thanks for wanting to help. Note, I had a lot of unpushed changes that included dealing with merging themes downstream on context. I've forced pushed those to save the work and resume progress here. I will fix conflicts and continue. |
c6298fe
to
4464caf
Compare
4464caf
to
a7e0599
Compare
a7e0599
to
9328b1b
Compare
docs/src/index.tsx
Outdated
style: { fontWeight: 700 }, | ||
}, | ||
] | ||
console.log('THEME', theme) |
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.
seems to be a debug leftover
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.
Thanks, branch was still in progress until right now 👍
6e27732
to
af4ed15
Compare
Codecov Report
@@ Coverage Diff @@
## master #16 +/- ##
==========================================
+ Coverage 85.97% 86.29% +0.32%
==========================================
Files 76 39 -37
Lines 1112 664 -448
Branches 228 90 -138
==========================================
- Hits 956 573 -383
+ Misses 149 88 -61
+ Partials 7 3 -4
Continue to review full report at Codecov.
|
src/components/Avatar/Avatar.tsx
Outdated
@@ -119,7 +113,7 @@ class Avatar extends UIComponent<any, any> { | |||
size="mini" | |||
name={icon} | |||
color="white" | |||
style={avatarRules.presenceIcon()} | |||
className={classes.presenceIcon} |
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 looks like a bug, will update.
src/components/Label/Label.tsx
Outdated
? callable(labelPropVariables)() | ||
: callable(Label.variables)() | ||
const iconVariables = callable(iconProps.variables)() || {} | ||
const labelVariables = callable(variables)() || {} |
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 may also need careful review.
The iconProps available at this point are produced by the shorthand factory. Any variables provided on iconProps at this point will not have access to the theme values on context so they cannot be resolved before being handed to the overrides function.
This means we will not be able to hand resolved variables to components reliably, unless we also make factories aware of how to resolve variables (which I think is a very bad idea). That would require making factories access context.
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.
To be clear, I don't have a solution or proposal for this right now but I think this PR needs to get merged. It is too difficult to maintain merge conflict fixes if we push it out.
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.
yes, agree with increasing complexity to address merge conflicts - will help you with merging this PR today.
src/components/List/listVariables.ts
Outdated
vars.contentLineHeight = siteVars.lineHeightSmall | ||
|
||
return vars | ||
} |
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.
Moving this particular file represents a significant change. The ListItem
variables were defined in the listVariables
file, making them accessible on context under List
instead of ListItem
. I had to rename this file to listItemVariables
and expose it on context as ListItem
in order for our theming contract to work (components get variables and styles by their display name).
src/components/Provider/Provider.tsx
Outdated
<RendererProvider renderer={outgoingTheme.renderer}> | ||
<ThemeProvider theme={outgoingTheme}>{children}</ThemeProvider> | ||
</RendererProvider> | ||
) |
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 is important. Each Provider now:
- consumes the theme from context
- merges it with its theme prop
- passes the merged theme downstream
This enables nested cascading themes.
src/lib/mergeThemes.ts
Outdated
}, target) | ||
} | ||
|
||
const mergeThemes = (...themes: IThemeInput[]): IThemePrepared => { |
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 function is best reviewed by looking at the tests, mergeThemes-test.ts.
_htmlFontSize = fontSize || getComputedFontSize() | ||
} | ||
|
||
export default rem |
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.
Cruft
PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.func]), | ||
), | ||
rtl: PropTypes.bool, | ||
theme: PropTypes.shape({ |
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.
Is this required? It looks to be based on the interface.
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.
Currently, docs are still generated from propTypes. That needs to be moved to being generated from typings. Then we can consider removing propTypes.
The other consideration is that most consumers are not running typescript. So, I think we may end up keeping both interfaces and propTypes around. Also, some runtime checks are really handy since we can do things with values (suggest icon names, etc).
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.
Sorry, I meant to say "should the theme
propType be .isRequired
?". I wasn't talking about the propTypes themselves.
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.
Got it, thanks. We have a scheduled task to make a default theme. For now, it is required.
src/lib/getClasses.ts
Outdated
|
||
// root, icon, etc. | ||
const componentParts: string[] = stylesArr.reduce((acc, next) => { | ||
return next ? _.union(acc, _.keys(next)) : acc |
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.
next
should always be truthy because of the above call to toCompactArray
, if I'm understanding this correctly.
src/lib/getClasses.ts
Outdated
|
||
/** | ||
* Returns a string of HTML classes. | ||
* Renders one or many component styles (objects of component parts) to the DOM. |
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.
What is this line about rendering to the DOM?
src/lib/mergeThemes.ts
Outdated
}, target) | ||
} | ||
|
||
const mergeThemes = (...themes: IThemeInput[]): IThemePrepared => { |
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.
Nit: I think this would be more simple if it took an array of themes.
-
It doesn't really take multiple arguments. It takes a single collection of themes. Representing this with an appropriate data type makes this obvious.
-
It becomes possible to add additional arguments if needed.
-
It makes it easier to provide a list of themes to this function. You can do
mergeThemes(...myListOfThemes)
, but I would ask "why?" when you can just domergeThemes(myListOfThemes)
. -
Least importantly, it saves you casting
arguments
to an array.
Justified myself because you asked :).
src/lib/mergeThemes.ts
Outdated
acc.rtl = mergeRTL(acc.rtl, next.rtl) | ||
|
||
// Use the correct renderer for RTL | ||
acc.renderer = acc.rtl ? felaRtlRenderer : felaRenderer |
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.
Should emptyTheme
have a default renderer? It looks to be possible to never set renderer
if next
is never truthy. Same goes for rtl
.
@@ -0,0 +1,5 @@ | |||
const toCompactArray = <T = any>(...values: T[]): T[] => { | |||
return [].concat(...values).filter(Boolean) |
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.
You shouldn't have to spread values
here. Also, why not _.compact
?
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.
Spread is required as each value may be a single value or an array. we need to concat each argument.
_.compact
only compacts a single array, it is essentially equivalent to [].filter(Boolean)
.
The feature I need is:
- Cast to array
- Allow items or arrays of items
- Compact the resulting array
So:
toCompactArray(1)
//=> [1]
toCompactArray(1, null, 2)
//=> [1, 2]
toCompactArray(1, 'two', [3, 4], null, ['more', 'stuff', undefined])
// => [1, 'two', 3, 4, 'more', 'stuff']
A more literal name would be something quite verbose like castToFlattenedCompactArray()
since it does all of these things.
You could also create it from _.compact(_.flatten(...values))
, however, given how often it is used and on hot paths I went with a minimal custom function that we can more easily optimize. Lodash does a lot of fancy checks and handling that we don't need to waste more cycles on.
Example, we can already optimize this by using reduce
and skipping the second loop via filter
. We can just not concat empty values into the accumulator. You could also put an early return check for values.length === 1
scenarios, etc...
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.
Sure, but why can't you just pass an array when you call toCompactArray
? 🤔
You'd still need to handle the flattening, of course.
componentVariables = {}, | ||
componentStyles = {}, | ||
rtl = false, | ||
renderer = felaRenderer, |
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.
See my previous comment about missing values for rtl
and renderer
. It would be a benefit to overall maintainability if the provider did all of the heavy lifting, and these consumers could be more confident about the interface they receive, since they don't have to work with all the same uncertainty.
import { IComponentPartStylesInput, ICSSInJSStyle } from '../../../../../types/theme' | ||
|
||
const accordionStyles: IComponentPartStylesInput = { | ||
root: (): ICSSInJSStyle => ({ |
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.
Would it perhaps be easier to type the overall styles object as [key: string]: (): ICSSInJSStyle
(or whatever the dammed syntax is) so that every individual style function doesn't have to declare its type?
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.
Yes, did this however it doesn't provide completions inside the style object for index keys. It will only do so if you type the return value. Even that has many shortcomings. Note the const accordionStyles: IComponentPartStylesInput
, which is doing exactly what you are referring to.
Since this PR is being broken down into smaller bits, we'll save the typings for later.
|
||
return { | ||
...rules, | ||
...(disabled |
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.
Nit: may be more maintainable to break this out into another function rather than inlining it.
@@ -0,0 +1,21 @@ | |||
export interface IDividerVariables { | |||
[key: string]: string | number |
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.
Why pin an unknown key to a specific type? Mostly just curious.
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 had to pick something and this seemed reasonable. This is essentially a "first guess" at what acceptable style variable values would be. Very much open to change and iteration.
We're targeting simple and flat variable objects. I can say I don't think we should allow arrays and objects and such. The theming system is pretty robust as it is, keeping variables simple will help make themes more usable.
src/lib/callable.ts
Outdated
@@ -1,3 +1,5 @@ | |||
const callable = val => (typeof val !== 'function' ? () => val : val) | |||
const callable = (possibleFunction: any) => (...args: any[]) => { | |||
return typeof possibleFunction === 'function' ? possibleFunction(...args) : possibleFunction |
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.
👍
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.
Can we attach a benchmark into JSDoc?
https://jsperf.com/startdust-callable
I thought that callableRest()
would be faster 😼 But, not
src/lib/renderComponent.tsx
Outdated
const styleParam: ComponentStyleFunctionParam = { | ||
props, | ||
variables, | ||
siteVariables, |
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.
⚠️ NOTE TO SELF
siteVariables
should not be passed to style functions. This was found in the breakout work for moving rules and variables to themes/teams/components. The Text component was directly accessing site variables via an import. Instead, it needs to get these values from the textVariables.ts.
This is the only case of siteVariables being accessed in a component's style function. It should also be removed.
Apologies, this PR is necessarily large:
Relevant features...
Styles prop
The
styles
prop is now supported:Nested Providers
You can now nest Providers and themes will be merged: