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

Spacer: Show tooltip with height value on resize #23077

Merged
merged 32 commits into from
Jun 22, 2020
Merged

Conversation

ItsJonQ
Copy link

@ItsJonQ ItsJonQ commented Jun 10, 2020

Description

Screen Capture on 2020-06-10 at 16-38-00

This update enhances the Spacer block by rendering a tooltip element above the cursor when the user engages in a "drag to resize" interaction.

✨ New <ResizeTooltip /> component

The tooltip comes from a new <ResizeTooltip /> component. This component is a general resize-aware component that renders a tooltip on size changes. It isn't bound to <ResizableBox /> (which is what Spacer uses), but it can be integrated with it.

As an aside... This component has 2 different positions:

  • bottom
  • corner (default)
📄 Corner

Screen Capture on 2020-06-10 at 16-06-28

The Tooltip will render on the top/right of the parent element.

⬇️ Bottom

Screen Shot 2020-06-11 at 10 03 13 AM

The Tooltip will render on the bottom/center of the parent element.

🎨 Design

As far as the Tooltip design goes... I went with a UI that was in the spirit of "G2". Something minimal, sporting the all black/white look.

How has this been tested?

  • Tested in local Storybook
  • Tested in local Gutenberg

To test...

  • Run npm install
  • Run npm run dev
  • Add a Spacer block
  • Drag to resize. You should see the Tooltip.
  • Drag the RangeSlider (sidebar) to resize the Spacer. You should NOT see the Tooltip.

Types of changes

  • Add react-resize-aware as dependency in @wordpress/components (it's also used in @wrodpress/compose)
  • Add new <ResizeTooltip /> component
  • Exports ResizeTooltip from @wordpress/components
  • Integrate <ResizeTooltip /> into Spacer block

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • My code has proper inline documentation.
  • I've included developer documentation if appropriate.
  • I've updated all React Native files affected by any refactorings/renamings in this PR.

The idea for this PR originally came from:
#22956

@ItsJonQ ItsJonQ added [Type] Enhancement A suggestion for improvement. [Package] Components /packages/components [Block] Spacer Affects the Spacer Block [Feature] Design Tools Tools that impact the appearance of blocks both to expand the number of tools and improve the experi labels Jun 10, 2020
@ItsJonQ ItsJonQ self-assigned this Jun 10, 2020
const classes = classnames( 'components-resizable-visualizer', className );

return (
<Root aria-hidden="true" className={ classes } ref={ ref } { ...props }>
Copy link
Author

@ItsJonQ ItsJonQ Jun 10, 2020

Choose a reason for hiding this comment

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

By default, I felt like it may be better to hide this tooltip via aria-hidden. This can be overridden via incoming props.

@github-actions
Copy link

github-actions bot commented Jun 10, 2020

Size Change: +1.77 kB (0%)

Total Size: 1.13 MB

Filename Size Change
build/annotations/index.js 3.62 kB +1 B
build/block-directory/index.js 7.27 kB +3 B (0%)
build/block-library/index.js 129 kB +87 B (0%)
build/blocks/index.js 48.1 kB +1 B
build/components/index.js 197 kB +1.66 kB (0%)
build/compose/index.js 9.62 kB +16 B (0%)
build/data/index.js 8.44 kB +1 B
build/edit-navigation/index.js 8.26 kB -1 B
build/edit-post/index.js 303 kB +6 B (0%)
build/edit-site/index.js 16.6 kB +1 B
build/edit-widgets/index.js 9.33 kB +1 B
build/editor/index.js 44.8 kB -1 B
build/format-library/index.js 7.72 kB -1 B
build/redux-routine/index.js 2.85 kB -3 B (0%)
ℹ️ View Unchanged
Filename Size Change
build/a11y/index.js 1.14 kB 0 B
build/api-fetch/index.js 3.4 kB 0 B
build/autop/index.js 2.83 kB 0 B
build/blob/index.js 620 B 0 B
build/block-directory/style-rtl.css 937 B 0 B
build/block-directory/style.css 937 B 0 B
build/block-editor/index.js 106 kB 0 B
build/block-editor/style-rtl.css 10.7 kB 0 B
build/block-editor/style.css 10.7 kB 0 B
build/block-library/editor-rtl.css 7.86 kB 0 B
build/block-library/editor.css 7.87 kB 0 B
build/block-library/style-rtl.css 8 kB 0 B
build/block-library/style.css 8.01 kB 0 B
build/block-library/theme-rtl.css 730 B 0 B
build/block-library/theme.css 732 B 0 B
build/block-serialization-default-parser/index.js 1.88 kB 0 B
build/block-serialization-spec-parser/index.js 3.1 kB 0 B
build/components/style-rtl.css 15.9 kB 0 B
build/components/style.css 15.8 kB 0 B
build/core-data/index.js 11.4 kB 0 B
build/data-controls/index.js 1.29 kB 0 B
build/date/index.js 5.47 kB 0 B
build/deprecated/index.js 772 B 0 B
build/dom-ready/index.js 569 B 0 B
build/dom/index.js 3.17 kB 0 B
build/edit-navigation/style-rtl.css 1.02 kB 0 B
build/edit-navigation/style.css 1.02 kB 0 B
build/edit-post/style-rtl.css 5.5 kB 0 B
build/edit-post/style.css 5.5 kB 0 B
build/edit-site/style-rtl.css 3.03 kB 0 B
build/edit-site/style.css 3.03 kB 0 B
build/edit-widgets/style-rtl.css 2.43 kB 0 B
build/edit-widgets/style.css 2.43 kB 0 B
build/editor/editor-styles-rtl.css 468 B 0 B
build/editor/editor-styles.css 469 B 0 B
build/editor/style-rtl.css 3.8 kB 0 B
build/editor/style.css 3.8 kB 0 B
build/element/index.js 4.65 kB 0 B
build/escape-html/index.js 733 B 0 B
build/format-library/style-rtl.css 542 B 0 B
build/format-library/style.css 543 B 0 B
build/hooks/index.js 2.13 kB 0 B
build/html-entities/index.js 622 B 0 B
build/i18n/index.js 3.56 kB 0 B
build/is-shallow-equal/index.js 711 B 0 B
build/keyboard-shortcuts/index.js 2.51 kB 0 B
build/keycodes/index.js 1.94 kB 0 B
build/list-reusable-blocks/index.js 3.12 kB 0 B
build/list-reusable-blocks/style-rtl.css 446 B 0 B
build/list-reusable-blocks/style.css 447 B 0 B
build/media-utils/index.js 5.29 kB 0 B
build/notices/index.js 1.79 kB 0 B
build/nux/index.js 3.4 kB 0 B
build/nux/style-rtl.css 662 B 0 B
build/nux/style.css 657 B 0 B
build/plugins/index.js 2.56 kB 0 B
build/primitives/index.js 1.5 kB 0 B
build/priority-queue/index.js 788 B 0 B
build/rich-text/index.js 14 kB 0 B
build/server-side-render/index.js 2.68 kB 0 B
build/shortcode/index.js 1.7 kB 0 B
build/token-list/index.js 1.28 kB 0 B
build/url/index.js 4.06 kB 0 B
build/viewport/index.js 1.85 kB 0 B
build/warning/index.js 1.14 kB 0 B
build/wordcount/index.js 1.17 kB 0 B

compressed-size-action

<Root aria-hidden="true" className={ classes } ref={ ref } { ...props }>
{ resizeListener }
<Label
aria-hidden={ props[ 'aria-hidden' ] }
Copy link
Author

Choose a reason for hiding this comment

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

We have to pass this through manually to apply to the inner label element. The reason is because the inner label is Portal'ed, which means it will not inherit the aria-hidden attribute from the <Root /> component.

@MichaelArestad
Copy link
Contributor

This is awesome. I notice there is some strange behavior on FF. It's not sticking to the cursor. It seems the position is only updating when I pause for a second.

2020-06-10 15 19 36

As far as the design and functionality, this is pretty great. I think sticking it to the cursor more useful because my eye is already looking there.

Once that minor bug is fixed, I can see it being useful for all sorts of things. Nice work.

@ItsJonQ
Copy link
Author

ItsJonQ commented Jun 10, 2020

@MichaelArestad Oooo! Thank you for verifying!! I'll take a look on Firefox 🦊

@ItsJonQ
Copy link
Author

ItsJonQ commented Jun 10, 2020

@MichaelArestad Can confirm your experience. Hmm! It's so weird...

On Firefox, the tooltip can follow my mouse just fine. However, when I drag to resize (using the ResizableBox), that's when it goes weird.

It may be something funky in that ResizableBox library. Will keep tinkering!

@MichaelArestad
Copy link
Contributor

It may be something funky in that ResizableBox library. Will keep tinkering!

Funky. Thanks for confirming.

@ItsJonQ
Copy link
Author

ItsJonQ commented Jun 10, 2020

@MichaelArestad I think I did it!

Made some minor tweaks.. The biggest one was binding the mousemove handler to document instead of window. Who would have known 🤷‍♀️

@MichaelArestad
Copy link
Contributor

@MichaelArestad I think I did it!

@ItsJonQ Nice! Can confirm! However, I have a new bug for you. I have to click the spacer drag handle twice before it the measurement shows up. I have to do this for every spacer I add to the same document.

@ItsJonQ
Copy link
Author

ItsJonQ commented Jun 11, 2020

@MichaelArestad Thank you!!

@mcsf 's feedback helped me rethink + optimize things 😉

Comment on lines 147 to 149
document.removeEventListener( 'mousedown', handleOnMouseDown );
document.removeEventListener( 'mousemove', handleOnMouseMove );
document.removeEventListener( 'mouseup', handleOnMouseUp );
Copy link
Contributor

Choose a reason for hiding this comment

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

Just looking at the code, I don't think this cleanup ever does anything. That's because the handle* functions are re-instantiated every time the effect runs.

I don't why I said this, as it didn't make sense: the registered listeners are properly referenced in the tear-down function returned by the effect, so we should always be able to remove the old event listeners.

Comment on lines 147 to 149
document.removeEventListener( 'mousedown', handleOnMouseDown );
document.removeEventListener( 'mousemove', handleOnMouseMove );
document.removeEventListener( 'mouseup', handleOnMouseUp );
Copy link
Contributor

Choose a reason for hiding this comment

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

Let me know what you think of these revisions

So I've been going back and forth looking at this effect and feeling generally unsatisfied when pondering things like the dependencies, and I think I know why. We're dealing with effects of first degree and effects of second degree, so to speak: the first-degree effects are the registration of the mouse* event listeners; the second-degree ones are the listeners themselves. This makes the reasoning about dependencies more confusing.

If we were stricter about constructing these effects, we might refactor this as follows:

export function useResizeLabel(  ) {
  // ...

  const clearMoveTimeout = useCallback( () => {
    if ( moveTimeoutRef.current ) {
      clearTimeout( moveTimeoutRef.current );
    }
  }, [ moveTimeoutRef ] );

  const handleOnMouseDown = useCallback( () => {
    if ( ! isCursor ) return;

    setIsDragging( true );
    clearMoveTimeout();
    unsetMoveXY();
  }, [
    // meaningful dependencies (mutable)
    isCursor,
    // formal dependencies (assumed constant)
    setIsDragging, clearMoveTimeout, unsetMoveYX,
  ] );

  const handleOnMouseMove = useCallback( /* same as above */ );

  const handleOnMouseUp = useCallback( /* same as above */ );
  
  useEffect( () => {
    document.addEventListener( 'mousedown', handleOnMouseDown );
    return () => {
      document.removeEventListener( 'mousedown', handleOnMouseDown );
    };
  }, [ handleOnMouseDown ] );

  useEffect( /* same as above for `mousemove` */ );

  useEffect( /* same as above for `mouseup` */ );

  return { ... };
}

Note those comments about "meaningful" vs. "formal" dependencies. That was my of not making a decision as to whether we should be a strict as possible when listing dependencies or not. :) I'll leave that up to you.

@shaunandrews
Copy link
Contributor

Love this. I do wonder about the capitalization of "PX". Maybe its just the size of that unit's label, but I wonder if lowercase ("px") would be better? I guess its mostly personal preference.

@ItsJonQ
Copy link
Author

ItsJonQ commented Jun 11, 2020

@shaunandrews + @MichaelArestad

Lowecase px

Screen Shot 2020-06-11 at 2 37 24 PM

Screen Capture on 2020-06-11 at 14-37-19

Vote with 👍 / 👎

Thank you!

@ItsJonQ
Copy link
Author

ItsJonQ commented Jun 11, 2020

Thanks @MichaelArestad ! Lowercase it is :)

@shaunandrews
Copy link
Contributor

Do we use units like this elsewhere? What does padding do?

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.

Looking at ResizeTooltop, what if we didn't have cursor as a possible position? Might not we get by with either bottom or corner?

I ask for a couple of reasons. I think there is a difficult balance to find between working "one need at a time", and building components that aim to be framework-ready (customisable, reusable) from the start. My personal impression from this PR is that we may be leaning too much towards the latter. I'd like to start off with less: fewer modes, fewer overrides* (e.g. do we need to pass zIndex? maybe we do, but I'd like to know the reasoning).

The other thing is that it would be good to have a baseline — in terms of UX, of performance, of any such criterion — against which to measure any improvements. In this case, we would err on the side of simplicity, both in the complexity of the implementation and in the richness of the interactions, by only adding one or two of the tool-tip positioning modes. That way, any subsequent PR to improve or expand would give us much more insight on what works best.

*: I always worry about options and overrides because they become interfaces to mind when thinking about backwards compatibility in the present and in the future.

@ItsJonQ
Copy link
Author

ItsJonQ commented Jun 16, 2020

@mcsf Those are fair points. I've removed the "cursor" position from the ResizeTooltip. I've adjusted some logic and styles to improve the integration of bottom position.

We can revisit adding a cursor positioned Tooltip in the future, if needed 👍

@mcsf
Copy link
Contributor

mcsf commented Jun 16, 2020

@mcsf Those are fair points. I've removed the "cursor" position from the ResizeTooltip. I've adjusted some logic and styles to improve the integration of bottom position.

We can revisit adding a cursor positioned Tooltip in the future, if needed 👍

Thanks for humouring me!

@ItsJonQ
Copy link
Author

ItsJonQ commented Jun 16, 2020

Thanks for humouring me!

Anytime 😉 ❤️

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.

Everything is very detailed and carefully laid out, and I appreciate that.

I did have a question about how to fit ResizeTooltop in ResizableBox, though (the position I tend to come from is to minimise how much is exposed at first, and see over time what should be opened up).

Comment on lines 81 to 85
<ResizeTooltip
axis="y"
isVisible={ isResizing }
position="bottom"
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

Have you considered rendering actually in ResizableBox? If there are reasons to keep it separate, do let me know. Otherwise, it might be a boon in developer experience that ResizableBox could handle everything related to resizing on its own.

I can't help but think that axis and position can be derived from ResizableBox's enable and to some extent size props. If we were to move Tooltip into Box the consumer of the component wouldn't have to worry about such extra props.

Copy link
Author

Choose a reason for hiding this comment

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

@mcsf I have considered putting it in ResizableBox :). That's how this idea started out.
The reason I thought of keeping it independent was for added flexibility. That is, to show dimensions on resize that aren't triggered by ResizableBox. For example, browser window resize.

To start, we could have this be a default (experimental) feature of ResizableBox. Something like <ResizableBox __experimentalShowTooltip={true} /> would show the Tooltip.

What do you think? :)

Copy link
Contributor

Choose a reason for hiding this comment

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

The reason I thought of keeping it independent was for added flexibility. That is, to show dimensions on resize that aren't triggered by ResizableBox. For example, browser window resize.

Hm, I could see that as an interaction, although personally I would wonder when we actually expect users to resize their proper window in a context where we'd show the tool tip. :)

So I think we're still in this territory: "the position I tend to come from is to minimise how much is exposed at first, and see over time what should be opened up". Internally, ResizeTooltip is a good component to build separately, but I would keep it private to our components.

To start, we could have this be a default (experimental) feature of ResizableBox. Something like <ResizableBox __experimentalShowTooltip={true} /> would show the Tooltip.

I'd be happy to see that experiment. :) Don't feel like you need to do it in this PR, though, as long as we keep the right things private/experimental.

Copy link
Author

Choose a reason for hiding this comment

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

Internally, ResizeTooltip is a good component to build separately, but I would keep it private to our components.

@mcsf Sounds good to me 👍 . With this PR, in it's current state, does exporting it as __experimental qualify? Or would it be better to integrate it into ResizableBox and not export it at all?

Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

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

in it's current state, does exporting it as __experimental qualify? Or would it be better to integrate it into ResizableBox and not export it at all?

If you don't mind too much, integrating in ResizableBox and not exporting would be the best, IMO!

Copy link
Author

Choose a reason for hiding this comment

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

@mcsf Alrighty! Let's go with that for now :)

@ItsJonQ
Copy link
Author

ItsJonQ commented Jun 18, 2020

@mcsf I just moved the ResizeTooltip under ResizableBox. Lemme know if this works! <3

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.

Assuming the manifest thing is handled, LGTM! Nice work, and thanks for the ping-ponging!

@@ -1022,7 +1022,7 @@
{
"title": "ResizeTooltip",
"slug": "resize-tooltip",
"markdown_source": "../packages/components/src/resize-tooltip/README.md",
"markdown_source": "../packages/components/src/resizable-box/resize-tooltip/README.md",
Copy link
Contributor

Choose a reason for hiding this comment

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

Should no longer be in the manifest, right?

Copy link
Author

Choose a reason for hiding this comment

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

I'm... not sure. This was auto generated. There's a README.md inside the directory. I guess I can remove it? Ideally I'd like to preserve the docs though.

An alternative would be to inline comment some of the documentation (for now)

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm... yeah, I see now. Well, I don’t have a preference. Maybe @nosolosw has a different take on how we should document components and let these tools work for us? Anyway, LGTM :)

Copy link
Member

Choose a reason for hiding this comment

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

I took a peek at how the script that processes the handbook docs works. It seems that it looks for README files within the components folder (see) and then updates the manifest used to expose them in the handbook (see ResizableBox page).

I'm unfamiliar with how we deal with subcomponents that aren't exposed via the @wordpress/components package. I had a quick look and this seems to be the only case? Anyway, it seems that we should either expose the resize-tooltip as a component proper or look at ways to remove its page from the handbook as it can't be used by external consumers of the API. For the latter, we could go with a quick hack (use a lowercased readme, use any other name for the file) or fix the issue (update the glob pattern to only match README files within the top-level component folder).

@ItsJonQ
Copy link
Author

ItsJonQ commented Jun 22, 2020

Alrighty! Travis is green and happy. Thanks for the review @mcsf !!

I'll merge it up

@ItsJonQ ItsJonQ merged commit 03a4f6c into master Jun 22, 2020
@ItsJonQ ItsJonQ deleted the add/resize-visualizers branch June 22, 2020 15:41
@github-actions github-actions bot added this to the Gutenberg 8.5 milestone Jun 22, 2020
@mcsf
Copy link
Contributor

mcsf commented Jun 22, 2020

🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Block] Spacer Affects the Spacer Block [Feature] Design Tools Tools that impact the appearance of blocks both to expand the number of tools and improve the experi [Package] Components /packages/components [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants