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

feat(slider): add range slider #14297

Merged
merged 79 commits into from
Oct 18, 2023

Conversation

m4olivei
Copy link
Contributor

@m4olivei m4olivei commented Jul 24, 2023

Closes #14567

Enhancement to the current React Slider component, allowing for a range selection of two values.

Requirements: https://ibm.ent.box.com/notes/1252927738733?s=4s8h59gazpuxv0fyxb1xq1pcxx6qvcwl
Designs: https://www.figma.com/file/Jk3pWNBG6ZWIRoldq2tM1x/Range---Slider-Enhancement?type=design&node-id=425-22406&mode=design&t=7ah75PWKFPtNMLUj-0

Mostly done and ready for review. Things on my radar still to do:

  • Rewrite Slider component to hooks, Will happen in a follow up PR
  • Write tests against the two handle functionality
  • How to internationalize announcement text for when a value is auto-corrected
  • e2e tests
  • Design review

Changelog

New

  • ariaLabelInputUpper optional prop for the Slider component to use as the aria-label attribute for the upper input
  • nameUpper optional prop for the Slider component that serves as the name attribute for the upper input
  • valueUpper optional prop for the Slider component that serves as the default value for the upper value of the range
  • twoHandles prop for the SliderSkeleton component which enhances the SliderSkeleton to be a range slider skeleton

Changed

  • When the new valueUpper prop is set, all of value, name and ariaLabelInput props are applied to the lower bound.
  • The onBlur callback function will provide a handlePosition: HandlePosition key in it's data argument to indicate which input / handle was changed to the given value.
  • The onChange callback function will provide value and valueUpper for when the Slider has two handles. value refers to the lower handle value when the Slider has two handles.
  • The onRelease callback function will provide value and valueUpper for when the Slider has two handles. value refers to the lower handle value when the Slider has two handles.

Testing / Reviewing

Extensive changes were made to the Slider component to accomodate this feature. Thourough regression testing should be done for the Slider with a single handle.

  • Using the Slider > Default story, regression test comparing to the current version.
  • Visit the Slider > Two Handle Slider story.
  • Test dragging each handle. In particular:
    • The handle nearest to the users click/touch on the track should activate and be the handle manipulated
    • The corresponding input should react, updating the value as the user drags
    • The user should not be allowed to go beyond the opposite handle
  • Manipulate the values of the inputs manually or via keyboard arrow keys. Values should update and corresponding handles should update appropriately
  • Test keyboard navigation and focus states.
    • The user should be able to tab focus on each handle
    • When focused on a handle the arrow keys should work to manipulate the handle
    • Holding shift and using the arrow keys should apply a multiplier to move the handle by a greater amount each step
  • Test with a screen reader
    • The screen reader should read each handle as a slider and speak the current value as the handle is moved just like the current single handle Slider.

The Slider > Playground story is the best place for testing various Slider configurations (single and two handle variations).

  • Set a value for valueUpper. This effectively puts the slider in two handle mode.
  • Turn on readOnly. Slider should hide the handles and show the inputs as Read Only. Compare the visual style of the inputs to the Text Input component.

Finally, the Slider Skeleton component has also been updated to support a twoHandles boolean prop.

@netlify
Copy link

netlify bot commented Jul 24, 2023

Deploy Preview for carbon-components-react ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit 7d97253
🔍 Latest deploy log https://app.netlify.com/sites/carbon-components-react/deploys/64bece1f31d1ad0008fde58a
😎 Deploy Preview https://deploy-preview-14297--carbon-components-react.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@netlify
Copy link

netlify bot commented Jul 24, 2023

Deploy Preview for carbon-elements ready!

Name Link
🔨 Latest commit 7d97253
🔍 Latest deploy log https://app.netlify.com/sites/carbon-elements/deploys/64bece1f988ecd0008f7d970
😎 Deploy Preview https://deploy-preview-14297--carbon-elements.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@netlify
Copy link

netlify bot commented Jul 24, 2023

Deploy Preview for v11-carbon-react ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit d95b07c
🔍 Latest deploy log https://app.netlify.com/sites/v11-carbon-react/deploys/652fd63b23a3520008aa5f53
😎 Deploy Preview https://deploy-preview-14297--v11-carbon-react.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@netlify
Copy link

netlify bot commented Jul 24, 2023

Deploy Preview for carbon-elements ready!

Name Link
🔨 Latest commit 8af20a3
🔍 Latest deploy log https://app.netlify.com/sites/carbon-elements/deploys/652edc40104ded0008f7acee
😎 Deploy Preview https://deploy-preview-14297--carbon-elements.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link
Member

@tay1orjones tay1orjones left a comment

Choose a reason for hiding this comment

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

This is awesome! Playing around with it some I see a few nits but they're things already on your list to address (bound events).

If you think it would be helpful, this component is due to be converted from a class-based component to a functional component. If you're interested, this could be a big improvement to readability of the component being able to encapsulate some of the logic into useEffect and useLayoutEffect blocks. No sweat if not, but thought I'd mention it.

@m4olivei
Copy link
Contributor Author

Thanks!

If you think it would be helpful, this component is due to be converted from a class-based component to a functional component. If you're interested, this could be a big improvement to readability of the component being able to encapsulate some of the logic into useEffect and useLayoutEffect blocks. No sweat if not, but thought I'd mention it.

I'm glad you brought that up! I wondered if you all had any such initiative. I'll try to include that as well here as time allows.

@m4olivei m4olivei force-pushed the feat/range-slider branch 3 times, most recently from 448575a to 332b5fb Compare July 31, 2023 19:16
@KevinCamelo
Copy link

KevinCamelo commented Oct 4, 2023

I think you're spot on with your suggestion of a background color @m4olivei, I think it might need a small tweak.

With the addition of a background-color on the entire icon, it leads to a middle indicator that hides and appears arbitrarily. In addition, the background on both sides creates a second line-segment.

Hide and show middle indicator Second line segment
issues-box Screenshot 2023-10-04 at 12 43 25 PM

Instead, I think we could have the background-color only appear on the rail directly inside the icon area on the right-hand side. That way, the middle indicator will always be visible, the rail isn't split into two line-segments, and makes it easier to read what is actually being selected. The same treatment would be provided to the focus state. The color used here would likely be $layer-02 if I'm not mistaken.

Here's what that proposal looks like:
vert2

@m4olivei
Copy link
Contributor Author

m4olivei commented Oct 4, 2023

Yep, makes sense @KevinCamelo . I've implemented that. Take a look. Be sure as well to jump between themes and check RTL to make sure I covered us in those use cases.

@m4olivei
Copy link
Contributor Author

m4olivei commented Oct 5, 2023

Forgot to tag @laurenmrice . I think we've addressed the feedback from your last comment. Could you take a look as well? Let me know if you need any adjustments. If all is well Percy needs to be approved for the visual tweaks we've made with the last few commits.

@mbgower
Copy link

mbgower commented Oct 5, 2023

Looks good! I entered in a letter in an input just to see what would happen and got a placeholder error. Wasn't sure whether that gets resolved elsewhere or we need to have all error messages composed as part of this. I was looking at https://deploy-preview-14297--v11-carbon-react.netlify.app/?path=/story/components-slider--two-handle-slider#storybook-preview-wrapper
image

@mbgower
Copy link

mbgower commented Oct 5, 2023

Actually, one other thing I noticed. The slider label is coded as a label, but it doesn't look like it's associated with the two inputs.
I guess "for" is not great for us here, since the attributed can't take two values.... We need the inputs to expose that label as well as their "lower bound" and "upper bound" info so, if this was a slider on price, you'd want to hear something like "price lower bound" and "price upper bound".
I can think of a few of ways of doing this. I'm sure you can think of more. One would involve a group label (which may only announce on the first input, which I think is okay). The other is to concatenate the two strings. A third may be to use a description for part of the label, effectively getting something like "Upper value, price". And I guess a fourth would be to put a span around the label and reuse it that way.
BTW, the label here is "Slider Label", which at the least should be "Slider label" and better, a realistic text string, like "Price range" or whatever.
I see the more primitive APG 2-thumb slider example just hard-codes the concatenation in an aria-label, which is another way, although as a rule I avoid aria-label

@KevinCamelo
Copy link

Yep, makes sense @KevinCamelo . I've implemented that. Take a look. Be sure as well to jump between themes and check RTL to make sure I covered us in those use cases.

Looking great, Matthew. Went across all themes— just noting that the selected state seems to not have the correct color across themes. The selected rail should use the border-interactive color token to work across themes. The icon as well does not align with the color in the original slider component.

G10/White

Screenshot 2023-10-05 at 10 47 53 AM

G90/100 (incorrect color on rail and icon)
Screenshot 2023-10-05 at 10 49 55 AM

How it should appear:
Screenshot 2023-10-05 at 10 54 07 AM

To match the dark theme of the OG slider:
Screenshot 2023-10-05 at 10 55 18 AM

For the rail: border-interactive
For the icon: interactive upon click.

Additional info on the icon:

From what I can tell, in the base slider component instead of using theicon-interactive, they use the interactive token to achieve the desired recolor for the icon across themes. If you use icon-interactive the interactive state is white, which would not align with the OG slider.

@m4olivei
Copy link
Contributor Author

m4olivei commented Oct 6, 2023

Looks good!

Thanks @mbgower !

I entered in a letter in an input just to see what would happen and got a placeholder error. Wasn't sure whether that gets resolved elsewhere or we need to have all error messages composed as part of this. I was looking at https://deploy-preview-14297--v11-carbon-react.netlify.app/?path=/story/components-slider--two-handle-slider#storybook-preview-wrapper

That gets handled by the user passing a prop, invalidText. The Storybook for the two handle slider passes "Invalid text goes here", it can be customized, but is expected to be general enough to cover any invalid state, of which there are few, given the auto-correction built in.

Actually, one other thing I noticed. The slider label is coded as a label, but it doesn't look like it's associated with the two inputs. I guess "for" is not great for us here, since the attributed can't take two values.... We need the inputs to expose that label as well as their "lower bound" and "upper bound" info so, if this was a slider on price, you'd want to hear something like "price lower bound" and "price upper bound".

Yep. I struggled with this earlier in the development. What I settled on, was that since the single label can't pair to both inputs, I leave the label disconnected (I guess it could be swapped for a <span>). Then I use aria-label on each of the inputs, which is expected to be passed as props (ariaLabelInput and unstable_ariaLabelInputUpper), the Storybook example passes "Lower bound" and "Upper bound" respectively. Using VoiceOver + Chrome, focusing on the input announces "10, Lower bound, stepper", and on the upper input "90, Upper bound, stepper". To acheive your example, you'd simply pass "price upper bound" and "price lower bound".

BTW, the label here is "Slider Label", which at the least should be "Slider label" and better, a realistic text string, like "Price range" or whatever.

I've corrected the casing on that. I kind of want to avoid adopting price for the example, b/c of internationalization, but open to update if you like.

@m4olivei
Copy link
Contributor Author

m4olivei commented Oct 6, 2023

Looking great, Matthew. Went across all themes— just noting that the selected state seems to not have the correct color across themes. The selected rail should use the border-interactive color token to work across themes. The icon as well does not align with the color in the original slider component.

Thanks @KevinCamelo , nice catch. I've gone through and corrected those tokens.

Copy link
Member

@laurenmrice laurenmrice left a comment

Choose a reason for hiding this comment

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

Looks good, thank you @m4olivei ⭐️

@tay1orjones tay1orjones added this pull request to the merge queue Oct 17, 2023
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to a conflict with the base branch Oct 18, 2023
@tay1orjones tay1orjones added this pull request to the merge queue Oct 18, 2023
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Oct 18, 2023
@tw15egan tw15egan added this pull request to the merge queue Oct 18, 2023
Merged via the queue into carbon-design-system:main with commit b102902 Oct 18, 2023
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Slider] Two Handles Defining the Range: Implementation
9 participants