- );
- }
-}
-
-CustomIconSlider.propTypes = {
- classes: PropTypes.object.isRequired,
-};
-
-export default withStyles(styles)(CustomIconSlider);
diff --git a/docs/src/pages/components/slider/CustomValueReducerSlider.js b/docs/src/pages/components/slider/CustomValueReducerSlider.js
deleted file mode 100644
index 78a975eadaca0c..00000000000000
--- a/docs/src/pages/components/slider/CustomValueReducerSlider.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { withStyles } from '@material-ui/core/styles';
-import Slider, { defaultValueReducer } from '@material-ui/lab/Slider';
-
-const styles = {
- root: {
- width: 300,
- },
-};
-
-/**
- * a value reducer that will snap to multiple of 10 but also to the edge value
- * Useful here because the max=104 is not a multiple of 10
- */
-function valueReducer(rawValue, props, event) {
- const { disabled, max, min, step } = props;
-
- function roundToStep(number) {
- return Math.round(number / step) * step;
- }
-
- if (!disabled && step) {
- if (rawValue > min && rawValue < max) {
- if (rawValue === max - step) {
- // If moving the Slider using arrow keys and value is formerly an maximum edge value
- return roundToStep(rawValue + step / 2);
- }
- if (rawValue === min + step) {
- // Same for minimum edge value
- return roundToStep(rawValue - step / 2);
- }
- return roundToStep(rawValue);
- }
- return rawValue;
- }
-
- return defaultValueReducer(rawValue, props, event);
-}
-
-/**
- * this slider has a max that is not a multiple of its step. We use a custom
- * `valueReducer` to adjust the given values
- */
-class StepSlider extends React.Component {
- state = {
- value: 30,
- };
-
- handleChange = (event, value) => {
- this.setState({ value });
- };
-
- render() {
- const { classes } = this.props;
- const { value } = this.state;
-
- return (
-
-
- );
- }
-}
+ {mark.label}
+
+
+ );
+ })}
+ {values.map((value, index) => {
+ const percent = valueToPercent(value, min, max);
+ const style = axisProps[axis].offset(percent);
+
+ return (
+
+
+
+ );
+ })}
+
+ );
+});
Slider.propTypes = {
+ /**
+ * The label of the slider.
+ */
+ 'aria-label': PropTypes.string,
+ /**
+ * The id of the element containing a label for the slider.
+ */
+ 'aria-labelledby': PropTypes.string,
+ /**
+ * A string value that provides a user-friendly name for the current value of the slider.
+ */
+ 'aria-valuetext': chainPropTypes(PropTypes.string, props => {
+ const range = Array.isArray(props.value || props.defaultValue);
+
+ if (range && props['aria-valuetext']) {
+ return new Error(
+ 'Material-UI: you need to use the `getAriaValueText` prop instead of `aria-valuetext` when using a range input.',
+ );
+ }
+
+ return null;
+ }),
+
/**
* Override or extend the styles applied to the component.
* See [CSS API](#css) below for more details.
@@ -645,15 +745,27 @@ Slider.propTypes = {
* Either a string to use a DOM element or a component.
*/
component: PropTypes.elementType,
+ /**
+ * The default element value. Use when the component is not controlled.
+ */
+ defaultValue: PropTypes.oneOfType([PropTypes.number, PropTypes.arrayOf(PropTypes.number)]),
/**
* If `true`, the slider will be disabled.
*/
disabled: PropTypes.bool,
/**
- * @ignore
- * from `withForwardRef`
+ * Accepts a function which returns a string value that provides a user-friendly name for the current value of the slider.
+ *
+ * @param {number} value The thumb label's value to format
+ * @param {number} index The thumb label's index to format
+ */
+ getAriaValueText: PropTypes.func,
+ /**
+ * Marks indicate predetermined values to which the user can move the slider.
+ * If `true` the marks will be spaced according the value of the `step` prop.
+ * If an array, it should contain objects with `value` and an optional `label` keys.
*/
- innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
+ marks: PropTypes.oneOfType([PropTypes.bool, PropTypes.array]),
/**
* The maximum allowed value of the slider.
* Should not be equal to min.
@@ -664,54 +776,67 @@ Slider.propTypes = {
* Should not be equal to max.
*/
min: PropTypes.number,
+ /**
+ * Name attribute of the hidden `input` element.
+ */
+ name: PropTypes.string,
/**
* Callback function that is fired when the slider's value changed.
+ *
+ * @param {object} event The event source of the callback
+ * @param {any} value The new value
*/
onChange: PropTypes.func,
/**
- * Callback function that is fired when the slide has stopped moving.
+ * Callback function that is fired when the `mouseup` is triggered.
+ *
+ * @param {object} event The event source of the callback
+ * @param {any} value The new value
+ */
+ onChangeCommitted: PropTypes.func,
+ /**
+ * @ignore
*/
- onDragEnd: PropTypes.func,
+ onMouseDown: PropTypes.func,
/**
- * Callback function that is fired when the slider has begun to move.
+ * The slider orientation.
*/
- onDragStart: PropTypes.func,
+ orientation: PropTypes.oneOf(['horizontal', 'vertical']),
/**
- * The granularity the slider can step through values.
+ * The granularity with which the slider can step through values. (A "discrete" slider.)
+ * When step is `null`, the thumb can only be slid onto marks provided with the `marks` prop.
*/
step: PropTypes.number,
/**
- * @ignore
+ * The component used to display the value label.
*/
- theme: PropTypes.object.isRequired,
+ ThumbComponent: PropTypes.elementType,
/**
- * The component used for the slider icon.
- * This is optional, if provided should be a react element.
+ * The value of the slider.
+ * For ranged sliders, provide an array with two values.
*/
- thumb: PropTypes.element,
+ value: PropTypes.oneOfType([PropTypes.number, PropTypes.arrayOf(PropTypes.number)]),
/**
- * The value of the slider.
+ * The value label componnet.
*/
- value: PropTypes.number.isRequired,
+ ValueLabelComponent: PropTypes.elementType,
/**
- * the reducer used to process the value emitted from the slider. If `null` or
- * the same value is returned no change is emitted.
- * @param {number} rawValue - value in [min, max]
- * @param {SliderProps} props - current props of the Slider
- * @param {Event} event - the event the change was triggered from
+ * Controls when the value label is displayed:
+ *
+ * - `auto` the value label will display when the thumb is hovered or focused.
+ * - `on` will display persistently.
+ * - `off` will never display.
*/
- valueReducer: PropTypes.func,
+ valueLabelDisplay: PropTypes.oneOf(['on', 'auto', 'off']),
/**
- * If `true`, the slider will be vertical.
+ * The format function the value label's value.
+ *
+ * When a function is provided, it should have the following signature:
+ *
+ * - {number} value The value label's value to format
+ * - {number} index The value label's index to format
*/
- vertical: PropTypes.bool,
-};
-
-Slider.defaultProps = {
- min: 0,
- max: 100,
- component: 'div',
- valueReducer: defaultValueReducer,
+ valueLabelFormat: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
};
-export default withStyles(styles, { name: 'MuiSlider', withTheme: true })(withForwardedRef(Slider));
+export default withStyles(styles, { name: 'MuiSlider' })(Slider);
diff --git a/packages/material-ui-lab/src/Slider/Slider.spec.tsx b/packages/material-ui-lab/src/Slider/Slider.spec.tsx
deleted file mode 100644
index a3f31ccb231c2a..00000000000000
--- a/packages/material-ui-lab/src/Slider/Slider.spec.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import Slider, { defaultValueReducer, ValueReducer } from '@material-ui/lab/Slider';
-import * as React from 'react';
-
-{
- // round to 1 digit instead of 3
- const valueReducer: ValueReducer = (rawValue, props, event) => {
- const { disabled, step } = props;
- if (!disabled && !step) {
- return +rawValue.toFixed(1);
- }
- return defaultValueReducer(rawValue, props, event);
- };
-
- ;
-}
diff --git a/packages/material-ui-lab/src/Slider/Slider.test.js b/packages/material-ui-lab/src/Slider/Slider.test.js
index da0f08df540013..75b9ae43d9c7bc 100644
--- a/packages/material-ui-lab/src/Slider/Slider.test.js
+++ b/packages/material-ui-lab/src/Slider/Slider.test.js
@@ -7,20 +7,30 @@ import {
findOutermostIntrinsic,
wrapsIntrinsicElement,
} from '@material-ui/core/test-utils';
-import Slider, { defaultValueReducer } from './Slider';
+import describeConformance from '@material-ui/core/test-utils/describeConformance';
+import Slider from './Slider';
function touchList(touchArray) {
touchArray.item = idx => touchArray[idx];
return touchArray;
}
+function fireBodyMouseEvent(name, properties = {}) {
+ const event = document.createEvent('MouseEvents');
+ event.initEvent(name, true, true);
+ Object.keys(properties).forEach(key => {
+ event[key] = properties[key];
+ });
+ document.body.dispatchEvent(event);
+ return event;
+}
+
describe('', () => {
let mount;
let classes;
before(() => {
classes = getClasses();
- // StrictMode violation: uses ButtonBase
mount = createMount({ strict: false });
});
@@ -28,23 +38,21 @@ describe('', () => {
mount.cleanUp();
});
- function findHandle(wrapper) {
+ describeConformance(, () => ({
+ classes,
+ inheritComponent: 'span',
+ mount,
+ refInstanceof: window.HTMLSpanElement,
+ testComponentPropWith: 'span',
+ }));
+
+ function findThumb(wrapper) {
// Will also match any other react component if not filtered. They won't appear in the DOM
// and are therefore an implementation detail. We're interested in what the user
// interacts with.
return wrapper.find('[role="slider"]').filterWhere(wrapsIntrinsicElement);
}
- it('should render a div', () => {
- const wrapper = mount();
- assert.strictEqual(findOutermostIntrinsic(wrapper).type(), 'div');
- });
-
- it('should render with the default classes', () => {
- const wrapper = mount();
- assert.strictEqual(findOutermostIntrinsic(wrapper).hasClass(classes.root), true);
- });
-
it('should render with the default and user classes', () => {
const wrapper = mount();
assert.strictEqual(
@@ -58,16 +66,10 @@ describe('', () => {
it('should call handlers', () => {
const handleChange = spy();
- const handleDragStart = spy();
- const handleDragEnd = spy();
+ const handleChangeCommitted = spy();
const wrapper = mount(
- ,
+ ,
);
wrapper.simulate('click');
@@ -75,66 +77,46 @@ describe('', () => {
// document.simulate('mouseup')
document.body.dispatchEvent(new window.MouseEvent('mouseup'));
- assert.strictEqual(handleChange.callCount, 1, 'should have called the handleChange cb');
- assert.strictEqual(handleDragStart.callCount, 1, 'should have called the handleDragStart cb');
- assert.strictEqual(handleDragEnd.callCount, 1, 'should have called the handleDragEnd cb');
+ assert.strictEqual(handleChange.callCount, 1);
+ assert.strictEqual(handleChangeCommitted.callCount, 1);
- assert.strictEqual(
- handleChange.args[0].length,
- 2,
- 'should have called the handleDragEnd cb with 2 arguments',
- );
- assert.strictEqual(
- handleDragStart.args[0].length,
- 2,
- 'should have called the handleDragEnd cb with 2 argument',
- );
- assert.strictEqual(
- handleDragEnd.args[0].length,
- 2,
- 'should have called the handleDragEnd cb with 2 arguments',
- );
+ assert.strictEqual(handleChange.args[0].length, 2);
+ assert.strictEqual(handleChangeCommitted.args[0].length, 2);
});
it('should only listen to changes from the same touchpoint', () => {
const handleChange = spy();
- const handleDragStart = spy();
- const handleDragEnd = spy();
- let touchEvent;
-
+ const handleChangeCommitted = spy();
+ const touches = [{ pageX: 0, pageY: 0 }];
const wrapper = mount(
- ,
+ ,
);
- wrapper.simulate('touchstart', {
+ const event = fireBodyMouseEvent('touchstart', {
changedTouches: touchList([{ identifier: 1 }]),
+ touches,
});
- wrapper.simulate('touchmove', {
+ wrapper.getDOMNode().dispatchEvent(event);
+ assert.strictEqual(handleChange.callCount, 1);
+ assert.strictEqual(handleChangeCommitted.callCount, 0);
+ fireBodyMouseEvent('touchend', {
changedTouches: touchList([{ identifier: 2 }]),
+ touches,
});
- touchEvent = new window.MouseEvent('touchend');
- touchEvent.changedTouches = touchList([{ identifier: 2 }]);
- document.body.dispatchEvent(touchEvent);
-
- assert.strictEqual(handleChange.callCount, 1, 'should have called the handleChange cb');
- assert.strictEqual(handleDragStart.callCount, 1, 'should have called the handleDragStart cb');
- assert.strictEqual(handleDragEnd.callCount, 0, 'should not have called the handleDragEnd cb');
-
- wrapper.simulate('touchmove', {
+ assert.strictEqual(handleChange.callCount, 1);
+ assert.strictEqual(handleChangeCommitted.callCount, 0);
+ fireBodyMouseEvent('touchmove', {
changedTouches: touchList([{ identifier: 1 }]),
+ touches,
});
- touchEvent = new window.MouseEvent('touchend');
- touchEvent.changedTouches = touchList([{ identifier: 1 }]);
- document.body.dispatchEvent(touchEvent);
-
- assert.strictEqual(handleChange.callCount, 2, 'should have called the handleChange cb');
- assert.strictEqual(handleDragStart.callCount, 1, 'should have called the handleDragStart cb');
- assert.strictEqual(handleDragEnd.callCount, 1, 'should have called the handleDragEnd cb');
+ assert.strictEqual(handleChange.callCount, 2);
+ assert.strictEqual(handleChangeCommitted.callCount, 0);
+ fireBodyMouseEvent('touchend', {
+ changedTouches: touchList([{ identifier: 1 }]),
+ touches,
+ });
+ assert.strictEqual(handleChange.callCount, 2);
+ assert.strictEqual(handleChangeCommitted.callCount, 1);
});
describe('when mouse leaves window', () => {
@@ -146,7 +128,7 @@ describe('', () => {
wrapper.simulate('mousedown');
document.body.dispatchEvent(new window.MouseEvent('mouseleave'));
- assert.strictEqual(handleChange.callCount, 1, 'should have called the handleChange cb');
+ assert.strictEqual(handleChange.callCount, 1);
});
});
@@ -164,7 +146,7 @@ describe('', () => {
document.body.dispatchEvent(mouseEnter);
document.body.dispatchEvent(new window.MouseEvent('mousemove'));
- assert.strictEqual(handleChange.callCount, 2, 'should have called the handleChange cb');
+ assert.strictEqual(handleChange.callCount, 2);
});
it('should not update if mouse is not clicked', () => {
@@ -180,16 +162,18 @@ describe('', () => {
document.body.dispatchEvent(mouseEnter);
document.body.dispatchEvent(new window.MouseEvent('mousemove'));
- assert.strictEqual(handleChange.callCount, 1, 'should have called the handleChange cb');
+ assert.strictEqual(handleChange.callCount, 1);
});
});
describe('unmount', () => {
it('should not have global event listeners registered after unmount', () => {
const handleChange = spy();
- const handleDragEnd = spy();
+ const handleChangeCommitted = spy();
- const wrapper = mount();
+ const wrapper = mount(
+ ,
+ );
const callGlobalListeners = () => {
document.body.dispatchEvent(new window.MouseEvent('mousemove'));
@@ -199,24 +183,22 @@ describe('', () => {
wrapper.simulate('mousedown');
callGlobalListeners();
// pre condition: the dispatched event actually did something when mounted
- assert.strictEqual(handleChange.callCount, 1);
- assert.strictEqual(handleDragEnd.callCount, 1);
-
+ assert.strictEqual(handleChange.callCount, 2);
+ assert.strictEqual(handleChangeCommitted.callCount, 1);
wrapper.unmount();
-
// After unmounting global listeners should not be registered anymore since that would
// break component encapsulation. If they are still mounted either react will throw warnings
// or other component logic throws.
// post condition: the dispatched events dont cause errors/warnings
callGlobalListeners();
- assert.strictEqual(handleChange.callCount, 1);
- assert.strictEqual(handleDragEnd.callCount, 1);
+ assert.strictEqual(handleChange.callCount, 2);
+ assert.strictEqual(handleChangeCommitted.callCount, 1);
});
});
- describe('prop: vertical', () => {
+ describe('prop: orientation', () => {
it('should render with the default and vertical classes', () => {
- const wrapper = mount();
+ const wrapper = mount();
assert.strictEqual(
wrapper
.find(`.${classes.root}`)
@@ -228,119 +210,77 @@ describe('', () => {
});
describe('prop: disabled', () => {
- const handleChange = spy();
- let wrapper;
-
- before(() => {
- wrapper = mount();
- });
-
- it('should render thumb with the disabled classes', () => {
- const handle = findHandle(wrapper);
-
- assert.strictEqual(handle.hasClass(classes.thumb), true);
- assert.strictEqual(handle.hasClass(classes.disabled), true);
- });
-
- it('should render tracks with the disabled classes', () => {
- const tracks = wrapper.find('div').filterWhere(n => n.hasClass(classes.track));
-
- assert.strictEqual(tracks.everyWhere(n => n.hasClass(classes.disabled)), true);
- });
-
- it("should not call 'onChange' handler", () => {
- wrapper.simulate('click');
-
- assert.strictEqual(handleChange.callCount, 0);
- });
-
- it('should signal that it is disabled', () => {
- assert.ok(findHandle(wrapper).props().disabled);
+ it('should render the disabled classes', () => {
+ const wrapper = mount();
+ assert.strictEqual(findOutermostIntrinsic(wrapper).hasClass(classes.disabled), true);
});
});
- describe('prop: slider', () => {
+ describe('keyboard', () => {
let wrapper;
- const moveLeftEvent = new window.KeyboardEvent('keydown', {
+ const moveLeftEvent = {
key: 'ArrowLeft',
- });
- const moveRightEvent = new window.KeyboardEvent('keydown', {
+ };
+ const moveRightEvent = {
key: 'ArrowRight',
- });
+ };
before(() => {
- function valueReducer(rawValue, props, event) {
- const { disabled, max, min, step } = props;
-
- function roundToStep(number) {
- return Math.round(number / step) * step;
- }
-
- if (!disabled && step) {
- if (rawValue > min && rawValue < max) {
- if (rawValue === max - step) {
- // If moving the Slider using arrow keys and value is formerly an maximum edge value
- return roundToStep(rawValue + step / 2);
- }
- if (rawValue === min + step) {
- // Same for minimum edge value
- return roundToStep(rawValue - step / 2);
- }
- return roundToStep(rawValue);
- }
- return rawValue;
- }
-
- return defaultValueReducer(rawValue, props, event);
- }
-
const onChange = (_, value) => {
wrapper.setProps({ value });
};
- wrapper = mount(
- ,
- );
+ wrapper = mount();
});
it('should reach right edge value', () => {
wrapper.setProps({ value: 90 });
- const handle = findHandle(wrapper);
+ const thumb = findThumb(wrapper);
- handle.prop('onKeyDown')(moveRightEvent);
- assert.strictEqual(wrapper.prop('value'), 100);
+ thumb.simulate('keydown', moveRightEvent);
+ assert.strictEqual(wrapper.props().value, 100);
- handle.prop('onKeyDown')(moveRightEvent);
- assert.strictEqual(wrapper.prop('value'), 108);
+ thumb.simulate('keydown', moveRightEvent);
+ assert.strictEqual(wrapper.props().value, 108);
- handle.prop('onKeyDown')(moveLeftEvent);
- assert.strictEqual(wrapper.prop('value'), 100);
+ thumb.simulate('keydown', moveLeftEvent);
+ assert.strictEqual(wrapper.props().value, 100);
- handle.prop('onKeyDown')(moveLeftEvent);
- assert.strictEqual(wrapper.prop('value'), 90);
+ thumb.simulate('keydown', moveLeftEvent);
+ assert.strictEqual(wrapper.props().value, 90);
});
it('should reach left edge value', () => {
wrapper.setProps({ value: 20 });
- const handle = findHandle(wrapper);
- handle.prop('onKeyDown')(moveLeftEvent);
- assert.strictEqual(wrapper.prop('value'), 10);
+ const thumb = findThumb(wrapper);
+ thumb.simulate('keydown', moveLeftEvent);
+ assert.strictEqual(wrapper.props().value, 10);
- handle.prop('onKeyDown')(moveLeftEvent);
- assert.strictEqual(wrapper.prop('value'), 6);
+ thumb.simulate('keydown', moveLeftEvent);
+ assert.strictEqual(wrapper.props().value, 6);
+
+ thumb.simulate('keydown', moveRightEvent);
+ assert.strictEqual(wrapper.props().value, 20);
+
+ thumb.simulate('keydown', moveRightEvent);
+ assert.strictEqual(wrapper.props().value, 30);
+ });
+ });
+
+ describe('markActive state', () => {
+ it('should set the mark active', () => {
+ function getActives(wrapper) {
+ return wrapper
+ .find(`.${classes.markLabel}`)
+ .map(node => node.hasClass(classes.markLabelActive));
+ }
+ const marks = [{ value: 5 }, { value: 10 }, { value: 15 }];
- handle.prop('onKeyDown')(moveRightEvent);
- assert.strictEqual(wrapper.prop('value'), 10);
+ const wrapper1 = mount();
+ assert.deepEqual(getActives(wrapper1), [true, true, false]);
- handle.prop('onKeyDown')(moveRightEvent);
- assert.strictEqual(wrapper.prop('value'), 20);
+ const wrapper2 = mount();
+ assert.deepEqual(getActives(wrapper2), [false, true, false]);
});
});
});
diff --git a/packages/material-ui-lab/src/Slider/ValueLabel.js b/packages/material-ui-lab/src/Slider/ValueLabel.js
new file mode 100644
index 00000000000000..66efb68a97d7cd
--- /dev/null
+++ b/packages/material-ui-lab/src/Slider/ValueLabel.js
@@ -0,0 +1,86 @@
+import React from 'react';
+import { withStyles } from '@material-ui/core/styles';
+import clsx from 'clsx';
+
+const styles = theme => ({
+ thumb: {
+ '&$open': {
+ '& $offset': {
+ transform: 'scale(1) translateY(-10px)',
+ },
+ },
+ },
+ open: {},
+ offset: {
+ zIndex: 1,
+ ...theme.typography.body2,
+ fontSize: theme.typography.pxToRem(12),
+ lineHeight: 1.2,
+ transition: theme.transitions.create(['transform'], {
+ duration: theme.transitions.duration.shortest,
+ }),
+ top: -34,
+ left: 'calc(-50% + -4px)',
+ transformOrigin: 'bottom center',
+ transform: 'scale(0)',
+ position: 'absolute',
+ },
+ circle: {
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ width: 32,
+ height: 32,
+ borderRadius: '50% 50% 50% 0',
+ backgroundColor: 'currentColor',
+ transform: 'rotate(-45deg)',
+ },
+ label: {
+ color: 'white',
+ transform: 'rotate(45deg)',
+ },
+});
+
+/**
+ * @ignore - internal component.
+ */
+function ValueLabel(props) {
+ const {
+ children,
+ classes,
+ className,
+ index,
+ open,
+ value,
+ valueLabelDisplay,
+ valueLabelFormat,
+ } = props;
+
+ if (valueLabelDisplay === 'off') {
+ return children;
+ }
+
+ return React.cloneElement(
+ children,
+ {
+ className: clsx(
+ children.props.className,
+ {
+ [classes.open]: open || valueLabelDisplay === 'on',
+ },
+ classes.thumb,
+ ),
+ },
+
+
+
+ {typeof valueLabelFormat === 'function'
+ ? valueLabelFormat(value, index)
+ : valueLabelFormat}
+
+
+ ,
+ );
+}
+
+export default withStyles(styles, { name: 'PrivateValueLabel' })(ValueLabel);
diff --git a/packages/material-ui-lab/src/Slider/index.js b/packages/material-ui-lab/src/Slider/index.js
index 922d80e49ceea2..9898d6a85d1d01 100644
--- a/packages/material-ui-lab/src/Slider/index.js
+++ b/packages/material-ui-lab/src/Slider/index.js
@@ -1 +1 @@
-export { default, defaultValueReducer } from './Slider';
+export { default } from './Slider';
diff --git a/packages/material-ui-lab/src/SpeedDial/SpeedDial.js b/packages/material-ui-lab/src/SpeedDial/SpeedDial.js
index 6899c4846a54b9..b92cf40814fe2b 100644
--- a/packages/material-ui-lab/src/SpeedDial/SpeedDial.js
+++ b/packages/material-ui-lab/src/SpeedDial/SpeedDial.js
@@ -8,7 +8,16 @@ import Zoom from '@material-ui/core/Zoom';
import Fab from '@material-ui/core/Fab';
import { isMuiElement, useForkRef } from '@material-ui/core/utils';
import * as utils from './utils';
-import { clamp } from '../utils';
+
+function clamp(value, min, max) {
+ if (value < min) {
+ return min;
+ }
+ if (value > max) {
+ return max;
+ }
+ return value;
+}
const dialRadius = 32;
const spacingActions = 16;
diff --git a/packages/material-ui-lab/src/utils/clamp.d.ts b/packages/material-ui-lab/src/utils/clamp.d.ts
deleted file mode 100644
index ebee452b697125..00000000000000
--- a/packages/material-ui-lab/src/utils/clamp.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-export default function clamp(value: number, min?: number, max?: number): number;
diff --git a/packages/material-ui-lab/src/utils/clamp.js b/packages/material-ui-lab/src/utils/clamp.js
deleted file mode 100644
index c0aa7667a1d1e6..00000000000000
--- a/packages/material-ui-lab/src/utils/clamp.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export default function clamp(value, min = 0, max = 100) {
- return Math.min(Math.max(value, min), max);
-}
diff --git a/packages/material-ui-lab/src/utils/index.d.ts b/packages/material-ui-lab/src/utils/index.d.ts
deleted file mode 100644
index b16ff7bb005842..00000000000000
--- a/packages/material-ui-lab/src/utils/index.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { default as clamp } from './clamp';
diff --git a/packages/material-ui-lab/src/utils/index.js b/packages/material-ui-lab/src/utils/index.js
deleted file mode 100644
index ba965424fc1233..00000000000000
--- a/packages/material-ui-lab/src/utils/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// eslint-disable-next-line import/prefer-default-export
-export { default as clamp } from './clamp';
diff --git a/packages/material-ui/src/ButtonBase/ButtonBase.test.js b/packages/material-ui/src/ButtonBase/ButtonBase.test.js
index bec4f6dd17bb06..6c51697791d4bb 100644
--- a/packages/material-ui/src/ButtonBase/ButtonBase.test.js
+++ b/packages/material-ui/src/ButtonBase/ButtonBase.test.js
@@ -126,7 +126,7 @@ describe('', () => {
events.forEach(n => {
const event = n.charAt(2).toLowerCase() + n.slice(3);
- wrapper.simulate(event, { persist: () => {} });
+ wrapper.simulate(event, { target: {}, persist: () => {} });
assert.strictEqual(handlers[n].callCount, 1, `should have called the ${n} handler`);
});
});
diff --git a/packages/material-ui/src/FilledInput/FilledInput.js b/packages/material-ui/src/FilledInput/FilledInput.js
index 2b8eb2ae5aeb66..c7ca09e01ee8ca 100644
--- a/packages/material-ui/src/FilledInput/FilledInput.js
+++ b/packages/material-ui/src/FilledInput/FilledInput.js
@@ -177,7 +177,7 @@ FilledInput.propTypes = {
*/
className: PropTypes.string,
/**
- * The default `input` element value, useful when not controlling the component.
+ * The default `input` element value. Use when the component is not controlled.
*/
defaultValue: PropTypes.any,
/**
diff --git a/packages/material-ui/src/Input/Input.js b/packages/material-ui/src/Input/Input.js
index 80b090b78c8248..2fa38996a8ff83 100644
--- a/packages/material-ui/src/Input/Input.js
+++ b/packages/material-ui/src/Input/Input.js
@@ -139,7 +139,7 @@ Input.propTypes = {
*/
className: PropTypes.string,
/**
- * The default `input` element value, useful when not controlling the component.
+ * The default `input` element value. Use when the component is not controlled.
*/
defaultValue: PropTypes.any,
/**
diff --git a/packages/material-ui/src/InputBase/InputBase.js b/packages/material-ui/src/InputBase/InputBase.js
index 2d5ac0ec2f2603..ae2d3bed9804e6 100644
--- a/packages/material-ui/src/InputBase/InputBase.js
+++ b/packages/material-ui/src/InputBase/InputBase.js
@@ -433,7 +433,7 @@ InputBase.propTypes = {
*/
className: PropTypes.string,
/**
- * The default `input` element value, useful when not controlling the component.
+ * The default `input` element value. Use when the component is not controlled.
*/
defaultValue: PropTypes.any,
/**
diff --git a/packages/material-ui/src/OutlinedInput/OutlinedInput.js b/packages/material-ui/src/OutlinedInput/OutlinedInput.js
index e15bbea9ae1e30..63f57c1270e25e 100644
--- a/packages/material-ui/src/OutlinedInput/OutlinedInput.js
+++ b/packages/material-ui/src/OutlinedInput/OutlinedInput.js
@@ -145,7 +145,7 @@ OutlinedInput.propTypes = {
*/
className: PropTypes.string,
/**
- * The default `input` element value, useful when not controlling the component.
+ * The default `input` element value. Use when the component is not controlled.
*/
defaultValue: PropTypes.any,
/**
diff --git a/packages/material-ui/src/Popper/Popper.spec.tsx b/packages/material-ui/src/Popper/Popper.spec.tsx
index 08aa01b00ac077..ce734544e6cf40 100644
--- a/packages/material-ui/src/Popper/Popper.spec.tsx
+++ b/packages/material-ui/src/Popper/Popper.spec.tsx
@@ -7,7 +7,7 @@ interface Props {
value: number;
}
-export default function ThumbLabelComponent(props: Props) {
+export default function ValueLabelComponent(props: Props) {
const { children, value } = props;
const popperRef = React.useRef(null);
diff --git a/packages/material-ui/src/RadioGroup/RadioGroup.js b/packages/material-ui/src/RadioGroup/RadioGroup.js
index 731260b37d9655..cdc77ade46decd 100644
--- a/packages/material-ui/src/RadioGroup/RadioGroup.js
+++ b/packages/material-ui/src/RadioGroup/RadioGroup.js
@@ -81,7 +81,7 @@ RadioGroup.propTypes = {
*/
children: PropTypes.node,
/**
- * The default `input` element value, useful when not controlling the component.
+ * The default `input` element value. Use when the component is not controlled.
*/
defaultValue: PropTypes.any,
/**
diff --git a/packages/material-ui/src/Tabs/Tabs.js b/packages/material-ui/src/Tabs/Tabs.js
index 9dfd56a7155765..ba7bbc038f70e1 100644
--- a/packages/material-ui/src/Tabs/Tabs.js
+++ b/packages/material-ui/src/Tabs/Tabs.js
@@ -463,7 +463,7 @@ Tabs.propTypes = {
*/
ScrollButtonComponent: PropTypes.elementType,
/**
- * Determine behavior of scroll buttons when tabs are set to scroll
+ * Determine behavior of scroll buttons when tabs are set to scroll:
*
* - `auto` will only present them when not all the items are visible.
* - `desktop` will only present them on medium and larger viewports.
diff --git a/packages/material-ui/src/utils/focusVisible.js b/packages/material-ui/src/utils/focusVisible.js
index 1a1e59a4480152..f20ddb884fcabc 100644
--- a/packages/material-ui/src/utils/focusVisible.js
+++ b/packages/material-ui/src/utils/focusVisible.js
@@ -75,7 +75,7 @@ function handleVisibilityChange() {
}
}
-export function prepare(ownerDocument) {
+function prepare(ownerDocument) {
ownerDocument.addEventListener('keydown', handleKeyDown, true);
ownerDocument.addEventListener('mousedown', handlePointerDown, true);
ownerDocument.addEventListener('pointerdown', handlePointerDown, true);
@@ -91,7 +91,7 @@ export function teardown(ownerDocument) {
ownerDocument.removeEventListener('visibilitychange', handleVisibilityChange, true);
}
-export function isFocusVisible(event) {
+function isFocusVisible(event) {
const { target } = event;
try {
return target.matches(':focus-visible');
@@ -110,7 +110,7 @@ export function isFocusVisible(event) {
/**
* Should be called if a blur event is fired on a focus-visible element
*/
-export function handleBlurVisible() {
+function handleBlurVisible() {
// To detect a tab/window switch, we look for a blur event followed
// rapidly by a visibility change.
// If we don't see a visibility change within 100ms, it's probably a
diff --git a/packages/material-ui/src/utils/index.js b/packages/material-ui/src/utils/index.js
index 98bba77882065d..fde589ccd4f444 100644
--- a/packages/material-ui/src/utils/index.js
+++ b/packages/material-ui/src/utils/index.js
@@ -1,8 +1,9 @@
export { default as deprecatedPropType } from './deprecatedPropType';
export * from './helpers';
export * from './reactHelpers';
-export { default as requirePropFactory } from './requirePropFactory';
+export { useIsFocusVisible } from './focusVisible';
export { default as ownerDocument } from './ownerDocument';
export { default as ownerWindow } from './ownerWindow';
+export { default as requirePropFactory } from './requirePropFactory';
export { default as unsupportedProp } from './unsupportedProp';
export { default as withForwardedRef } from './withForwardedRef';
diff --git a/pages/api/filled-input.md b/pages/api/filled-input.md
index 95e7ca66914820..a893115333595f 100644
--- a/pages/api/filled-input.md
+++ b/pages/api/filled-input.md
@@ -22,7 +22,7 @@ import FilledInput from '@material-ui/core/FilledInput';
| autoFocus | bool | | If `true`, the `input` element will be focused during the first mount. |
| classes | object | | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. |
| className | string | | The CSS class name of the wrapper element. |
-| defaultValue | any | | The default `input` element value, useful when not controlling the component. |
+| defaultValue | any | | The default `input` element value. Use when the component is not controlled. |
| disabled | bool | | If `true`, the `input` element will be disabled. |
| disableUnderline | bool | | If `true`, the input will not have an underline. |
| endAdornment | node | | End `InputAdornment` for this component. |
diff --git a/pages/api/input-base.md b/pages/api/input-base.md
index fb29675c77548c..d078839bbc91cd 100644
--- a/pages/api/input-base.md
+++ b/pages/api/input-base.md
@@ -24,7 +24,7 @@ It contains a load of style reset and some state logic.
| autoFocus | bool | | If `true`, the `input` element will be focused during the first mount. |
| classes | object | | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. |
| className | string | | The CSS class name of the wrapper element. |
-| defaultValue | any | | The default `input` element value, useful when not controlling the component. |
+| defaultValue | any | | The default `input` element value. Use when the component is not controlled. |
| disabled | bool | | If `true`, the `input` element will be disabled. |
| endAdornment | node | | End `InputAdornment` for this component. |
| error | bool | | If `true`, the input will indicate an error. This is normally obtained via context from FormControl. |
diff --git a/pages/api/input.md b/pages/api/input.md
index 214b31c3adebf6..4fabfbca8d43e4 100644
--- a/pages/api/input.md
+++ b/pages/api/input.md
@@ -22,7 +22,7 @@ import Input from '@material-ui/core/Input';
| autoFocus | bool | | If `true`, the `input` element will be focused during the first mount. |
| classes | object | | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. |
| className | string | | The CSS class name of the wrapper element. |
-| defaultValue | any | | The default `input` element value, useful when not controlling the component. |
+| defaultValue | any | | The default `input` element value. Use when the component is not controlled. |
| disabled | bool | | If `true`, the `input` element will be disabled. |
| disableUnderline | bool | | If `true`, the input will not have an underline. |
| endAdornment | node | | End `InputAdornment` for this component. |
diff --git a/pages/api/outlined-input.md b/pages/api/outlined-input.md
index 2c3fea414ffd02..4d78f0d9063afb 100644
--- a/pages/api/outlined-input.md
+++ b/pages/api/outlined-input.md
@@ -22,7 +22,7 @@ import OutlinedInput from '@material-ui/core/OutlinedInput';
| autoFocus | bool | | If `true`, the `input` element will be focused during the first mount. |
| classes | object | | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. |
| className | string | | The CSS class name of the wrapper element. |
-| defaultValue | any | | The default `input` element value, useful when not controlling the component. |
+| defaultValue | any | | The default `input` element value. Use when the component is not controlled. |
| disabled | bool | | If `true`, the `input` element will be disabled. |
| endAdornment | node | | End `InputAdornment` for this component. |
| error | bool | | If `true`, the input will indicate an error. This is normally obtained via context from FormControl. |
diff --git a/pages/api/radio-group.md b/pages/api/radio-group.md
index 430648bcc9b892..bcd48d94f2dfd3 100644
--- a/pages/api/radio-group.md
+++ b/pages/api/radio-group.md
@@ -19,7 +19,7 @@ import RadioGroup from '@material-ui/core/RadioGroup';
| Name | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| children | node | | The content of the component. |
-| defaultValue | any | | The default `input` element value, useful when not controlling the component. |
+| defaultValue | any | | The default `input` element value. Use when the component is not controlled. |
| name | string | | The name used to reference the value of the control. |
| onChange | func | | Callback fired when a radio button is selected.
**Signature:** `function(event: object, value: string) => void` *event:* The event source of the callback. You can pull out the new value by accessing `event.target.value`. *value:* The `value` of the selected radio button |
| value | string | | Value of the selected radio button. |
diff --git a/pages/api/slider.md b/pages/api/slider.md
index be02f2e8c52e0a..d5ad85b9472bb3 100644
--- a/pages/api/slider.md
+++ b/pages/api/slider.md
@@ -18,21 +18,29 @@ import Slider from '@material-ui/lab/Slider';
| Name | Type | Default | Description |
|:-----|:-----|:--------|:------------|
+| aria-label | string | | The label of the slider. |
+| aria-labelledby | string | | The id of the element containing a label for the slider. |
+| aria-valuetext | string | | A string value that provides a user-friendly name for the current value of the slider. |
| classes | object | | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. |
-| component | elementType | 'div' | The component used for the root node. Either a string to use a DOM element or a component. |
-| disabled | bool | | If `true`, the slider will be disabled. |
+| component | elementType | 'span' | The component used for the root node. Either a string to use a DOM element or a component. |
+| defaultValue | union: number | arrayOf | | The default element value. Use when the component is not controlled. |
+| disabled | bool | false | If `true`, the slider will be disabled. |
+| getAriaValueText | func | | Accepts a function which returns a string value that provides a user-friendly name for the current value of the slider.
**Signature:** `function(value: number, index: number) => void` *value:* The thumb label's value to format *index:* The thumb label's index to format |
+| marks | union: bool | array | [] | Marks indicate predetermined values to which the user can move the slider. If `true` the marks will be spaced according the value of the `step` prop. If an array, it should contain objects with `value` and an optional `label` keys. |
| max | number | 100 | The maximum allowed value of the slider. Should not be equal to min. |
| min | number | 0 | The minimum allowed value of the slider. Should not be equal to max. |
-| onChange | func | | Callback function that is fired when the slider's value changed. |
-| onDragEnd | func | | Callback function that is fired when the slide has stopped moving. |
-| onDragStart | func | | Callback function that is fired when the slider has begun to move. |
-| step | number | | The granularity the slider can step through values. |
-| thumb | element | | The component used for the slider icon. This is optional, if provided should be a react element. |
-| value * | number | | The value of the slider. |
-| valueReducer | func | function defaultValueReducer(rawValue, props) { const { disabled, step } = props; if (disabled) { return null; } if (step) { return roundToStep(rawValue, step); } return Number(rawValue.toFixed(3));} | the reducer used to process the value emitted from the slider. If `null` or the same value is returned no change is emitted.
**Signature:** `function(rawValue: number, props: SliderProps, event: Event) => void` *rawValue:* value in [min, max] *props:* current props of the Slider *event:* the event the change was triggered from |
-| vertical | bool | | If `true`, the slider will be vertical. |
-
-The component cannot hold a ref.
+| name | string | | Name attribute of the hidden `input` element. |
+| onChange | func | | Callback function that is fired when the slider's value changed.
**Signature:** `function(event: object, value: any) => void` *event:* The event source of the callback *value:* The new value |
+| onChangeCommitted | func | | Callback function that is fired when the `mouseup` is triggered.
**Signature:** `function(event: object, value: any) => void` *event:* The event source of the callback *value:* The new value |
+| orientation | enum: 'horizontal' | 'vertical' | 'horizontal' | The slider orientation. |
+| step | number | 1 | The granularity with which the slider can step through values. (A "discrete" slider.) When step is `null`, the thumb can only be slid onto marks provided with the `marks` prop. |
+| ThumbComponent | elementType | 'span' | The component used to display the value label. |
+| value | union: number | arrayOf | | The value of the slider. For ranged sliders, provide an array with two values. |
+| ValueLabelComponent | elementType | ValueLabel | The value label componnet. |
+| valueLabelDisplay | enum: 'on' | 'auto' | 'off' | 'off' | Controls when the value label is displayed: - `auto` the value label will display when the thumb is hovered or focused. - `on` will display persistently. - `off` will never display. |
+| valueLabelFormat | union: string | func | x => x | The format function the value label's value. When a function is provided, it should have the following signature: - {number} value The value label's value to format - {number} index The value label's index to format |
+
+The `ref` is forwarded to the root element.
Any other properties supplied will be provided to the root element (native element).
@@ -45,19 +53,19 @@ This property accepts the following keys:
| Name | Description |
|:-----|:------------|
| root | Styles applied to the root element.
-| container | Styles applied to the container element.
-| track | Styles applied to the track elements.
-| trackBefore | Styles applied to the track element before the thumb.
-| trackAfter | Styles applied to the track element after the thumb.
-| thumbWrapper | Styles applied to the thumb wrapper element.
+| marked | Styles applied to the root element if `marks` is provided with at least one label.
+| vertical | Pseudo-class applied to the root element if `orientation="vertical"`.
+| disabled | Pseudo-class applied to the root element if `disabled={true}`.
+| rail | Styles applied to the rail element.
+| track | Styles applied to the track element.
| thumb | Styles applied to the thumb element.
-| thumbIconWrapper | Class applied to the thumb element if custom thumb icon provided.
-| thumbIcon |
-| disabled | Class applied to the track and thumb elements to trigger JSS nested styles if `disabled`.
-| jumped | Class applied to the track and thumb elements to trigger JSS nested styles if `jumped`.
-| focused | Class applied to the track and thumb elements to trigger JSS nested styles if `focused`.
-| activated | Class applied to the track and thumb elements to trigger JSS nested styles if `activated`.
-| vertical | Class applied to the root, track and container to trigger JSS nested styles if `vertical`.
+| active | Pseudo-class applied to the thumb element if it's active.
+| focusVisible | Pseudo-class applied to the thumb element if keyboard focused.
+| valueLabel | Styles applied to the thumb label element.
+| mark | Styles applied to the mark element.
+| markActive | Styles applied to the mark element if active (depending on the value).
+| markLabel | Styles applied to the mark label element.
+| markLabelActive | Styles applied to the mark label element if active (depending on the value).
Have a look at the [overriding styles with classes](/customization/components/#overriding-styles-with-classes) section
and the [implementation of the component](https://github.com/mui-org/material-ui/blob/master/packages/material-ui-lab/src/Slider/Slider.js)
diff --git a/pages/api/tabs.md b/pages/api/tabs.md
index dfe437d8258ca1..5eebe61939d772 100644
--- a/pages/api/tabs.md
+++ b/pages/api/tabs.md
@@ -26,7 +26,7 @@ import Tabs from '@material-ui/core/Tabs';
| indicatorColor | enum: 'secondary' | 'primary' | 'secondary' | Determines the color of the indicator. |
| onChange | func | | Callback fired when the value changes.
**Signature:** `function(event: object, value: any) => void` *event:* The event source of the callback *value:* We default to the index of the child (number) |
| ScrollButtonComponent | elementType | TabScrollButton | The component used to render the scroll buttons. |
-| scrollButtons | enum: 'auto' | 'desktop' | 'on' | 'off' | 'auto' | Determine behavior of scroll buttons when tabs are set to scroll - `auto` will only present them when not all the items are visible. - `desktop` will only present them on medium and larger viewports. - `on` will always present them. - `off` will never present them. |
+| scrollButtons | enum: 'auto' | 'desktop' | 'on' | 'off' | 'auto' | Determine behavior of scroll buttons when tabs are set to scroll: - `auto` will only present them when not all the items are visible. - `desktop` will only present them on medium and larger viewports. - `on` will always present them. - `off` will never present them. |
| TabIndicatorProps | object | | Properties applied to the `TabIndicator` element. |
| textColor | enum: 'secondary' | 'primary' | 'inherit' | 'inherit' | Determines the color of the `Tab`. |
| value | any | | The value of the currently selected `Tab`. If you don't want any selected `Tab`, you can set this property to `false`. |
diff --git a/scripts/sizeSnapshot/webpack.config.js b/scripts/sizeSnapshot/webpack.config.js
index 69d34b87cc075c..25a23da5618b24 100644
--- a/scripts/sizeSnapshot/webpack.config.js
+++ b/scripts/sizeSnapshot/webpack.config.js
@@ -96,6 +96,12 @@ async function getSizeLimitBundles() {
webpack: true,
path: 'packages/material-ui/build/esm/InputBase/Textarea.js',
},
+ {
+ // vs https://bundlephobia.com/result?p=rc-slider
+ name: 'Slider',
+ webpack: true,
+ path: 'packages/material-ui-lab/build/esm/Slider/index.js',
+ },
{
name: 'docs.main',
webpack: false,