Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
54 changes: 35 additions & 19 deletions packages/react-core/src/components/Slider/Slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { css } from '@patternfly/react-styles';
import { SliderStep } from './SliderStep';
import { InputGroup, InputGroupText } from '../InputGroup';
import { TextInput } from '../TextInput';
import { Tooltip } from '../Tooltip';

export interface SliderStepObject {
/** Value of the step. This value is a percentage of the slider where the tick is drawn. */
Expand Down Expand Up @@ -44,6 +45,8 @@ export interface SliderProps extends Omit<React.HTMLProps<HTMLDivElement>, 'onCh
inputAriaLabel?: string;
/* Aria label for the thumb */
thumbAriaLabel?: string;
/* Adds a tooltip over the thumb containing the current value */
hasTooltipOverThumb?: boolean;
/** Label that is place after the input field */
inputLabel?: string | number;
/** Position of the input */
Expand Down Expand Up @@ -73,6 +76,7 @@ export const Slider: React.FunctionComponent<SliderProps> = ({
inputLabel,
inputAriaLabel = 'Slider value input',
thumbAriaLabel = 'Value',
hasTooltipOverThumb = false,
inputPosition = 'right',
onChange,
leftActions,
Expand Down Expand Up @@ -141,7 +145,9 @@ export const Slider: React.FunctionComponent<SliderProps> = ({
return matchingStep.label;
}
}
return localValue.toString();
// For continuous steps default to showing 2 decimals in tooltip
// Consider making it configurable via a property
return Number(Number(localValue).toFixed(2)).toString();
};

const handleThumbDragEnd = () => {
Expand Down Expand Up @@ -209,12 +215,12 @@ export const Slider: React.FunctionComponent<SliderProps> = ({

thumbRef.current.style.setProperty('--pf-c-slider--value', `${newPercentage}%`);
// convert percentage to value
const newValue = (newPercentage * (max - min)) / 100 + min;
const newValue = Math.round(((newPercentage * (max - min)) / 100 + min) * 100) / 100;
setValue(newValue);

if (!customSteps) {
// snap to new value if not custom steps
snapValue = Math.round((newValue - min) / step) * step + min;
snapValue = Math.round((Math.round((newValue - min) / step) * step + min) * 100) / 100;
thumbRef.current.style.setProperty('--pf-c-slider--value', `${snapValue}%`);
setValue(snapValue);
}
Expand Down Expand Up @@ -344,6 +350,25 @@ export const Slider: React.FunctionComponent<SliderProps> = ({
return builtSteps;
};

const thumbComponent = (
<div
className={css(styles.sliderThumb)}
ref={thumbRef}
tabIndex={isDisabled ? -1 : 0}
role="slider"
aria-valuemin={customSteps ? customSteps[0].value : min}
aria-valuemax={customSteps ? customSteps[customSteps.length - 1].value : max}
aria-valuenow={localValue}
aria-valuetext={findAriaTextValue()}
aria-label={thumbAriaLabel}
aria-disabled={isDisabled}
onMouseDown={!isDisabled ? handleMouseDown : null}
onTouchStart={!isDisabled ? handleTouchStart : null}
onKeyDown={!isDisabled ? handleThumbKeys : null}
onClick={!isDisabled ? onThumbClick : null}
/>
);

return (
<div
className={css(styles.slider, className, isDisabled && styles.modifiers.disabled)}
Expand Down Expand Up @@ -379,22 +404,13 @@ export const Slider: React.FunctionComponent<SliderProps> = ({
{buildSteps()}
</div>
)}
<div
className={css(styles.sliderThumb)}
ref={thumbRef}
tabIndex={isDisabled ? -1 : 0}
role="slider"
aria-valuemin={customSteps ? customSteps[0].value : min}
aria-valuemax={customSteps ? customSteps[customSteps.length - 1].value : max}
aria-valuenow={localValue}
aria-valuetext={findAriaTextValue()}
aria-label={thumbAriaLabel}
aria-disabled={isDisabled}
onMouseDown={!isDisabled ? handleMouseDown : null}
onTouchStart={!isDisabled ? handleTouchStart : null}
onKeyDown={!isDisabled ? handleThumbKeys : null}
onClick={!isDisabled ? onThumbClick : null}
/>
{hasTooltipOverThumb ? (
<Tooltip entryDelay={0} content={findAriaTextValue()}>
{thumbComponent}
</Tooltip>
) : (
thumbComponent
)}
{isInputVisible && inputPosition === 'aboveThumb' && (
<div className={css(styles.sliderValue, styles.modifiers.floating)}>{displayInput()}</div>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { mount, shallow } from 'enzyme';
import { Slider } from '../Slider';
import { Button } from '../../Button';

Expand Down Expand Up @@ -69,4 +69,9 @@ describe('slider', () => {
const view = mount(<Slider value={50} isDisabled />);
expect(view).toMatchSnapshot();
});

test('renders slider with tooltip on thumb', () => {
const view = shallow(<Slider value={50} hasTooltipOverThumb />);
expect(view).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -1194,3 +1194,69 @@ exports[`slider renders slider with input actions 1`] = `
</div>
</Slider>
`;

exports[`slider renders slider with tooltip on thumb 1`] = `
<div
className="pf-c-slider"
style={
Object {
"--pf-c-slider--value": "50%",
"--pf-c-slider__value--c-form-control--width-chars": 1,
}
}
>
<div
className="pf-c-slider__main"
>
<div
className="pf-c-slider__rail"
onClick={[Function]}
>
<div
className="pf-c-slider__rail-track"
/>
</div>
<div
aria-hidden="true"
className="pf-c-slider__steps"
>
<SliderStep
isActive={true}
isLabelHidden={false}
isTickHidden={true}
key="0"
label="0"
value={0}
/>
<SliderStep
isActive={false}
isLabelHidden={false}
isTickHidden={true}
key="100"
label="100"
value={100}
/>
</div>
<Tooltip
content="50"
entryDelay={0}
>
<div
aria-disabled={false}
aria-label="Value"
aria-valuemax={100}
aria-valuemin={0}
aria-valuenow={50}
aria-valuetext="50"
className="pf-c-slider__thumb"
onClick={[Function]}
onKeyDown={[Function]}
onMouseDown={[Function]}
onTouchStart={[Function]}
role="slider"
tabIndex={0}
/>
</Tooltip>
</div>
</div>
`;
19 changes: 15 additions & 4 deletions packages/react-core/src/components/Slider/examples/Slider.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,13 @@ class DiscreteInput extends React.Component {

```js
import React from 'react';
import { Slider, Text, TextVariants } from '@patternfly/react-core';
import { Checkbox, Slider, Text, TextVariants } from '@patternfly/react-core';

class ContinuousInput extends React.Component {
constructor(props) {
super(props);
this.state = {
hasTooltipOverThumb: false,
value: 50,
valueCustom: 50
};
Expand All @@ -160,14 +161,24 @@ class ContinuousInput extends React.Component {
render() {
return (
<>
<Text component={TextVariants.h3}>Slider value is: {this.state.value.toFixed(2)}</Text>
<Slider value={this.state.value} onChange={this.onChange} />
<Checkbox
id="thumb-has-tooltip"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would add a style={{ marginBottom: 20 }} to the Checkbox.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Added.

label="hasTooltipOverThumb"
isChecked={this.state.hasTooltipOverThumb}
onChange={hasTooltipOverThumb => this.setState({ hasTooltipOverThumb })}
style={{ marginBottom: 20 }} />
<Text component={TextVariants.h3}>Slider Value is: {this.state.value}</Text>
<Slider
hasTooltipOverThumb={this.state.hasTooltipOverThumb}
value={this.state.value}
onChange={this.onChange} />
<br />
<Text component={TextVariants.h3}>Slider value is: {this.state.valueCustom.toFixed(2)}</Text>
<Text component={TextVariants.h3}>Slider value is: {this.state.valueCustom}</Text>
<Slider
onChange={this.onChangeCustom}
value={this.state.valueCustom}
areCustomStepsContinuous
hasTooltipOverThumb={this.state.hasTooltipOverThumb}
customSteps={[
{ value: 0, label: '0%' },
{ value: 100, label: '100%' }
Expand Down