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

Components: Extract a generic dropdown component #2888

Merged
merged 2 commits into from
Oct 6, 2017

Conversation

youknowriad
Copy link
Contributor

@youknowriad youknowriad commented Oct 5, 2017

When working on several UI elements, I found myself repeating the same pattern over and over again when needing a Dropdown component:

  • Adding an "open" state value
  • Adding event handlers to handle open, toggle, close and click outside

This PR factorize these into a single Dropdown component leveraging renderProps to allow any custom UI while factorizing the state updates and the events handlers (see the README for usage)

We still need the direct usage of Popover in one or two advanced use-cases.

Checklist:

  • Build the Dropdown component
  • Test and document the Dropdown component
  • Use it instead of Popover

Testing instructions

Ensure that:

  • The color picker
  • The post visibility selector
  • The post schedule selector
  • The table of contents
  • The mode switcher

still work as expected

@youknowriad youknowriad self-assigned this Oct 5, 2017
@codecov
Copy link

codecov bot commented Oct 5, 2017

Codecov Report

Merging #2888 into master will increase coverage by 0.26%.
The diff coverage is 36.36%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2888      +/-   ##
==========================================
+ Coverage   33.92%   34.19%   +0.26%     
==========================================
  Files         190      191       +1     
  Lines        5695     5656      -39     
  Branches      999      992       -7     
==========================================
+ Hits         1932     1934       +2     
+ Misses       3183     3150      -33     
+ Partials      580      572       -8
Impacted Files Coverage Δ
editor/table-of-contents/index.js 0% <0%> (ø) ⬆️
editor/inserter/index.js 0% <0%> (ø) ⬆️
editor/header/mode-switcher/index.js 0% <0%> (ø) ⬆️
editor/sidebar/post-visibility/index.js 41.93% <0%> (-3.31%) ⬇️
blocks/color-palette/index.js 0% <0%> (ø) ⬆️
editor/sidebar/post-schedule/index.js 56.25% <70%> (-10.42%) ⬇️
components/dropdown/index.js 86.66% <86.66%> (ø)
... and 1 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a89801b...9f8e4ee. Read the comment docs.

Copy link
Contributor

@mcsf mcsf left a comment

Choose a reason for hiding this comment

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

The diff looks good, and nothing felt weird when testing. 💥

I was going to ask about the merits of this approach vs. something based on a higher-order component, but when I tried to write an example I got the feeling that this approach is better. :)

}

clickOutside( event ) {
if ( ! this.container.contains( event.target ) ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we guard against falsy this.container?

Copy link
Contributor Author

@youknowriad youknowriad Oct 5, 2017

Choose a reason for hiding this comment

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

contains is inclusive :)

return (
<div className="blocks-color-palette">
{ colors.map( ( color ) => {
const style = { color: color };
Copy link
Contributor

Choose a reason for hiding this comment

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

Super minor: { color } :)

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 forgot to update this before the merge.


return (
<div ref={ this.bindNode } className="editor-inserter">
function Inserter( { position, children, onInsertBlock, insertionPoint } ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

So satisfying to see. :)

@mcsf
Copy link
Contributor

mcsf commented Oct 5, 2017

Oh, for the record, one thing behaves slightly differently: toggling the Table of Contents popover. In master, if I keep clicking the (i) icon, the popover keeps switching between two positions (below and above, the latter being outside of the viewport), whereas in this branch the popover goes between three states: below -> closed -> above -> closed -> below ….

gut-wordcount-popover

So, in a way, things are incidentally less broken in this branch. 😅

@youknowriad
Copy link
Contributor Author

youknowriad commented Oct 5, 2017

@mcsf I suspect this issue to be still present in this branch somehow, it has to do with how the Popover switches its position if there's not enough place. This is something we're aware of (I think there are issues for this), and we'd have to fix globally for all popovers.

@mcsf
Copy link
Contributor

mcsf commented Oct 5, 2017

This is something we're aware of (I think there are issues for this), and we'd have to fix globally for all popovers.

I suspected this, thanks for the context.

@youknowriad
Copy link
Contributor Author

@mcsf here's a talk explaining why renderProps may be better than HoCs. https://www.youtube.com/watch?v=BcVAq3YFiuc

Unlike the speaker, I'm not all for one or another, it depends on the use-case. Composing HoC is nice sometimes.

@mcsf
Copy link
Contributor

mcsf commented Oct 5, 2017

Oh, functions-as-props definitely have a lot of merit, but as with any abstraction it was good to compare the two for a specific case.

</Popover>
</div>
) }
renderContent={ () => (
Copy link
Member

Choose a reason for hiding this comment

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

Did you consider just passing this as children? Even if it were a function callback?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I did, it was my first option, but then I hesitated whether the renderToggle should be the children function because it's the one being displayed as a child and not in a portal.

The answer was not obvious to me, which one to choose so I preferred avoiding it completely and being consistent between renderToggle and renderContent

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants