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 tooltips for single and two handle sliders #15129

Merged
merged 27 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a614050
fix(slider): refine what is focusable to be more broadly inclusive
m4olivei Nov 2, 2023
7d17624
feat(slider): add tooltips to slider
m4olivei Nov 2, 2023
23d6972
fix(slider): adjust for nice default tooltips on single handle
m4olivei Nov 8, 2023
aed9823
fix(slider): correct single handle RTL bug
m4olivei Nov 8, 2023
5147cfb
fix(slider): put back the focus handler for the lower thumb
m4olivei Nov 8, 2023
a03bbf4
fix(slider): rtl positioning bug
m4olivei Nov 8, 2023
1bd0a2f
chore: udpate tests
andreancardona Nov 8, 2023
1bf4ffe
Merge branch 'main' into feat/slider-tooltips
andreancardona Nov 8, 2023
f856716
test(slider): adjust unit tests for slider w/ tooltips
m4olivei Nov 9, 2023
573a500
Merge branch 'main' into feat/slider-tooltips
m4olivei Nov 9, 2023
8fa79c4
Merge branch 'main' into feat/slider-tooltips
m4olivei Nov 10, 2023
f235822
fix(slider): remove space when text input is hidden
m4olivei Nov 10, 2023
0213aff
fix(slider): restore blue focus filled track
m4olivei Nov 10, 2023
0ed9df0
feat(slider): adjustments to tooltip positioning
m4olivei Nov 10, 2023
e32aaa7
feat(slider): use a ThumbWrapper component to conditionaly do tooltips
m4olivei Nov 10, 2023
0335f04
feat(slider): add stories for hidden inputs
m4olivei Nov 10, 2023
1dde972
fix(tooltip): hold the tooltip open if the user is mid-drag interaction
m4olivei Nov 13, 2023
dd58a54
Merge branch 'main' into feat/slider-tooltips
m4olivei Nov 13, 2023
a661383
Merge branch 'main' into feat/slider-tooltips
m4olivei Nov 21, 2023
c3b30ef
refactor(slider): move slider handle componets into own file
m4olivei Nov 21, 2023
e51e916
fix(slider): make skeletons good again
m4olivei Nov 21, 2023
bf8b72c
fix(slider): manage focus when activating using the track
m4olivei Nov 21, 2023
9b120fb
chore(slider): remove redundant preventDefault
m4olivei Nov 22, 2023
7469fd2
fix(slider): more robust activeHandle detection
m4olivei Nov 22, 2023
4a6b89c
test(slider): fix slider tests
m4olivei Nov 22, 2023
2f5368b
fix(slider): fix positioning of thumbs for rtl
m4olivei Nov 22, 2023
65a5dc7
fix(slider): correct tooltips rtl
m4olivei Nov 22, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 37 additions & 22 deletions packages/react/src/components/Slider/Slider-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,12 @@ describe('Slider', () => {
});

it('should accurately position slider on mount', () => {
renderSlider({ value: 50, max: 100, min: 0 });
expect(screen.getByRole('slider')).toHaveStyle({
const { container } = renderSlider({ value: 50, max: 100, min: 0 });
// eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
const sliderWrapper = container.querySelector(
`.${prefix}--slider__thumb-wrapper`
);
expect(sliderWrapper).toHaveStyle({
insetInlineStart: '50%',
});
});
Expand Down Expand Up @@ -681,40 +685,51 @@ describe('Slider', () => {

// Keyboard interactions on the upper thumb, lets mix it up and do the up
// and down arrow keys this time.
// Note: Somewhat unintuitively, a click on the upperThumb in the moves it
// as close to the lowerThumb as possible. This is because the elements
// in Jest don't exist in a specific position, layer and size.
// @see https://testing-library.com/docs/user-event/pointer
await click(upperThumb);
expect(upperThumb).toHaveFocus();
await keyboard('{ArrowUp}');
expect(onChange).toHaveBeenLastCalledWith({ value: 10, valueUpper: 91 });
expect(upperThumb).toHaveAttribute('aria-valuenow', '91');
expect(lowerThumb).toHaveAttribute('aria-valuemax', '91');
expect(upperInput).toHaveValue(91);
expect(onChange).toHaveBeenLastCalledWith({ value: 10, valueUpper: 11 });
expect(upperThumb).toHaveAttribute('aria-valuenow', '11');
expect(lowerThumb).toHaveAttribute('aria-valuemax', '11');
expect(upperInput).toHaveValue(11);
await keyboard('{ArrowDown}');
expect(onChange).toHaveBeenLastCalledWith({ value: 10, valueUpper: 90 });
expect(upperThumb).toHaveAttribute('aria-valuenow', '90');
expect(lowerThumb).toHaveAttribute('aria-valuemax', '90');
expect(upperInput).toHaveValue(90);
expect(onChange).toHaveBeenLastCalledWith({ value: 10, valueUpper: 10 });
expect(upperThumb).toHaveAttribute('aria-valuenow', '10');
expect(lowerThumb).toHaveAttribute('aria-valuemax', '10');
expect(upperInput).toHaveValue(10);
await keyboard('{Shift>}{ArrowUp}{/Shift}');
expect(onChange).toHaveBeenLastCalledWith({ value: 10, valueUpper: 94 });
expect(upperThumb).toHaveAttribute('aria-valuenow', '94');
expect(lowerThumb).toHaveAttribute('aria-valuemax', '94');
expect(upperInput).toHaveValue(94);
expect(onChange).toHaveBeenLastCalledWith({ value: 10, valueUpper: 14 });
expect(upperThumb).toHaveAttribute('aria-valuenow', '14');
expect(lowerThumb).toHaveAttribute('aria-valuemax', '14');
expect(upperInput).toHaveValue(14);
await keyboard('{Shift>}{ArrowDown}{/Shift}');
expect(onChange).toHaveBeenLastCalledWith({ value: 10, valueUpper: 90 });
expect(upperThumb).toHaveAttribute('aria-valuenow', '90');
expect(lowerThumb).toHaveAttribute('aria-valuemax', '90');
expect(upperInput).toHaveValue(90);
expect(onChange).toHaveBeenLastCalledWith({ value: 10, valueUpper: 10 });
expect(upperThumb).toHaveAttribute('aria-valuenow', '10');
expect(lowerThumb).toHaveAttribute('aria-valuemax', '10');
expect(upperInput).toHaveValue(10);
});

it('should accurately position handles on mount', () => {
renderTwoHandleSlider({
const { container } = renderTwoHandleSlider({
value: 50,
unstable_valueUpper: 50,
min: 0,
max: 100,
});
const [lowerThumb, upperThumb] = screen.getAllByRole('slider');
expect(lowerThumb).toHaveStyle({ insetInlineStart: '50%' });
expect(upperThumb).toHaveStyle({ insetInlineStart: '50%' });
// eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
const sliderWrapperLower = container.querySelector(
`.${prefix}--slider__thumb-wrapper--lower`
);
// eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
const sliderWrapperUpper = container.querySelector(
`.${prefix}--slider__thumb-wrapper--upper`
);
expect(sliderWrapperLower).toHaveStyle({ insetInlineStart: '50%' });
expect(sliderWrapperUpper).toHaveStyle({ insetInlineStart: '50%' });
});

it('marks input field as hidden if hidden via props', () => {
Expand Down
37 changes: 35 additions & 2 deletions packages/react/src/components/Slider/Slider.Skeleton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import React, { HTMLAttributes } from 'react';
import cx from 'classnames';
import { usePrefix } from '../../internal/usePrefix';
import classNames from 'classnames';
import { LowerHandle, UpperHandle } from './SliderHandles';

export interface SliderSkeletonProps extends HTMLAttributes<HTMLDivElement> {
/**
Expand All @@ -35,11 +36,13 @@ const SliderSkeleton = ({
...rest
}: SliderSkeletonProps) => {
const prefix = usePrefix();
const isRtl = document?.dir === 'rtl';
const containerClasses = classNames(
`${prefix}--slider-container`,
`${prefix}--skeleton`,
{
[`${prefix}--slider-container--two-handles`]: twoHandles,
[`${prefix}--slider-container--rtl`]: isRtl,
}
);
const lowerThumbClasses = classNames(`${prefix}--slider__thumb`, {
Expand All @@ -48,6 +51,18 @@ const SliderSkeleton = ({
const upperThumbClasses = classNames(`${prefix}--slider__thumb`, {
[`${prefix}--slider__thumb--upper`]: twoHandles,
});
const lowerThumbWrapperClasses = classNames(
`${prefix}--slider__thumb-wrapper`,
{
[`${prefix}--slider__thumb-wrapper--lower`]: twoHandles,
}
);
const upperThumbWrapperClasses = classNames(
`${prefix}--slider__thumb-wrapper`,
{
[`${prefix}--slider__thumb-wrapper--upper`]: twoHandles,
}
);
return (
<div className={cx(`${prefix}--form-item`, className)} {...rest}>
{!hideLabel && (
Expand All @@ -58,8 +73,26 @@ const SliderSkeleton = ({
<div className={`${prefix}--slider`}>
<div className={`${prefix}--slider__track`} />
<div className={`${prefix}--slider__filled-track`} />
<div className={lowerThumbClasses} />
{twoHandles ? <div className={upperThumbClasses} /> : undefined}
<div className={lowerThumbWrapperClasses}>
<div className={lowerThumbClasses}>
{twoHandles && !isRtl ? (
<LowerHandle />
) : twoHandles && isRtl ? (
<UpperHandle />
) : undefined}
</div>
</div>
{twoHandles ? (
<div className={upperThumbWrapperClasses}>
<div className={upperThumbClasses}>
{twoHandles && !isRtl ? (
<UpperHandle />
) : twoHandles && isRtl ? (
<LowerHandle />
) : undefined}
</div>
</div>
) : undefined}
</div>
<span className={`${prefix}--slider__range-label`} />
</div>
Expand Down
30 changes: 30 additions & 0 deletions packages/react/src/components/Slider/Slider.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,20 @@ export const Default = () => (
/>
);

export const SliderWithHiddenInputs = () => (
<Slider
labelText="Slider label"
value={50}
min={0}
max={100}
step={1}
stepMultiplier={10}
noValidate
invalidText="Invalid message goes here"
hideTextInput={true}
/>
);

export const ControlledSlider = () => {
const [val, setVal] = useState(87);
return (
Expand Down Expand Up @@ -110,6 +124,22 @@ export const TwoHandleSlider = () => (
/>
);

export const TwoHandleSliderWithHiddenInputs = () => (
<Slider
ariaLabelInput="Lower bound"
unstable_ariaLabelInputUpper="Upper bound"
labelText="Slider label"
value={10}
unstable_valueUpper={90}
min={0}
max={100}
step={1}
stepMultiplier={10}
invalidText="Invalid message goes here"
hideTextInput={true}
/>
);

export const Skeleton = () => <SliderSkeleton />;

export const TwoHandleSkeleton = () => <SliderSkeleton twoHandles={true} />;
Expand Down
Loading