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

UnitControl: Improve interactions + add unit parsing #22329

Merged
merged 18 commits into from
May 20, 2020

Conversation

ItsJonQ
Copy link

@ItsJonQ ItsJonQ commented May 13, 2020

Description

Screen Capture on 2020-05-13 at 13-21-43

(GIF: Demo of dragging UnitControl to update Cover height)

This update adds rich features and interactions to the UnitControl component. Originally, this work was in support of adding padding controls to the Cover block. This update abstracts just the UnitControls from that effort.

Notable features for UnitControl include:

  • Drag (mouse) to update values
  • Unit parsing from input. Example: 10px is parsed and understood as [10, 'px']
  • Improved step increment calculations (holding Shift when updating)

UnitControl and NumberControl are now powered by a new lower level primitive: InputControl.

InputControl handles the majority of the interaction and event logic. Components using it, like NumberControl, are able to tap into the state flow and add features like "drag to update".

Notable features for InputControl include:

  • Floating label UI interaction - Not enabled by default
  • Styled with G2 designs, out of the box

How has this been tested?

  • Tested in Storybook
  • Tested in local Gutenberg
  • Tested in Jest (via unit tests)

How to test?

Storybook

  • Run npm install to download dependencies
  • Fire up storybook with npm run storybook:dev
  • Search for unit control
  • Play around with the component to ensure it works as expected

Gutenberg

  • Run npm install to download dependencies
  • Fire up local development with npm run dev
  • Add a Cover block
  • Adjust the height. Ensure it works as expected (Note: Unit parsing feature is not enabled. This would involve updating the Cover block component a bit)

Types of changes

  • Adding experimental components

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.

@github-actions
Copy link

github-actions bot commented May 13, 2020

Size Change: +9.98 kB (0%)

Total Size: 1.12 MB

Filename Size Change
build/block-directory/index.js 6.93 kB +1 B
build/block-editor/index.js 105 kB +1 B
build/components/index.js 190 kB +9.98 kB (5%) 🔍
build/data/index.js 8.42 kB -6 B (0%)
build/edit-site/index.js 12.8 kB +1 B
build/editor/index.js 44.3 kB +2 B (0%)
build/element/index.js 4.65 kB -1 B
build/redux-routine/index.js 2.85 kB -1 B
ℹ️ View Unchanged
Filename Size Change
build/a11y/index.js 1.02 kB 0 B
build/annotations/index.js 3.62 kB 0 B
build/api-fetch/index.js 3.39 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 790 B 0 B
build/block-directory/style.css 791 B 0 B
build/block-editor/style-rtl.css 10.8 kB 0 B
build/block-editor/style.css 10.8 kB 0 B
build/block-library/editor-rtl.css 7.17 kB 0 B
build/block-library/editor.css 7.17 kB 0 B
build/block-library/index.js 119 kB 0 B
build/block-library/style-rtl.css 7.48 kB 0 B
build/block-library/style.css 7.48 kB 0 B
build/block-library/theme-rtl.css 683 B 0 B
build/block-library/theme.css 685 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/blocks/index.js 48.1 kB 0 B
build/components/style-rtl.css 17.1 kB 0 B
build/components/style.css 17.1 kB 0 B
build/compose/index.js 9.24 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 568 B 0 B
build/dom/index.js 3.11 kB 0 B
build/edit-navigation/index.js 6.6 kB 0 B
build/edit-navigation/style-rtl.css 857 B 0 B
build/edit-navigation/style.css 856 B 0 B
build/edit-post/index.js 302 kB 0 B
build/edit-post/style-rtl.css 12.2 kB 0 B
build/edit-post/style.css 12.2 kB 0 B
build/edit-site/style-rtl.css 5.22 kB 0 B
build/edit-site/style.css 5.22 kB 0 B
build/edit-widgets/index.js 7.73 kB 0 B
build/edit-widgets/style-rtl.css 4.59 kB 0 B
build/edit-widgets/style.css 4.59 kB 0 B
build/editor/editor-styles-rtl.css 425 B 0 B
build/editor/editor-styles.css 428 B 0 B
build/editor/style-rtl.css 5.07 kB 0 B
build/editor/style.css 5.08 kB 0 B
build/escape-html/index.js 733 B 0 B
build/format-library/index.js 7.64 kB 0 B
build/format-library/style-rtl.css 502 B 0 B
build/format-library/style.css 502 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.13 kB 0 B
build/list-reusable-blocks/style-rtl.css 226 B 0 B
build/list-reusable-blocks/style.css 226 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 616 B 0 B
build/nux/style.css 613 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 789 B 0 B
build/rich-text/index.js 14.8 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.02 kB 0 B
build/viewport/index.js 1.84 kB 0 B
build/warning/index.js 1.14 kB 0 B
build/wordcount/index.js 1.17 kB 0 B

compressed-size-action


### isFloatingLabel

If true, the `label` will render with a floating interaction.
Copy link
Member

Choose a reason for hiding this comment

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

This description seems a bit vague; even I'm not entirely sure what it means. I assume it means the label appears inside the input until the input is focused? Also, is this prop even a good idea, accessibility-wise?

Also, all this type info belongs in a JSDoc comment, doesn't it?

Copy link
Author

Choose a reason for hiding this comment

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

Thanks for noticing this! I'm not sure how best to describe this interaction/design.

It works like the TextFields from Material UI:
https://material-ui.com/components/text-fields/

From a design perspective, it offers more versatility, especially if controls need to be combined in cramped spaces.

As for as accessibility goes, how its setup, I believe it it acceptable from both a motion perspective and aria label + HTML markup perspective.

cc'ing some a11y folks!

@enriquesanchez / @diegohaz Are there any concerns for having floating labels in Inputs like these?

Screen Shot 2020-05-14 at 3 07 28 PM

https://material-ui.com/components/text-fields/

Thank you!

Copy link
Member

Choose a reason for hiding this comment

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

I don't have strong opinions about that. But there's this article with arguments against floating labels: Floating labels are problematic

And this other one is a response to that: Are float labels really that problematic after all?

Copy link
Contributor

Choose a reason for hiding this comment

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

I always check Adam Silver's articles when in doubt about forms and a11y. That first link Diego shared is from him :)

  • Sorry if this is obvious, but where is this floating label going to be used? Only on this control or on all input fields?
  • I don't see the floating label in the gif example above, but how would it look like when the floating label is longer than the field itself?

Copy link
Author

Choose a reason for hiding this comment

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

@diegohaz + @enriquesanchez Thanks for replying!

where is this floating label going to be used? Only on this control or on all input fields?

It's situational! By default, floating labels are disabled. They're useful if the input is narrower or used in compact spaces. For example, within a smaller Popover.

but how would it look like when the floating label is longer than the field itself?

Ah! the GIF above does not have a floating label. The best example of this interaction would be from the Material TextField demo:

https://material-ui.com/components/text-fields/

My screen capture -> GIF software doesn't do a good job at capturing the various stages.

The key frames work like this ✨

No value, label works like a placeholder:

Screen Shot 2020-05-19 at 12 45 31 PM

No value, focused, the label floats up:

Screen Shot 2020-05-19 at 12 46 20 PM

With value, the label stays up:
Screen Shot 2020-05-19 at 12 46 27 PM

Copy link
Author

Choose a reason for hiding this comment

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

Ah! Gotcha. I misread. My apologies. There isn't handling of this at the moment.

The Material TextField doesn't handle this use case too well either:
https://codesandbox.io/s/material-demo-e49dz

I can try some truncation techniques really quickly. I don't think this should be a blocker though.

Copy link
Member

Choose a reason for hiding this comment

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

The big issue is that input labels have to be translated, and some languages will use much longer words than others. An input with a floating label of "left" might be correctly sized, but once you translate that to another language, it may suddenly be twice as long.

WordPress generally prefers decisions over options. If there's not a really good reason to provide an alternate input style, we shouldn't provide it. I think it's okay to merge this PR since the InputControl will be experimental, but I don't think we should change the component to stable until we know whether the floating label options should be kept or not.

Copy link
Author

Choose a reason for hiding this comment

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

Screen Capture on 2020-05-19 at 13-52-02

This is the best I could do given the (surprisingly) limited CSS rendering control over <fieldset>.

I don't think we should change the component to stable until we know whether the floating label options should be kept or not

Yup! I agree. We can also drop the floating label feature if it's causing issues.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks @ItsJonQ! I was trying to make a point with my question, apologies if it wasn't clear 😃.

I feel that at this point there's some uncertainty re: floating labels (a11y, localization, placement, etc.) that my recommendation is to stick to regular ol' fashioned text labels.

Copy link
Author

Choose a reason for hiding this comment

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

I feel that at this point there's some uncertainty re: floating labels (a11y, localization, placement, etc.) that my recommendation is to stick to regular ol' fashioned text labels.

I agree! Ol' fashion text labels are default 😊

The floating labels are a new feature, which can be experimentally trialed in certain scenarios

/**
* External dependencies
*/
import { noop } from 'lodash';
Copy link
Member

Choose a reason for hiding this comment

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

I've seen this code pattern used in several places, but given that we can now use optional chaining, I think that instead of doing this, we could just let the functions be undefined by default. I'm just guessing (and could be completely wrong about this), but it seems like that would perform slightly better. To avoid calling functions that are undefined, we would use optional chaining like so:

onBlur?.( event )`

Again, this is just an idea; it probably doesn't matter much either way.

Copy link
Author

Choose a reason for hiding this comment

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

Interesting idea! I thought about it for a bit (considering optional chaining is a new thing)

From my experience in the React library/module space, I've found it smoother for callbacks to fire without guards. It kept the code tidier and there was less to think about.

That being said, optional chaining does tidy it up a lot.

I'm perhaps biased in that I still prefer noop callbacks.
I'm open to using optional chaining instead, if that's what others prefer 👍

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.

There's a lot to unpack here and I've barely scratched the surface, I feel! That said, in terms of UX this seems pretty solid, and I think there are some good overall patterns in the implementation.

packages/components/src/unit-control/utils.js Show resolved Hide resolved
packages/components/src/unit-control/utils.js Outdated Show resolved Hide resolved
packages/components/src/unit-control/utils.js Show resolved Hide resolved
packages/components/src/input-control/README.md Outdated Show resolved Hide resolved
packages/components/src/input-control/state.js Outdated Show resolved Hide resolved
packages/components/src/input-control/state.js Outdated Show resolved Hide resolved
@ItsJonQ
Copy link
Author

ItsJonQ commented May 20, 2020

Thanks for the feedback all! I'll merge this once Travis is green and happy ✨

@@ -61,7 +61,7 @@ export function parseUnit( initialValue, units = CSS_UNITS ) {
unit = unit.toLowerCase();

if ( hasUnits( units ) ) {
const match = units.find( ( item ) => item.value === unit );
const match = keyBy( units, 'value' )[ unit ];
Copy link
Contributor

@mcsf mcsf May 20, 2020

Choose a reason for hiding this comment

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

@ItsJonQ: Ah, I wasn't clear, but I meant that a keyed version of units could be kept where it can be reused — so possibly as a module constant. Otherwise we're still iterating over the collection (and in this case doing extra work) every time!

Copy link
Author

Choose a reason for hiding this comment

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

@mcsf Ah, gotcha! It's a little tricky, as the units coming in may not be constant. Ultimately, this is determined by the add_theme_support config within the context of Gutenberg.

We could memoize it? I'm wondering if it's worth it, considering this operation is quite small (within the grand scheme of things)

Copy link
Contributor

Choose a reason for hiding this comment

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

We could memoise, yeah, but I don't want to take more of your time on this now, so I suggest reverting to the initial array and find situation, and we can improve from there. :)

Copy link
Author

Choose a reason for hiding this comment

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

Sounds good! Thank you @mcsf !!

Jon Q added 6 commits May 20, 2020 14:28
This update adds rich features and interactions to the UnitControl component.
Originally, this work was in support of adding padding controls to the Cover block.
This update abstracts just the UnitControls from that effort.

UnitControl and NumberControl are now powered by a new lower level primitive: InputControl.

InputControl handles the majority of the interaction and event logic. Components using it, like NumberControl, are able to tap into the state flow and add features like "drag to update".
@ItsJonQ
Copy link
Author

ItsJonQ commented May 20, 2020

Rebasing with latest master

@ItsJonQ ItsJonQ closed this May 20, 2020
@ItsJonQ ItsJonQ reopened this May 20, 2020
@ItsJonQ
Copy link
Author

ItsJonQ commented May 20, 2020

Accidentally clicked "Close and comment" 🙈

@ItsJonQ
Copy link
Author

ItsJonQ commented May 20, 2020

Hmm... looks like Travis has been failing the E2E (not just this PR, but for others as well)

@ItsJonQ
Copy link
Author

ItsJonQ commented May 20, 2020

Yay! Travis is happy! Merging :D

@ItsJonQ ItsJonQ merged commit 6efa61c into master May 20, 2020
@ItsJonQ ItsJonQ deleted the try/improve-unit-control branch May 20, 2020 19:38
@github-actions github-actions bot added this to the Gutenberg 8.2 milestone May 20, 2020
@ItsJonQ
Copy link
Author

ItsJonQ commented May 20, 2020

Thanks again everyone! I can't wait to use these 🎉

@afercia
Copy link
Contributor

afercia commented Sep 12, 2020

Late to the party. I came across this while testing the new Navigation page.

This is the best I could do given the (surprisingly) limited CSS rendering control over <fieldset>.

I'm not sure I understand why a fieldset + legend elements are used for this pattern. They're semantic elements meant to group logically-related form controls. The group name is given by the legend element. Instead, in this case:

  • both the fieldset and the legend elements have an aria-hidden=true attribute so they're hidden from assistive technologies, thus defeating the elements purpose to be semantic elements
  • in any case, the fieldset does not group any form element: it's an empty fieldset
  • to my understanding, the fieldset is absolutely positioned over the form element in question and uses pointer-events: none; to allow click-throughs
  • at this point why use a fieldset element in the first place? It could be a simple div element
  • if there's no <label> element correctly associated to the input, or an aria-label, the input will be unlabelled even if there's some "visible text": all form controls need to be properly labelled
  • lastly, (noticed while testing the new Navigation page), it seems the fieldset is always rendered even when there's no need for it, see the screenshot below:

Screenshot 2020-09-12 at 13 53 35

This is confusing: why we should render a fieldset when it's not needed?

@ItsJonQ can you please shed some light on the reason why a fieldset + legend elements are used here?

Regarding the "floating labels" pattern: in general, anything that "moves" on the screen without user activation is potentially confusing and triggers cognitive load. I'm not sure this pattern should be used without an in-depth discussion between the accessibility and design teams.

In any case, there are better ways to implement "floating labels" that make sure the input is labelled. See for example what GMail does:

floating label gmail visible text and aria label 02

where it's of fundamental importance that the visible text matches the input aria-label.

@ItsJonQ
Copy link
Author

ItsJonQ commented Sep 14, 2020

This is confusing: why we should render a fieldset when it's not needed?

@afercia Hi there! Thank you for digging in!

I had used fieldset as it enabled the borders to render correctly when "floating" the label. I had followed the structure of other examples to achieve this affect. However, as you had noted, there appears to be a better methods (I need to poke at Google/Gmail to figure it out). The current implementation is not worth the degradation of a11y for inputs.

I'll follow up with improvements on this as soon as I can. Would it be okay if I @mention you on the PR (when it's available)?

@afercia
Copy link
Contributor

afercia commented Sep 14, 2020

Would it be okay if I @mention you on the PR (when it's available)?

Sure. I'd consider to also post a quick heads up on the Slack accessibility channel so that if I'm not available, someone else can provide feedback. Thanks! ❤️

@ItsJonQ
Copy link
Author

ItsJonQ commented Sep 14, 2020

@afercia Will do! Will also be sure to tag the PR with "Needs Accessibility Feedback"

@afercia
Copy link
Contributor

afercia commented Sep 14, 2020

I just noticed that now that the styles from input-control-styles are applied to some <label> elements, weird things happen as in: clicking the label doesn't focus the associated form element. Will create a separate issue...

@ItsJonQ
Copy link
Author

ItsJonQ commented Sep 14, 2020

I just noticed that now that the styles from input-control-styles are applied to some elements, weird things happen as in: clicking the label doesn't focus the associated form element. Will create a separate issue...

@afercia I noticed this too when working on the fix. This has been resolved! (Good timing, I'm writing the PR as we speak!)

@afercia
Copy link
Contributor

afercia commented Sep 14, 2020

Ah good! I was just writing the new issue :D Too many pointer-events: none;, right? Will wait then and check your PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Package] Components /packages/components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants