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

RangeControls: Improve UI + UX #19916

Merged
merged 34 commits into from
Feb 13, 2020
Merged

Conversation

ItsJonQ
Copy link

@ItsJonQ ItsJonQ commented Jan 27, 2020

Description

Screen Shot 2020-01-27 at 11 42 09 AM

This update improves the UI and UX for the RangeControl component with @wordpress/components.

Features ✨

  • Visual tooltip that displays current value (Tooltip). Follows the "thumb" (circle)
  • Larger hitbox, making it easier to click/drag
  • More consistent UI between slider, input, and Reset button
  • Renders step markers with a simple + custom prop (marks)
  • Tooltip auto positions top or bottom, based on ideal rendering position
  • Can easily change the color of the "track" using a color prop
  • Can customize the tooltip's content with a custom renderProp
  • Ability to show/hide the accompanying input[type="number"]

Example of step marks + Tooltip rendering:

A11Y Considerations 💪

  • Uses native input[range] under the hood, which gives us native keyboard interaction handling
  • Uses aria roles on elements to emphasize changes / reduce noise (via aria-hidden)
  • Respects reduce-motion to disable all animations

Other Considerations ✌️

Screen Shot 2020-01-27 at 12 25 03 PM

  • RTL support
  • README file has been updated with additive API changes

How has this been tested?

This update was developed within Storybook. When ready, it was integrated and tested with Gutenberg, local dev.

Voiceover

This component was tested with Mac OS Voiceover, to ensure the value change announcements + keyboard interactions worked as expected.

Mobile

IMAGE 2020-01-27 11:35:06

Although, this component only updates the Web version, the changes were tested on both iOS (iPhone 8) and Android (Samsung A50)

iPhone 8 Screen Recording

Note: The inputMode for the number input was set to decimal, which allows for the numpad keyboard to appear for mobile.

Types of changes

CSS

The CSS for the updated component was written with Emotion, leveraging the new/experimental CSS-in-JS system.

Tooltip

The Tooltip that appears to indicate the value is not a @wordpress/components one, but rather, a simple/custom implementation.

Reason

I had originally tried using the Popover/Tooltip from @wordpress/components, but this led to (perceived) performance rendering issues:

https://d.pr/i/a6i3R4

The positioning render cycle was not in sync with the quick changes, which made the experience look and feel really laggy.

As a work-around, I created a simple Tooltip which is currently only being used in the new RangeControl component. (I'm open to changing this approach if anyone has suggestions)

Backwards Compatibility

This update preserves all of the Component APIs. Therefore, it shouldn't break backwards compatibility. It passes all of the previous unit tests and appears to be working as expected when testing within Gutenberg. Component API changes are additive.

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. .

Resolves: #19845

@ItsJonQ ItsJonQ added the [Focus] Accessibility (a11y) Changes that impact accessibility and need corresponding review (e.g. markup changes). label Jan 27, 2020
@karmatosed
Copy link
Member

This is great! A few pieces of feedback to talk about and then happy to review.

  • Could we have no shadow perhaps as I'm not sure we had before on the circle?
  • Are we able to increase the size of the arrows on input, I would love that to be bigger.

@ItsJonQ ItsJonQ force-pushed the try/range-controls-ui-update branch from 03ac853 to 66c86e6 Compare January 29, 2020 14:24
@ItsJonQ
Copy link
Author

ItsJonQ commented Jan 29, 2020

@karmatosed Haii!

Could we have no shadow perhaps as I'm not sure we had before on the circle?

Shadow removed. In doing so, I noticed that there was barely any visual feedback to indicate a focus state. I've adjusted it so that the circle has a blue ring (similar to input fields) when focused:

Screen Shot 2020-01-29 at 9 06 58 AM

Are we able to increase the size of the arrows on input, I would love that to be bigger.

That input field is currently using an out of the box number input. How they render is based on your browser x OS.

To make the arrows bigger, we'd need to do quite a bit of custom UI/CSS work. In which case, I'd advise perhaps updating the base TextControl to support this (in a separate PR).

Hope that makes sense!

@@ -0,0 +1,8 @@
// Keeping this className style rule as it is currently being
// used OUTSIDE of the RangeControl component.
Copy link
Contributor

Choose a reason for hiding this comment

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

Bad :)

Copy link
Contributor

Choose a reason for hiding this comment

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

I did a quick search and I noticed that it's only used in FontSizePicker.
Maybe we can just move the style there and rename the class to follow our guidelines components-font-size-picker__number

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 to me! I'll do that now

Copy link
Contributor

@youknowriad youknowriad left a comment

Choose a reason for hiding this comment

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

LGTM 👍 Thanks for your hard work here.

Rename rangeControl related ID and classNames to match the structure
of FontSizePicker
@ItsJonQ
Copy link
Author

ItsJonQ commented Feb 6, 2020

@youknowriad

Module cssjanus has an incompatible license 'Apache-2.0'

Aww. Does that mean we can't use this package?

@ZebulanStanphill
Copy link
Member

Just to clarify, Apache 2.0 is compatible with GPLv3, but not earlier versions of the GPL. WordPress is GPLv2+ (which means it could legally switch to GPLv3+ to use this library, but dropping GPLv2 compatibility is probably not a reasonable option here).

If you really want to use this particular library, you could ask the library contributors (there are 12 of them) to consider relicensing or dual-licensing under a license compatible with GPLv2+.

@ItsJonQ
Copy link
Author

ItsJonQ commented Feb 6, 2020

@ZebulanStanphill Thanks for your insight! I'm going to explore alternative solutions now

@ItsJonQ
Copy link
Author

ItsJonQ commented Feb 6, 2020

Updates! I took a look at various solutions, and nothing seemed to feel right. I've opted to remove cssjanus, and wrote a tiny albeit naively simple and basic utility.

cc'ing @youknowriad and @aduth for thoughts!

(Sidenote: My Regex game is quite weak 😂 . Please feel free to improve the implementation!)

It's definitely not as seamless as cssjanus or the postcss auto-generated workflow.
I can see this working short term... but would definitely love something better for the long term.

@youknowriad
Copy link
Contributor

Looks like there's some valid e2e test failures.

@ItsJonQ
Copy link
Author

ItsJonQ commented Feb 7, 2020

@youknowriad Ah. Looks like I need to update the E2E selectors for FontSizePicker, since we renamed them

By updating the className for the input number field
@ItsJonQ
Copy link
Author

ItsJonQ commented Feb 7, 2020

Yay! E2E is fixed 😍

Comment on lines +181 to +182
value: 0,
label: '0',
Copy link
Member

Choose a reason for hiding this comment

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

If it's a common use case that the label would be the same as value, could we make it optional and fall back to a stringified value?

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 if it's common yet :).
I can see cases where you may want to render '10%' instead

Comment on lines +276 to +278
- Type: `Boolean`
- Required: No
- Platform: Web
Copy link
Member

Choose a reason for hiding this comment

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

Would be good to describe the default here, since it might be unexpected that the default is true (that it's an opt-out, not an opt-in).

.components-range-control__number {
.components-font-size-picker__number {
display: inline-block;
font-weight: 500;
Copy link
Member

Choose a reason for hiding this comment

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

Why?

currentInput: null,
} ),
] )( RangeControl );
export const RangeControlNext = compose( withInstanceId )( BaseRangeControl );
Copy link
Member

Choose a reason for hiding this comment

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

We could have dropped compose here rather easily, as it really only provides value when composing multiple:

Suggested change
export const RangeControlNext = compose( withInstanceId )( BaseRangeControl );
export const RangeControlNext = withInstanceId( BaseRangeControl );

Comment on lines -14 to +15
import RangeControl from '../';
import RangeControl from '../index';
Copy link
Member

Choose a reason for hiding this comment

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

Why?

top: -4px;
width: 1px;

${markFill};
Copy link
Member

Choose a reason for hiding this comment

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

I see it's not really documented anywhere (other than "when in doubt, space it out"), but: I would expect this should be formatted as:

Suggested change
${markFill};
${ markFill };

@jsnajdr Is this something we should/can integrate into wp-prettier?

Edit: Now I'm confused, because I see the expected spacing in another example below. Is this something that Prettier doesn't have opinions about?

Copy link
Member

Choose a reason for hiding this comment

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

It's a template string with backticks, isn't it? Then Prettier should have an opinion here and should do the `${ spaced }` formatting.

Copy link
Member

Choose a reason for hiding this comment

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

@jsnajdr That's what I would expect as well, but it does not appear to be working correctly.

Copy link
Member

Choose a reason for hiding this comment

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

It looks like a bug in wp-prettier. If the template literal is a styled component, with the styled.span tag, it's treated specially. Thanks for finding and reporting!

Copy link
Member

Choose a reason for hiding this comment

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

I created a wp-prettier fix in this commit: Automattic/wp-prettier@01fd780

A fix was needed for all the template literals that have a recognized format (CSS, GraphQL, HTML...) and where the string itself is being formatted.

I usually release such patches when a new upstream version comes out. In this case, I'll be waiting for prettier@1.19.2.


const TOOLTIP_OFFSET_HEIGHT = 32;

export default function SimpleTooltip( {
Copy link
Member

Choose a reason for hiding this comment

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

Can we create an issue to track this technical debt?

Comment on lines +17 to +39
/**
* An incredibly basic ltr -> rtl converter for style properties
*
* @param {Object} ltrStyles
* @return {Object} Converted ltr -> rtl styles
*/
const convertLtrToRtl = ( ltrStyles = {} ) => {
const nextStyles = {};

for ( const key in ltrStyles ) {
const value = ltrStyles[ key ];
let nextKey = key;
if ( /left/gi.test( key ) ) {
nextKey = [ key.replace( 'left', 'right' ) ];
}
if ( /Left/gi.test( key ) ) {
nextKey = [ key.replace( 'Left', 'Right' ) ];
}
nextStyles[ nextKey ] = value;
}

return nextStyles;
};
Copy link
Member

Choose a reason for hiding this comment

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

Seems like a thing we'd want unit tests for.

I see at least one possible issue: It won't handle anything short-hand that's still relevant for RTL, e.g.

margin: 0 10px 0 0;

Also, I'm confused about the reassignment of nextKey. It's being replaced into an array? But then the array is being used as the key of an object? An array is not a valid key of an object.

At which point: The RegExp#tests are redundant, because String#replace will do nothing if the pattern is not found.

Also: String#replace only replaces the first instance when provided a string, not all instances. You'd want your regular expression /left/gi for that (key.replace( /left/gi, 'right' ).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Focus] Accessibility (a11y) Changes that impact accessibility and need corresponding review (e.g. markup changes). [Package] Components /packages/components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Iteration on range control
6 participants