-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Themes: Refactor themes-selection
#2444
Conversation
29d0185
to
0f20e03
Compare
0f20e03
to
a7b156d
Compare
// Theme ID (theme-slug) | ||
id: React.PropTypes.string.isRequired, | ||
// Index of theme in results list, if any | ||
theme: React.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.
Interesting, this PropType is too complex to repeat in various places. Should we factor out this PropType somewhere so it can be shared? Might be better just to have it in one place and expect a PropType of object elsewhere.
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'd like to see a Calypso lib of PropTypes somewhere. Maybe something following the client/state
hierarchy?
061155b
to
5cb693f
Compare
|
||
export function getButtonOptions( site, theme, isLoggedOut, actions, setSelectedTheme, togglePreview ) { | ||
let options = pick( buttonOptions, option => ! ( option.isHidden && option.isHidden( site, theme, isLoggedOut ) ) ); | ||
let options = pick( buttonOptions, option => ! option.isHidden ); | ||
options = mapValues( options, appendLabelAndHeader ); |
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 use lodash's merge
or similar 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.
Can we use lodash's
merge
or similar here?
62b7335
to
96829a1
Compare
I've rebased to master to do some Chrome Timeline runs. |
Running the Chrome Timeline, performance between this branch and This PR shows encouraging signs in terms of which functions are most expensive, with (Compare the percentages between these two shots, not the times) |
@seear Great, thanks for looking into this! |
d4a9880
to
d274b0b
Compare
d274b0b
to
9ed2d24
Compare
Code looks good to me after the walkthrough session and subsequent tweaks, so I'll give a 👍 I have not run any kind of rigorous testing, so it might be worth running either the e2e tests or going through a full set of manual tests before merge if you haven't already done so. |
Instead of passing down a gradually more bound getButtonOptions() function, invoke it fully inside main.jsx already, and make its `getUrl` and `action` properties functions that accept theme as an argument. Then, invoke those functions inside the `Theme` and `MoreButton` components, i.e. only at the very bottom of the component hierarchy. Also, use the return value from `getButtonOptions()` to pass some other props more cleanly. * Pass theme arg to getUrl() only in ThemeMoreButton instead of binding early If we want to invoke our actions and URL generators inside the more button instead of binding to theme (too) early, we need to pass the entire object, since all action and URL helpers require different theme attributes. * Get rid of JSX bind()s (for performance) * theme-options: * Move buttonOptions back inside getButtonOptions() * s/hideForSite/isHidden/ * s/setSelectedTheme/letUserSelectSite/ * s/togglePreview/showPreview/ * Inline `appendUrl()` and `appendAction()` The functional approach made things less legible here IMO -- e.g. it had a 3-clause if/elseif branch that actually acted on 4 items. I find it clearer as it is now. * Move the remaining `appendActionTracking()` function out of `getButtonOptions()`. * Add tracking to 'More' button actions inside main.jsx * main.jsx: Leverage `buttonOptions` to simplify the WebPreview button action * components/theme/test: Remove obsolete isSelected prop * Fix Theme component tests * ThemesSelection: Instead of customize and preview, accept unified `onScreenshotClick` and `screenshotLabel` props * themes/main: s/setSelectedTheme/showSiteSelectorModal/
9ed2d24
to
1c74c53
Compare
Thanks @seear! Squashed, and ran e2e tests. Will merge once CircleCI finished. |
Themes: Refactor `themes-selection`
tl;dr: Instead of passing down a gradually more bound
getButtonOptions()
function, invoke it fully insidemain.jsx
already, and make itsgetUrl
andaction
properties functions that accepttheme
as an argument. Then, invoke those functions inside theTheme
andMoreButton
components, i.e. only at the very bottom of the component hierarchy. Also, use the return value fromgetButtonOptions()
to pass some other props more cleanly. Why? Been an illegible mess on the brink of unmaintenable.Removes the
theme
argument fromgetButtonOptions()
, as returned fromtheme-options
. This way,getButtonOptions()
returns an object that is much easier to work with than the bound functions it previously returned. Instead, that object's (nested)getUrl
andaction
properties are now bound functions that accepttheme
and are invoked at the very leaves of the component hierarchy -- in the 'More' button menu, when the latter's entries are rendered. (If this turns out to affect performance negatively, we might want to tackle #1599 as a follow-up to this PR.)This means no more expensive JSX
.bind()
s along that chain. It also means we have to pass an entire theme object down to the 'More' button, instead of just select attributes, asgetUrl
andaction
functions rely on helpers that use all different theme object properties. Note that we also had a hack in place that re-created atheme
object from individual properties, and that this PR now also gets rid of.Furthermore, that bundled information on theme actions is used inside
main.jsx
to pass data more cleanly down to descendant components. For example, action and label for the theme screenshot are now set there (instead of passing downcustomize
andtogglePreview
actions as props individually, and letting theTheme
component itself decide on the labelling. Ugh.).No visual changes. To test -- check that the theme showcase works as before. Also check the logged-out signup flow since it also uses
ThemesList
.Some more possible refactoring items for future PRs:
Theme
insideThemesList
), avoiding JSX.bind()
s quite naturally leads to a pattern where the list (parent) component passes two different kind of data to the element (child) component: 1 -- the individual element data (the theme object in this case), and 2 -- a bunch of functions that accept the element data as an argument (e.g.onScreenshotClick
), and are invoked by the element component. However, this is still a bit inconsistent as of this PR, as some of those functions are already invoked atThemesList
's level.actions
totheme-options
, we mightimport
the actions module there, and only passdispatch
frommain
.ThemesSiteSelectorModal
anisVisible
prop (instead of conditionally rendering it via{ this.isThemeOrActionSet() && <ThemesSiteSelectorModal ... /> }
). There used to be some issues with this approach in the past (as ThemesSiteSelectorModal rendersTheme
, so possibly due to missing props), but we might want to re-visit this.buttonOptions
, i.e. by passing the action name (e.g. 'preview') as a string for key lookup. This would somewhat minimize duplicated props. (For the special case ofTheme
components that don't need a more button menu (signup flow!), we could pass just one option inbuttonOptions
.) However, we can't currently do this due to different analytics events that we are recording.getButtonOptions
,getOptions
,buttonContents
etc for essentially the same thing, so we should probably settle on a unique name for these things.theme-options
forCurrentThemeCard
(we can re-useisHidden
in order to hide the 'Support' button on Jetpack sites, and for free themes).WebPreview
(and how itsButton
is wired to an action and given a label) could still use some more makeover.