From 30659ae4d8a589d123d5d45c39c57255379a7a51 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 9 Nov 2021 14:02:32 -0800 Subject: [PATCH 01/53] init refactor of slider --- packages/react-slider/src/Slider.stories.tsx | 223 ++++++------- .../src/components/Slider/Slider.types.ts | 25 -- .../src/components/Slider/renderSlider.tsx | 16 +- .../src/components/Slider/useSlider.ts | 25 +- .../src/components/Slider/useSliderState.tsx | 42 +-- .../src/components/Slider/useSliderStyles.ts | 305 +++--------------- 6 files changed, 173 insertions(+), 463 deletions(-) diff --git a/packages/react-slider/src/Slider.stories.tsx b/packages/react-slider/src/Slider.stories.tsx index 7a485f0f1313d..ba9f30f66fdb8 100644 --- a/packages/react-slider/src/Slider.stories.tsx +++ b/packages/react-slider/src/Slider.stories.tsx @@ -55,9 +55,9 @@ export const BasicSliderExample = (props: SliderProps) => { return (
- - - + {/* + */} + {/* { keyboardStep={2} onChange={onSliderChange} input={{ id: 'controlled-slider' }} - /> + /> */} @@ -77,118 +77,125 @@ export const BasicSliderExample = (props: SliderProps) => { ); }; -export const MarkedSliderExample = (props: SliderProps) => { - const styles = useStyles(); +// export const MarkedSliderExample = (props: SliderProps) => { +// const styles = useStyles(); - return ( -
- - - - - - , - }, - { value: 9, label: '9 oz' }, - ]} - max={10} - /> - - - - , - }, - { - value: 8, - label: '8', - mark: , - }, - { - value: 9, - mark: , - }, - ]} - max={10} - /> - -
- - - - , - }, - 9, - ]} - max={10} - /> -
-
- ); -}; +// return ( +//
+// +// +// +// +// +// , +// }, +// { value: 9, label: '9 oz' }, +// ]} +// max={10} +// /> +// +// +// +// , +// }, +// { +// value: 8, +// label: '8', +// mark: , +// }, +// { +// value: 9, +// mark: , +// }, +// ]} +// max={10} +// /> +// +//
+// +// +// +// , +// }, +// 9, +// ]} +// max={10} +// /> +//
+//
+// ); +// }; -export const VerticalSliderExample = (props: SliderProps) => { - const [sliderValue, setSliderValue] = React.useState(160); - const onSliderChange = ( - ev: React.PointerEvent | React.KeyboardEvent, - data: { value: number }, - ) => setSliderValue(data.value); +// export const VerticalSliderExample = (props: SliderProps) => { +// const [sliderValue, setSliderValue] = React.useState(160); +// const onSliderChange = ( +// ev: React.PointerEvent | React.KeyboardEvent, +// data: { value: number }, +// ) => setSliderValue(data.value); - const styles = useStyles(); +// const styles = useStyles(); - return ( -
- - - - - - - - -
- ); -}; +// return ( +//
+// +// +// +// +// +// +// +// +//
+// ); +// }; -export const CustomSliderExample = (props: SliderProps) => { - const styles = useStyles(); +// export const CustomSliderExample = (props: SliderProps) => { +// const styles = useStyles(); - return ( -
- - - - -
- ); -}; +// return ( +//
+// +// +// +// +//
+// ); +// }; export default { title: 'Components/Slider', component: Slider, + decorators: [ + Story => ( +
+ +
+ ), + ], } as Meta; diff --git a/packages/react-slider/src/components/Slider/Slider.types.ts b/packages/react-slider/src/components/Slider/Slider.types.ts index ffe4f4c9d54d5..d4904f2032d15 100644 --- a/packages/react-slider/src/components/Slider/Slider.types.ts +++ b/packages/react-slider/src/components/Slider/Slider.types.ts @@ -12,42 +12,17 @@ export type SliderSlots = { */ rail: IntrinsicShorthandProps<'div'>; - /** - * The wrapper around the Slider component. - */ - sliderWrapper: IntrinsicShorthandProps<'div'>; - - /** - * The wrapper around the Slider's track. It is primarily used to handle the positioning of the track. - */ - trackWrapper: IntrinsicShorthandProps<'div'>; - /** * The bar showing the current selected area adjacent to the Slider's thumb. */ track: IntrinsicShorthandProps<'div'>; - /** - * The wrapper holding the marks and mark labels for the Slider. - */ - marksWrapper: IntrinsicShorthandProps<'div'>; - - /** - * The wrapper around the Slider's thumb. It is primarily used to handle the dragging animation from translateX. - */ - thumbWrapper: IntrinsicShorthandProps<'div'>; - /** * The draggable icon used to select a given value from the Slider. * This is the element containing `role = 'slider'`. */ thumb: IntrinsicShorthandProps<'div'>; - /** - * The area in which the Slider's rail allows for the thumb to be dragged. - */ - activeRail: IntrinsicShorthandProps<'div'>; - /** * The hidden input for the Slider. */ diff --git a/packages/react-slider/src/components/Slider/renderSlider.tsx b/packages/react-slider/src/components/Slider/renderSlider.tsx index f0cca69d2e07c..e6f871de558f2 100644 --- a/packages/react-slider/src/components/Slider/renderSlider.tsx +++ b/packages/react-slider/src/components/Slider/renderSlider.tsx @@ -11,18 +11,10 @@ export const renderSlider = (state: SliderState) => { return ( - {state.marks && } - - - - - - - - - - - + + + + ); }; diff --git a/packages/react-slider/src/components/Slider/useSlider.ts b/packages/react-slider/src/components/Slider/useSlider.ts index ec51a0d6de1c6..a7f588522173a 100644 --- a/packages/react-slider/src/components/Slider/useSlider.ts +++ b/packages/react-slider/src/components/Slider/useSlider.ts @@ -6,18 +6,7 @@ import { SliderProps, SliderSlots, SliderState } from './Slider.types'; /** * Array of all shorthand properties listed in sliderShorthandProps */ -export const sliderShorthandProps: (keyof SliderSlots)[] = [ - 'root', - 'activeRail', - 'input', - 'rail', - 'sliderWrapper', - 'thumb', - 'thumbWrapper', - 'track', - 'trackWrapper', - 'marksWrapper', -]; +export const sliderShorthandProps: (keyof SliderSlots)[] = ['root', 'activeRail', 'input', 'rail', 'thumb', 'track']; /** * Given user props, returns state and render function for a Slider. @@ -42,13 +31,9 @@ export const useSlider = (props: SliderProps, ref: React.Ref): Slid // Slots activeRail, input, - marksWrapper, rail, - sliderWrapper, thumb, - thumbWrapper, track, - trackWrapper, } = props; const state: SliderState = { @@ -68,14 +53,10 @@ export const useSlider = (props: SliderProps, ref: React.Ref): Slid components: { activeRail: 'div', input: 'input', - marksWrapper: 'div', rail: 'div', root: 'div', - sliderWrapper: 'div', thumb: 'div', - thumbWrapper: 'div', track: 'div', - trackWrapper: 'div', }, root: getNativeElementProps('span', { ref, @@ -89,13 +70,9 @@ export const useSlider = (props: SliderProps, ref: React.Ref): Slid type: 'range', }, }), - marksWrapper: resolveShorthand(marksWrapper, { required: true }), rail: resolveShorthand(rail, { required: true }), - sliderWrapper: resolveShorthand(sliderWrapper, { required: true }), thumb: resolveShorthand(thumb, { required: true }), - thumbWrapper: resolveShorthand(thumbWrapper, { required: true }), track: resolveShorthand(track, { required: true }), - trackWrapper: resolveShorthand(trackWrapper, { required: true }), }; useSliderState(state); diff --git a/packages/react-slider/src/components/Slider/useSliderState.tsx b/packages/react-slider/src/components/Slider/useSliderState.tsx index 8768af712256e..e76ccf42bcd3f 100644 --- a/packages/react-slider/src/components/Slider/useSliderState.tsx +++ b/packages/react-slider/src/components/Slider/useSliderState.tsx @@ -11,11 +11,11 @@ import { import { calculateSteps, getKeydownValue, - getMarkPercent, - getMarkValue, + // getMarkPercent, + // getMarkValue, getPercent, on, - renderMarks, + // renderMarks, } from '../../utils/index'; import type { SliderState } from './Slider.types'; @@ -33,7 +33,7 @@ export const useSliderState = (state: SliderState) => { disabled = false, ariaValueText, onChange, - marks, + // marks, vertical = false, origin, } = state; @@ -91,7 +91,6 @@ export const useSliderState = (state: SliderState) => { (ev: React.PointerEvent): void => { const position = calculateSteps(ev, railRef, min, max, step, vertical, dir); const currentStepPosition = Math.round(position / step) * step; - setRenderedPosition(position); updateValue(currentStepPosition, ev); }, @@ -167,16 +166,14 @@ export const useSliderState = (state: SliderState) => { return origin !== undefined ? getPercent(origin, min, max) : 0; }, [max, min, origin]); - const markValues = React.useMemo((): number[] => getMarkValue(marks, min, max, step), [marks, max, min, step]); + // const markValues = React.useMemo((): number[] => getMarkValue(marks, min, max, step), [marks, max, min, step]); - const markPercent = React.useMemo((): string[] => getMarkPercent(markValues), [markValues]); + // const markPercent = React.useMemo((): string[] => getMarkPercent(markValues), [markValues]); - const thumbWrapperStyles = { - transform: vertical - ? `translateY(${valuePercent}%)` - : `translateX(${dir === 'rtl' ? -valuePercent : valuePercent}%)`, - transition: stepAnimation ? `transform ease-in-out ${animationTime}` : 'none', - ...state.thumbWrapper.style, + const thumbStyles = { + [vertical ? 'top' : 'left']: valuePercent + '%', + transition: stepAnimation ? `left ease-in-out ${animationTime}` : 'none', + ...state.thumb.style, }; const trackStyles = { @@ -202,13 +199,6 @@ export const useSliderState = (state: SliderState) => { ...state.track.style, }; - const marksWrapperStyles = marks - ? { - [vertical ? 'gridTemplateRows' : 'gridTemplateColumns']: markPercent.join(''), - ...state.marksWrapper.style, - } - : {}; - // Root props if (!disabled) { state.root.onPointerDown = onPointerDown; @@ -218,17 +208,11 @@ export const useSliderState = (state: SliderState) => { // Track Props state.track.style = trackStyles; - // Mark props - if (marks) { - state.marksWrapper.children = renderMarks(markValues, marks); - state.marksWrapper.style = marksWrapperStyles; - } - - // Thumb Wrapper Props - state.thumbWrapper.style = thumbWrapperStyles; + // Thumb Props + state.thumb.style = thumbStyles; // Active Rail Props - state.activeRail.ref = railRef; + state.rail.ref = railRef; // Input Props state.input.ref = useMergedRefs(state.input.ref, inputRef); diff --git a/packages/react-slider/src/components/Slider/useSliderStyles.ts b/packages/react-slider/src/components/Slider/useSliderStyles.ts index 8349f59d7aca3..8af22321ca1d5 100644 --- a/packages/react-slider/src/components/Slider/useSliderStyles.ts +++ b/packages/react-slider/src/components/Slider/useSliderStyles.ts @@ -1,28 +1,30 @@ import { makeStyles, mergeClasses } from '@fluentui/react-make-styles'; import { createFocusOutlineStyle } from '@fluentui/react-tabster'; +import { calculateSteps } from '../../utils/calculateSteps'; import type { SliderState } from './Slider.types'; export const sliderClassName = 'fui-Slider'; const thumbClassName = `${sliderClassName}-thumb`; const trackClassName = `${sliderClassName}-track`; -const markContainerClassName = `${sliderClassName}-markItemContainer`; -const firstMarkClassName = `${sliderClassName}-firstMark`; -const lastMarkClassName = `${sliderClassName}-lastMark`; -const markClassName = `${sliderClassName}-mark`; -const markLabelClassName = `${sliderClassName}-label`; +const railClassName = `${sliderClassName}-rail`; +// const markContainerClassName = `${sliderClassName}-markItemContainer`; +// const firstMarkClassName = `${sliderClassName}-firstMark`; +// const lastMarkClassName = `${sliderClassName}-lastMark`; +// const markClassName = `${sliderClassName}-mark`; +// const markLabelClassName = `${sliderClassName}-label`; /** * Styles for the root slot */ export const useRootStyles = makeStyles({ - root: theme => ({ + root: { position: 'relative', - display: 'inline-flex', + display: 'inline-grid', + gridTemplateAreas: '"slider"', userSelect: 'none', touchAction: 'none', - verticalAlign: 'bottom', - }), + }, small: { '--slider-thumb-size': '10px', @@ -36,18 +38,19 @@ export const useRootStyles = makeStyles({ '--slider-mark-size': '4px', }, - horizontal: theme => ({ + horizontal: { minWidth: '120px', - minHeight: 'var(--slider-thumb-size)', - flexDirection: 'column', - }), + height: 'var(--slider-thumb-size)', + alignItems: 'center', + }, - vertical: theme => ({ + vertical: { transform: 'scaleY(-1)', - minWidth: 'var(--slider-thumb-size)', + width: 'var(--slider-thumb-size)', minHeight: '120px', - flexDirection: 'row', - }), + justifyItems: 'center', + gridTemplateColumns: 'var(--slider-thumb-size)', + }, enabled: theme => ({ cursor: 'grab', @@ -75,31 +78,7 @@ export const useRootStyles = makeStyles({ }), focusIndicator: theme => - createFocusOutlineStyle(theme, { selector: 'focus-within', style: { outlineOffset: '6px' } }), -}); - -/** - * Styles for the slider wrapper slot - */ -export const useSliderWrapper = makeStyles({ - sliderWrapper: theme => ({ - position: 'absolute', - overflow: 'hidden', - }), - - horizontal: theme => ({ - left: '0px', - right: '0px', - top: '0px', - minHeight: 'var(--slider-thumb-size)', - }), - - vertical: theme => ({ - top: '0px', - bottom: '0px', - left: '0px', - minWidth: 'var(--slider-thumb-size)', - }), + createFocusOutlineStyle(theme, { selector: 'focus-within', style: { outlineOffset: '10px' } }), }); /** @@ -107,10 +86,9 @@ export const useSliderWrapper = makeStyles({ */ export const useRailStyles = makeStyles({ rail: theme => ({ - position: 'absolute', borderRadius: theme.borderRadiusXLarge, - boxSizing: 'border-box', pointerEvents: 'none', + gridArea: 'slider', }), enabled: theme => ({ @@ -122,42 +100,14 @@ export const useRailStyles = makeStyles({ border: `1px solid ${theme.colorTransparentStrokeDisabled}`, }), - horizontal: theme => ({ + horizontal: { height: 'var(--slider-rail-size)', - top: '50%', - left: 'calc(var(--slider-thumb-size) * .5)', - right: 'calc(var(--slider-thumb-size) * .5)', - transform: 'translateY(-50%)', - }), + }, - vertical: theme => ({ + vertical: { width: 'var(--slider-rail-size)', - left: '50%', - top: 'calc(var(--slider-thumb-size) * .5)', - bottom: 'calc(var(--slider-thumb-size) * .5)', - transform: 'translateX(-50%)', - }), -}); - -/** - * Styles for the trackWrapper slot - */ -export const useTrackWrapperStyles = makeStyles({ - trackWrapper: theme => ({ - position: 'absolute', - }), - - horizontal: theme => ({ - top: '50%', - left: 'calc(var(--slider-thumb-size) * .5)', - right: 'calc(var(--slider-thumb-size) * .5)', - }), - - vertical: theme => ({ - left: '50%', - top: 'calc(var(--slider-thumb-size) * .5)', - bottom: 'calc(var(--slider-thumb-size) * .5)', - }), + height: '100%', + }, }); /** @@ -165,23 +115,18 @@ export const useTrackWrapperStyles = makeStyles({ */ export const useTrackStyles = makeStyles({ track: theme => ({ - position: 'absolute', borderRadius: theme.borderRadiusXLarge, + gridArea: 'slider', + position: 'relative', }), - horizontal: theme => ({ + horizontal: { height: 'var(--slider-rail-size)', - top: '50%', - transform: 'translateY(-50%)', - minWidth: 'calc(var(--slider-thumb-size) / 4)', - }), + }, - vertical: theme => ({ + vertical: { width: 'var(--slider-rail-size)', - left: '50%', - transform: 'translateX(-50%)', - minHeight: 'calc(var(--slider-thumb-size) / 4)', - }), + }, enabled: theme => ({ background: theme.colorCompoundBrandBackground, @@ -192,111 +137,6 @@ export const useTrackStyles = makeStyles({ }), }); -/** - * Styles for the mark slot - */ -export const useMarksWrapperStyles = makeStyles({ - marksWrapper: theme => ({ - position: 'relative', - display: 'grid', - outline: 'none', - zIndex: '1', - whiteSpace: 'nowrap', - [`& .${markClassName}`]: { - background: theme.colorNeutralBackground1, - }, - - [`& .${markLabelClassName}`]: { - padding: '2px', - fontSize: '12px', - }, - - [`& .${firstMarkClassName}, .${lastMarkClassName}`]: { - opacity: '0', - }, - }), - - horizontal: theme => ({ - marginTop: 'calc(var(--slider-rail-size) + var(--slider-mark-size))', - marginLeft: 'calc(var(--slider-thumb-size) / 2)', - marginRight: 'calc(var(--slider-thumb-size) / 2)', - justifyItems: 'end', - - [`& .${markContainerClassName}`]: { - display: 'flex', - flexDirection: 'column', - transform: 'translateX(50%)', - alignItems: 'center', - }, - - [`& .${markLabelClassName}`]: { - fontFamily: theme.fontFamilyBase, - color: theme.colorNeutralForeground1, - paddingTop: 'calc(var(--slider-thumb-size) /2 )', - }, - - [`& .${markClassName}`]: { - height: '4px', - width: '1px', - }, - }), - - vertical: theme => ({ - marginTop: 'calc(var(--slider-thumb-size) / 2)', - marginBottom: 'calc(var(--slider-thumb-size) / 2)', - marginLeft: 'calc(var(--slider-rail-size) + var(--slider-mark-size))', - justifyItems: 'start', - - [`& .${markContainerClassName}`]: { - display: 'flex', - flexDirection: 'row', - transform: 'translateY(50%)', - alignItems: 'center', - maxWidth: '100%', - maxHeight: '100%', - }, - - [`& .${markLabelClassName}`]: { - paddingLeft: 'calc(var(--slider-thumb-size) /2 )', - transform: 'scaleY(-1)', - }, - - [`& .${markClassName}`]: { - height: '1px', - width: 'var(--slider-mark-size)', - }, - }), - - disabled: theme => ({ - [`& .${markLabelClassName}`]: { - color: theme.colorNeutralForegroundDisabled, - }, - }), -}); - -/** - * Styles for the thumb slot - */ -export const useThumbWrapperStyles = makeStyles({ - thumbWrapper: theme => ({ - position: 'absolute', - outline: 'none', - zIndex: '2', - }), - - horizontal: theme => ({ - left: 'calc(var(--slider-thumb-size) / 2)', - right: 'calc(var(--slider-thumb-size) / 2)', - top: '50%', - }), - - vertical: theme => ({ - top: 'calc(var(--slider-thumb-size) / 2)', - bottom: 'calc(var(--slider-thumb-size) / 2)', - left: '50%', - }), -}); - /** * Styles for the thumb slot */ @@ -305,15 +145,11 @@ export const useThumbStyles = makeStyles({ position: 'absolute', width: 'var(--slider-thumb-size)', height: 'var(--slider-thumb-size)', - top: '0px', - left: '0px', - bottom: '0px', - right: '0px', outline: 'none', borderRadius: theme.borderRadiusCircular, - boxSizing: 'border-box', boxShadow: `0 0 0 calc(var(--slider-thumb-size) * .2) ${theme.colorNeutralBackground1} inset`, - transform: 'translate(-50%, -50%)', + transform: 'translateX(-50%)', + left: 0, ':before': { position: 'absolute', @@ -339,27 +175,8 @@ export const useThumbStyles = makeStyles({ }, }), - horizontal: theme => ({ - top: '50%', - }), -}); - -/** - * Styles for the activeRail slot - */ -export const useActiveRailStyles = makeStyles({ - activeRail: theme => ({ - position: 'absolute', - }), - - horizontal: theme => ({ - left: 'calc(var(--slider-thumb-size) / 2)', - right: 'calc(var(--slider-thumb-size) / 2)', - }), - vertical: theme => ({ - top: 'calc(var(--slider-thumb-size) / 2)', - bottom: 'calc(var(--slider-thumb-size) / 2)', + transform: 'translateY(-50%)', }), }); @@ -369,11 +186,10 @@ export const useActiveRailStyles = makeStyles({ const useInputStyles = makeStyles({ input: { opacity: 0, - position: 'absolute', + gridArea: 'slider', padding: 0, margin: 0, - width: '100%', - height: '100%', + height: 'var(--slider-thumb-size)', touchAction: 'none', pointerEvents: 'none', }, @@ -384,14 +200,9 @@ const useInputStyles = makeStyles({ */ export const useSliderStyles = (state: SliderState): SliderState => { const rootStyles = useRootStyles(); - const sliderWrapperStyles = useSliderWrapper(); const railStyles = useRailStyles(); - const trackWrapperStyles = useTrackWrapperStyles(); const trackStyles = useTrackStyles(); - const marksWrapperStyles = useMarksWrapperStyles(); - const thumbWrapperStyles = useThumbWrapperStyles(); const thumbStyles = useThumbStyles(); - const activeRailStyles = useActiveRailStyles(); const inputStyles = useInputStyles(); state.root.className = mergeClasses( @@ -405,31 +216,14 @@ export const useSliderStyles = (state: SliderState): SliderState => { state.root.className, ); - state.sliderWrapper.className = mergeClasses( - sliderWrapperStyles.sliderWrapper, - state.vertical ? sliderWrapperStyles.vertical : sliderWrapperStyles.horizontal, - state.sliderWrapper.className, - ); - state.rail.className = mergeClasses( + railClassName, railStyles.rail, state.vertical ? railStyles.vertical : railStyles.horizontal, state.disabled ? railStyles.disabled : railStyles.enabled, state.rail.className, ); - state.sliderWrapper.className = mergeClasses( - sliderWrapperStyles.sliderWrapper, - state.vertical ? sliderWrapperStyles.vertical : sliderWrapperStyles.horizontal, - state.sliderWrapper.className, - ); - - state.trackWrapper.className = mergeClasses( - trackWrapperStyles.trackWrapper, - state.vertical ? trackWrapperStyles.vertical : trackWrapperStyles.horizontal, - state.trackWrapper.className, - ); - state.track.className = mergeClasses( trackClassName, trackStyles.track, @@ -438,33 +232,14 @@ export const useSliderStyles = (state: SliderState): SliderState => { state.track.className, ); - state.marksWrapper.className = mergeClasses( - marksWrapperStyles.marksWrapper, - state.vertical ? marksWrapperStyles.vertical : marksWrapperStyles.horizontal, - state.disabled && marksWrapperStyles.disabled, - state.marksWrapper.className, - ); - - state.thumbWrapper.className = mergeClasses( - thumbWrapperStyles.thumbWrapper, - state.vertical ? thumbWrapperStyles.vertical : thumbWrapperStyles.horizontal, - state.thumbWrapper.className, - ); - state.thumb.className = mergeClasses( thumbClassName, thumbStyles.thumb, - !state.vertical && thumbStyles.horizontal, + state.vertical && thumbStyles.vertical, state.disabled ? thumbStyles.disabled : thumbStyles.enabled, state.thumb.className, ); - state.activeRail.className = mergeClasses( - activeRailStyles.activeRail, - state.vertical ? activeRailStyles.vertical : activeRailStyles.horizontal, - state.activeRail.className, - ); - state.input.className = mergeClasses(inputStyles.input, state.input.className); return state; From 8ad4cfd8f6c58730ae9c9ac1cd513c00ab893b18 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Fri, 12 Nov 2021 07:48:55 -0800 Subject: [PATCH 02/53] start bringing marks back in --- packages/react-slider/src/Slider.stories.tsx | 178 +++++++++--------- .../src/components/Slider/useSliderState.tsx | 18 +- 2 files changed, 101 insertions(+), 95 deletions(-) diff --git a/packages/react-slider/src/Slider.stories.tsx b/packages/react-slider/src/Slider.stories.tsx index ba9f30f66fdb8..3ab76d20bf42c 100644 --- a/packages/react-slider/src/Slider.stories.tsx +++ b/packages/react-slider/src/Slider.stories.tsx @@ -44,106 +44,106 @@ const CustomLabel = () => ( /> ); -export const BasicSliderExample = (props: SliderProps) => { - const [sliderValue, setSliderValue] = React.useState(160); - const onSliderChange = ( - ev: React.PointerEvent | React.KeyboardEvent, - data: { value: number }, - ) => setSliderValue(data.value); - - const styles = useStyles(); - - return ( -
- {/* - */} - {/* - */} - - - - - - -
- ); -}; +// export const BasicSliderExample = (props: SliderProps) => { +// const [sliderValue, setSliderValue] = React.useState(160); +// const onSliderChange = ( +// ev: React.PointerEvent | React.KeyboardEvent, +// data: { value: number }, +// ) => setSliderValue(data.value); -// export const MarkedSliderExample = (props: SliderProps) => { // const styles = useStyles(); // return ( //
-// -// -// -// -// -// , -// }, -// { value: 9, label: '9 oz' }, -// ]} -// max={10} -// /> -// -// -// +// +// +// {/* // , -// }, -// { -// value: 8, -// label: '8', -// mark: , -// }, -// { -// value: 9, -// mark: , -// }, -// ]} -// max={10} +// value={sliderValue} +// min={10} +// max={200} +// step={10} +// keyboardStep={2} +// onChange={onSliderChange} +// input={{ id: 'controlled-slider' }} // /> -// -//
-// -// -// -// , -// }, -// 9, -// ]} -// max={10} -// /> -//
+// +// +// +// +// +// */} //
// ); // }; +export const BasicSliderExample = (props: SliderProps) => { + const styles = useStyles(); + + return ( +
+ + + + + + , + }, + { value: 9, label: '9 oz' }, + ]} + max={10} + /> + + + + , + }, + { + value: 8, + label: '8', + mark: , + }, + { + value: 9, + mark: , + }, + ]} + max={10} + /> + +
+ + + + , + }, + 9, + ]} + max={10} + /> +
+
+ ); +}; + // export const VerticalSliderExample = (props: SliderProps) => { // const [sliderValue, setSliderValue] = React.useState(160); // const onSliderChange = ( diff --git a/packages/react-slider/src/components/Slider/useSliderState.tsx b/packages/react-slider/src/components/Slider/useSliderState.tsx index e76ccf42bcd3f..316b52c3fdbc5 100644 --- a/packages/react-slider/src/components/Slider/useSliderState.tsx +++ b/packages/react-slider/src/components/Slider/useSliderState.tsx @@ -11,11 +11,11 @@ import { import { calculateSteps, getKeydownValue, - // getMarkPercent, - // getMarkValue, + getMarkPercent, + getMarkValue, getPercent, on, - // renderMarks, + renderMarks, } from '../../utils/index'; import type { SliderState } from './Slider.types'; @@ -33,7 +33,7 @@ export const useSliderState = (state: SliderState) => { disabled = false, ariaValueText, onChange, - // marks, + marks, vertical = false, origin, } = state; @@ -166,9 +166,9 @@ export const useSliderState = (state: SliderState) => { return origin !== undefined ? getPercent(origin, min, max) : 0; }, [max, min, origin]); - // const markValues = React.useMemo((): number[] => getMarkValue(marks, min, max, step), [marks, max, min, step]); + const markValues = React.useMemo((): number[] => getMarkValue(marks, min, max, step), [marks, max, min, step]); - // const markPercent = React.useMemo((): string[] => getMarkPercent(markValues), [markValues]); + const markPercent = React.useMemo((): string[] => getMarkPercent(markValues), [markValues]); const thumbStyles = { [vertical ? 'top' : 'left']: valuePercent + '%', @@ -208,6 +208,12 @@ export const useSliderState = (state: SliderState) => { // Track Props state.track.style = trackStyles; + // Mark props + // if (marks) { + // state.marksWrapper.children = renderMarks(markValues, marks); + // state.marksWrapper.style = marksWrapperStyles; + // } + // Thumb Props state.thumb.style = thumbStyles; From 0c9ad2a57fe539afe3dbf115bc4e0d6483ba0a2e Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Thu, 18 Nov 2021 10:46:07 -0800 Subject: [PATCH 03/53] story cleanup --- packages/react-components/package.json | 1 + .../src/components/Slider/Slider.stories.tsx | 2 +- .../Slider/stories/SliderControlled.stories.tsx | 9 ++++----- .../Slider/stories/SliderDefault.stories.tsx | 13 +++++++------ .../Slider/stories/SliderDisabled.stories.tsx | 11 +++++------ .../Slider/stories/SliderOrigin.stories.tsx | 11 +++++------ .../Slider/stories/SliderStep.stories.tsx | 11 +++++------ packages/react-slider/src/index.ts | 1 - 8 files changed, 28 insertions(+), 31 deletions(-) diff --git a/packages/react-components/package.json b/packages/react-components/package.json index 71f945ac7244d..e03bdba7229bd 100644 --- a/packages/react-components/package.json +++ b/packages/react-components/package.json @@ -51,6 +51,7 @@ "@fluentui/react-popover": "9.0.0-beta.4", "@fluentui/react-portal": "9.0.0-beta.4", "@fluentui/react-provider": "9.0.0-beta.4", + "@fluentui/react-slider": "9.0.0-beta.4", "@fluentui/react-theme": "9.0.0-beta.3", "@fluentui/react-tooltip": "9.0.0-beta.4", "@fluentui/react-utilities": "9.0.0-beta.3", diff --git a/packages/react-slider/src/components/Slider/Slider.stories.tsx b/packages/react-slider/src/components/Slider/Slider.stories.tsx index fc53efea985ad..f7730aa1034ac 100644 --- a/packages/react-slider/src/components/Slider/Slider.stories.tsx +++ b/packages/react-slider/src/components/Slider/Slider.stories.tsx @@ -13,7 +13,7 @@ export default { component: Slider, decorators: [ Story => ( -
+
), diff --git a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx index 6b252eb7fe583..2793cbc5947a9 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx @@ -1,9 +1,8 @@ import * as React from 'react'; import { Label } from '@fluentui/react-label'; -import { Slider } from '../../../index'; -import type { SliderProps } from '../../../index'; +import { Slider } from '../../../index'; // codesandbox-dependency: @fluentui/react-slider ^9.0.0-beta -export const Controlled = (props: SliderProps) => { +export const Controlled = () => { const [sliderValue, setSliderValue] = React.useState(160); const onSliderChange = ( ev: React.PointerEvent | React.KeyboardEvent, @@ -11,7 +10,7 @@ export const Controlled = (props: SliderProps) => { ) => setSliderValue(data.value); return ( -
+ <> { onChange={onSliderChange} input={{ id: 'controlled-slider' }} /> -
+ ); }; diff --git a/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx index 20ce57d847ded..05740d7b01cf4 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx @@ -1,11 +1,12 @@ import * as React from 'react'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore import { Label } from '@fluentui/react-label'; -import { Slider } from '../../../index'; -import type { SliderProps } from '../../../index'; +import { Slider } from '../../../index'; // codesandbox-dependency: @fluentui/react-slider ^9.0.0-beta -export const Default = (props: SliderProps) => { -
+export const Default = () => ( + <> -
; -}; + +); diff --git a/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx index 5ce0b9cca4bd4..3b8d58e512be8 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx @@ -1,11 +1,10 @@ import * as React from 'react'; import { Label } from '@fluentui/react-label'; -import { Slider } from '../../../index'; -import type { SliderProps } from '../../../index'; +import { Slider } from '../../../index'; // codesandbox-dependency: @fluentui/react-slider ^9.0.0-beta -export const Disabled = (props: SliderProps) => { -
+export const Disabled = () => ( + <> -
; -}; + +); diff --git a/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx index 9e8d3fe529546..4431c6e2e7c6f 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx @@ -1,11 +1,10 @@ import * as React from 'react'; import { Label } from '@fluentui/react-label'; -import { Slider } from '../../../index'; -import type { SliderProps } from '../../../index'; +import { Slider } from '../../../index'; // codesandbox-dependency: @fluentui/react-slider ^9.0.0-beta -export const Origin = (props: SliderProps) => { -
+export const Origin = () => ( + <> -
; -}; + +); diff --git a/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx index c298f95eb4189..4714185693a8b 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx @@ -1,11 +1,10 @@ import * as React from 'react'; import { Label } from '@fluentui/react-label'; -import { Slider } from '../../../index'; -import type { SliderProps } from '../../../index'; +import { Slider } from '../../../index'; // codesandbox-dependency: @fluentui/react-slider ^9.0.0-beta -export const Step = (props: SliderProps) => { -
+export const Step = () => ( + <> -
; -}; + +); diff --git a/packages/react-slider/src/index.ts b/packages/react-slider/src/index.ts index e84dd8b7f473c..f48a8541581d5 100644 --- a/packages/react-slider/src/index.ts +++ b/packages/react-slider/src/index.ts @@ -1,2 +1 @@ export * from './Slider'; -export * from './RangedSlider'; From 50c6f84a4fa3a3c96cd7aa49728cafccd0c54b8d Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Thu, 18 Nov 2021 10:56:11 -0800 Subject: [PATCH 04/53] cleanup --- packages/react-slider/src/components/Slider/Slider.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-slider/src/components/Slider/Slider.stories.tsx b/packages/react-slider/src/components/Slider/Slider.stories.tsx index f7730aa1034ac..4202323c37006 100644 --- a/packages/react-slider/src/components/Slider/Slider.stories.tsx +++ b/packages/react-slider/src/components/Slider/Slider.stories.tsx @@ -13,7 +13,7 @@ export default { component: Slider, decorators: [ Story => ( -
+
), From 40d8a970947a560e640f5ea9405b5dcb4e79885b Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Thu, 18 Nov 2021 11:05:25 -0800 Subject: [PATCH 05/53] update spec --- .../src/components/Slider/Spec.md | 68 ++++--------------- 1 file changed, 15 insertions(+), 53 deletions(-) diff --git a/packages/react-slider/src/components/Slider/Spec.md b/packages/react-slider/src/components/Slider/Spec.md index 4cbbfd8020950..28fd49db2ba50 100644 --- a/packages/react-slider/src/components/Slider/Spec.md +++ b/packages/react-slider/src/components/Slider/Spec.md @@ -23,10 +23,10 @@ https://open-ui.org/components/slider.research #### Research Summary -**Marks**: Amongst other component libraries marks/ticks/notches are used to help visibly differ the current location of the thumb. Marks are also used to create custom steps through providing an array of values to jump too. +**Marks**: Amongst other component libraries marks/ticks/notches are used to help visibly differ the current location of the thumb. Marks are also used to create custom steps through providing an array of values to jump too. Marks will be excluded from v1 of the Slider to reduce complexity and await guidance for marks from design and partners. **Ranged Slider** -Since the `RangedSlider` and `Slider` have very different use cases and accessibility concerns they are planned to be separated into different components. +Since the `RangedSlider` and `Slider` have very different use cases and accessibility concerns they are planned to be separated into different components. Slider component will be the focus of v1 with the multi-thumb slider being a focus after launch. ## Sample Code @@ -37,22 +37,6 @@ Since the `RangedSlider` and `Slider` have very different use cases and accessib // Slider can be controlled -// Marks can be a Boolean (default marks) - - -// Marks can be a number array (specific marks) - - -// Marks can be an array of mark definitions - - -// Marks can be used as the step values using `step` - ``` ## Variants @@ -85,12 +69,11 @@ https://hackmd.io/VUpPADJ7Ry-ZXTrtffD7Sg ### Visual behavior props -| Name | drawing | drawing | Description | -| -------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | -| disabled | ✓ | ✓ | Whether to render the **Slider** as disabled. @defaultvalue `false` (render enabled) | -| vertical | ✓ | ✓ | Whether to render the **Slider** vertically. @default `false` (render horizontally) | -| marks | x | x | Whether the **Slider** will have marks to visibly display its steps. @default `false` (renders without marks) | -| size | x | x | The size of the Slider. | +| Name | drawing | drawing | Description | +| -------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | +| disabled | ✓ | ✓ | Whether to render the **Slider** as disabled. @defaultvalue `false` (render enabled) | +| vertical | ✓ | ✓ | Whether to render the **Slider** vertically. @default `false` (render horizontally) | +| size | x | x | The size of the Slider. | ### Event handlers props @@ -136,18 +119,10 @@ https://hackmd.io/VUpPADJ7Ry-ZXTrtffD7Sg ```jsx - {state.marks && } - - - - - - - - - - - + + + + ``` @@ -155,23 +130,10 @@ https://hackmd.io/VUpPADJ7Ry-ZXTrtffD7Sg ```jsx
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
``` From 6dc8efc16a08c46aeac4ecad7fab1df66533e51f Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Thu, 18 Nov 2021 11:07:23 -0800 Subject: [PATCH 06/53] Change files --- ...ct-components-2b36a74e-2147-4df0-a4f2-ab6c12c56033.json | 7 +++++++ ...-react-slider-03252d64-5321-44f5-a760-9bbdadf077a3.json | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 change/@fluentui-react-components-2b36a74e-2147-4df0-a4f2-ab6c12c56033.json create mode 100644 change/@fluentui-react-slider-03252d64-5321-44f5-a760-9bbdadf077a3.json diff --git a/change/@fluentui-react-components-2b36a74e-2147-4df0-a4f2-ab6c12c56033.json b/change/@fluentui-react-components-2b36a74e-2147-4df0-a4f2-ab6c12c56033.json new file mode 100644 index 0000000000000..a71f71a9421b2 --- /dev/null +++ b/change/@fluentui-react-components-2b36a74e-2147-4df0-a4f2-ab6c12c56033.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Add slider package", + "packageName": "@fluentui/react-components", + "email": "mgodbolt@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-slider-03252d64-5321-44f5-a760-9bbdadf077a3.json b/change/@fluentui-react-slider-03252d64-5321-44f5-a760-9bbdadf077a3.json new file mode 100644 index 0000000000000..fa7503f67bdcc --- /dev/null +++ b/change/@fluentui-react-slider-03252d64-5321-44f5-a760-9bbdadf077a3.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Refactor slider component", + "packageName": "@fluentui/react-slider", + "email": "mgodbolt@microsoft.com", + "dependentChangeType": "patch" +} From d3123522244c78abd09ce931a2018d83f57ef4df Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Thu, 18 Nov 2021 12:22:46 -0800 Subject: [PATCH 07/53] update api --- packages/react-slider/etc/react-slider.api.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/react-slider/etc/react-slider.api.md b/packages/react-slider/etc/react-slider.api.md index 64fcd72eb93eb..8405247b85645 100644 --- a/packages/react-slider/etc/react-slider.api.md +++ b/packages/react-slider/etc/react-slider.api.md @@ -10,12 +10,12 @@ import type { ForwardRefComponent } from '@fluentui/react-utilities'; import { IntrinsicShorthandProps } from '@fluentui/react-utilities'; import * as React_2 from 'react'; +// @public (undocumented) +export const lowerThumbClassName: string; + // @public export const RangedSlider: ForwardRefComponent; -// @public (undocumented) -export const rangedSliderClassName = "fui-RangedSlider"; - // @public (undocumented) export interface RangedSliderCommons extends Omit { defaultValue?: [number, number]; @@ -52,9 +52,6 @@ export const renderSlider: (state: SliderState) => JSX.Element; // @public export const Slider: ForwardRefComponent; -// @public (undocumented) -export const sliderClassName = "fui-Slider"; - // @public (undocumented) export type SliderCommons = { defaultValue?: number; @@ -101,6 +98,9 @@ export type SliderSlots = { // @public (undocumented) export type SliderState = ComponentState & SliderCommons; +// @public (undocumented) +export const upperThumbClassName: string; + // @public export const useRangedSlider: (props: RangedSliderProps, ref: React_2.Ref) => RangedSliderState; From d4236a70c629059343430b1ea7dc851c50a332cb Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Fri, 19 Nov 2021 09:53:28 -0800 Subject: [PATCH 08/53] Apply suggestions from code review Co-authored-by: Oleksandr Fediashov Co-authored-by: ling1726 --- ...act-components-2b36a74e-2147-4df0-a4f2-ab6c12c56033.json | 2 +- ...i-react-slider-03252d64-5321-44f5-a760-9bbdadf077a3.json | 2 +- packages/react-slider/src/components/Slider/Spec.md | 4 ++-- .../components/Slider/stories/SliderControlled.stories.tsx | 6 +++--- .../src/components/Slider/stories/SliderDefault.stories.tsx | 4 ++-- .../components/Slider/stories/SliderDisabled.stories.tsx | 6 ++++-- .../src/components/Slider/stories/SliderOrigin.stories.tsx | 6 ++++-- .../src/components/Slider/stories/SliderStep.stories.tsx | 6 ++++-- 8 files changed, 21 insertions(+), 15 deletions(-) diff --git a/change/@fluentui-react-components-2b36a74e-2147-4df0-a4f2-ab6c12c56033.json b/change/@fluentui-react-components-2b36a74e-2147-4df0-a4f2-ab6c12c56033.json index a71f71a9421b2..7f05f8e9393e4 100644 --- a/change/@fluentui-react-components-2b36a74e-2147-4df0-a4f2-ab6c12c56033.json +++ b/change/@fluentui-react-components-2b36a74e-2147-4df0-a4f2-ab6c12c56033.json @@ -1,6 +1,6 @@ { "type": "prerelease", - "comment": "Add slider package", + "comment": "Export slider package", "packageName": "@fluentui/react-components", "email": "mgodbolt@microsoft.com", "dependentChangeType": "patch" diff --git a/change/@fluentui-react-slider-03252d64-5321-44f5-a760-9bbdadf077a3.json b/change/@fluentui-react-slider-03252d64-5321-44f5-a760-9bbdadf077a3.json index fa7503f67bdcc..ab7754a06449e 100644 --- a/change/@fluentui-react-slider-03252d64-5321-44f5-a760-9bbdadf077a3.json +++ b/change/@fluentui-react-slider-03252d64-5321-44f5-a760-9bbdadf077a3.json @@ -1,6 +1,6 @@ { "type": "prerelease", - "comment": "Refactor slider component", + "comment": "Refactor slider component: removed marks support, RangedSlider is removed", "packageName": "@fluentui/react-slider", "email": "mgodbolt@microsoft.com", "dependentChangeType": "patch" diff --git a/packages/react-slider/src/components/Slider/Spec.md b/packages/react-slider/src/components/Slider/Spec.md index 28fd49db2ba50..1847d16de0f65 100644 --- a/packages/react-slider/src/components/Slider/Spec.md +++ b/packages/react-slider/src/components/Slider/Spec.md @@ -23,10 +23,10 @@ https://open-ui.org/components/slider.research #### Research Summary -**Marks**: Amongst other component libraries marks/ticks/notches are used to help visibly differ the current location of the thumb. Marks are also used to create custom steps through providing an array of values to jump too. Marks will be excluded from v1 of the Slider to reduce complexity and await guidance for marks from design and partners. +**Marks**: Amongst other component libraries marks/ticks/notches are used to help visibly differ the current location of the thumb. Marks are also used to create custom steps through providing an array of values to jump too. Marks will be excluded from initial release of the Slider to reduce complexity and await guidance for marks from design and partners. **Ranged Slider** -Since the `RangedSlider` and `Slider` have very different use cases and accessibility concerns they are planned to be separated into different components. Slider component will be the focus of v1 with the multi-thumb slider being a focus after launch. +Since the `RangedSlider` and `Slider` have very different use cases and accessibility concerns they are planned to be separated into different components. Slider component will be the focus of initial release with the multi-thumb slider being a focus after launch. ## Sample Code diff --git a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx index 2793cbc5947a9..d617e59ebde4c 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx @@ -1,17 +1,17 @@ import * as React from 'react'; import { Label } from '@fluentui/react-label'; -import { Slider } from '../../../index'; // codesandbox-dependency: @fluentui/react-slider ^9.0.0-beta +import { Slider } from '../../../index'; export const Controlled = () => { const [sliderValue, setSliderValue] = React.useState(160); - const onSliderChange = ( + const onSliderChange: SliderProps['onChange'] = ( ev: React.PointerEvent | React.KeyboardEvent, data: { value: number }, ) => setSliderValue(data.value); return ( <> - + ( <> - + ); diff --git a/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx index 3b8d58e512be8..08782344bd0aa 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx @@ -1,10 +1,12 @@ import * as React from 'react'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// ts-ignore import { Label } from '@fluentui/react-label'; -import { Slider } from '../../../index'; // codesandbox-dependency: @fluentui/react-slider ^9.0.0-beta +import { Slider } from '../../../index'; export const Disabled = () => ( <> - + ); diff --git a/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx index 4431c6e2e7c6f..c9d0c9c4137db 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx @@ -1,10 +1,12 @@ import * as React from 'react'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// ts-ignore import { Label } from '@fluentui/react-label'; -import { Slider } from '../../../index'; // codesandbox-dependency: @fluentui/react-slider ^9.0.0-beta +import { Slider } from '../../../index'; export const Origin = () => ( <> - + ); diff --git a/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx index 4714185693a8b..9e8f3eb261f51 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx @@ -1,10 +1,12 @@ import * as React from 'react'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// ts-ignore import { Label } from '@fluentui/react-label'; -import { Slider } from '../../../index'; // codesandbox-dependency: @fluentui/react-slider ^9.0.0-beta +import { Slider } from '../../../index'; export const Step = () => ( <> - + ); From 07e60a1e5757fa7bdf2ec8a718c8c393864464c6 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Fri, 19 Nov 2021 10:52:57 -0800 Subject: [PATCH 09/53] move stateful styles into css variables --- packages/react-slider/etc/react-slider.api.md | 61 +--------------- .../src/components/Slider/Slider.stories.tsx | 1 + .../stories/SliderControlled.stories.tsx | 6 +- .../Slider/stories/SliderVertical.stories.tsx | 12 ++++ .../src/components/Slider/useSliderState.tsx | 70 +++++++++---------- .../src/components/Slider/useSliderStyles.ts | 20 ++++-- 6 files changed, 66 insertions(+), 104 deletions(-) create mode 100644 packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx diff --git a/packages/react-slider/etc/react-slider.api.md b/packages/react-slider/etc/react-slider.api.md index 8405247b85645..f81ff6f6de627 100644 --- a/packages/react-slider/etc/react-slider.api.md +++ b/packages/react-slider/etc/react-slider.api.md @@ -10,48 +10,15 @@ import type { ForwardRefComponent } from '@fluentui/react-utilities'; import { IntrinsicShorthandProps } from '@fluentui/react-utilities'; import * as React_2 from 'react'; -// @public (undocumented) -export const lowerThumbClassName: string; - -// @public -export const RangedSlider: ForwardRefComponent; - -// @public (undocumented) -export interface RangedSliderCommons extends Omit { - defaultValue?: [number, number]; - onChange?: (ev: React_2.PointerEvent | React_2.KeyboardEvent, data: { - value: [number, number]; - }) => void; - value?: [number, number]; -} - -// @public (undocumented) -export interface RangedSliderProps extends Omit, 'onChange' | 'defaultValue'>, RangedSliderCommons { -} - -// @public (undocumented) -export type RangedSliderSlots = Omit & { - lowerThumb: IntrinsicShorthandProps<'div'>; - lowerThumbWrapper: IntrinsicShorthandProps<'div'>; - upperThumb: IntrinsicShorthandProps<'div'>; - upperThumbWrapper: IntrinsicShorthandProps<'div'>; - lowerInput: IntrinsicShorthandProps<'input'>; - upperInput: IntrinsicShorthandProps<'input'>; -}; - -// @public (undocumented) -export interface RangedSliderState extends ComponentState, RangedSliderCommons { -} - -// @public -export const renderRangedSlider: (state: RangedSliderState) => JSX.Element; - // @public export const renderSlider: (state: SliderState) => JSX.Element; // @public export const Slider: ForwardRefComponent; +// @public (undocumented) +export const sliderClassName = "fui-Slider"; + // @public (undocumented) export type SliderCommons = { defaultValue?: number; @@ -62,11 +29,6 @@ export type SliderCommons = { keyboardStep?: number; disabled?: boolean; vertical?: boolean; - marks?: boolean | (number | { - value: number; - label?: string | JSX.Element; - mark?: JSX.Element; - })[]; origin?: number; size?: 'small' | 'medium'; onChange?: (ev: React_2.PointerEvent | React_2.KeyboardEvent, data: { @@ -85,31 +47,14 @@ export const sliderShorthandProps: (keyof SliderSlots)[]; export type SliderSlots = { root: IntrinsicShorthandProps<'div'>; rail: IntrinsicShorthandProps<'div'>; - sliderWrapper: IntrinsicShorthandProps<'div'>; - trackWrapper: IntrinsicShorthandProps<'div'>; track: IntrinsicShorthandProps<'div'>; - marksWrapper: IntrinsicShorthandProps<'div'>; - thumbWrapper: IntrinsicShorthandProps<'div'>; thumb: IntrinsicShorthandProps<'div'>; - activeRail: IntrinsicShorthandProps<'div'>; input: IntrinsicShorthandProps<'input'>; }; // @public (undocumented) export type SliderState = ComponentState & SliderCommons; -// @public (undocumented) -export const upperThumbClassName: string; - -// @public -export const useRangedSlider: (props: RangedSliderProps, ref: React_2.Ref) => RangedSliderState; - -// @public (undocumented) -export const useRangedSliderState: (state: RangedSliderState) => RangedSliderState; - -// @public (undocumented) -export const useRangedSliderStyles: (state: RangedSliderState) => RangedSliderState; - // @public export const useSlider: (props: SliderProps, ref: React_2.Ref) => SliderState; diff --git a/packages/react-slider/src/components/Slider/Slider.stories.tsx b/packages/react-slider/src/components/Slider/Slider.stories.tsx index 4202323c37006..7852ed6c82b2c 100644 --- a/packages/react-slider/src/components/Slider/Slider.stories.tsx +++ b/packages/react-slider/src/components/Slider/Slider.stories.tsx @@ -6,6 +6,7 @@ export * from './stories/SliderDefault.stories'; export * from './stories/SliderControlled.stories'; export * from './stories/SliderStep.stories'; export * from './stories/SliderOrigin.stories'; +export * from './stories/SliderVertical.stories'; export * from './stories/SliderDisabled.stories'; export default { diff --git a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx index d617e59ebde4c..5783df44cf18c 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx @@ -1,6 +1,8 @@ import * as React from 'react'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore import { Label } from '@fluentui/react-label'; -import { Slider } from '../../../index'; +import { Slider, SliderProps } from '../../../index'; export const Controlled = () => { const [sliderValue, setSliderValue] = React.useState(160); @@ -11,7 +13,7 @@ export const Controlled = () => { return ( <> - + ( + <> + + + +); diff --git a/packages/react-slider/src/components/Slider/useSliderState.tsx b/packages/react-slider/src/components/Slider/useSliderState.tsx index e976934653aab..b836264e08384 100644 --- a/packages/react-slider/src/components/Slider/useSliderState.tsx +++ b/packages/react-slider/src/components/Slider/useSliderState.tsx @@ -44,6 +44,11 @@ export const useSliderState = (state: SliderState) => { const inputRef = React.useRef(null); const disposables = React.useRef<(() => void)[]>([]); + const valuePercent = getPercent(renderedPosition !== undefined ? renderedPosition : currentValue, min, max); + const originPercent = React.useMemo(() => { + return origin !== undefined ? getPercent(origin, min, max) : 0; + }, [max, min, origin]); + /** * Updates the controlled `currentValue` to the new `incomingValue` and clamps it. */ @@ -133,46 +138,23 @@ export const useSliderState = (state: SliderState) => { [currentValue, dir, hideStepAnimation, keyboardStep, max, min, onKeyDownCallback, updatePosition], ); - const getTrackBorderRadius = () => { - if (origin !== undefined && origin !== (max || min)) { - if (vertical) { - return originPercent > valuePercent ? '99px 99px 0px 0px' : '0px 0px 99px 99px'; - } else { - return (dir === 'rtl' ? valuePercent > originPercent : valuePercent < originPercent) - ? '99px 0px 0px 99px' - : '0px 99px 99px 0px'; - } - } - return '99px'; - }; - useUnmount(() => { disposables.current.forEach(dispose => dispose()); disposables.current = []; }); - const valuePercent = getPercent(renderedPosition !== undefined ? renderedPosition : currentValue, min, max); - - const originPercent = React.useMemo(() => { - return origin !== undefined ? getPercent(origin, min, max) : 0; - }, [max, min, origin]); - - const thumbStyles = { - [vertical ? 'top' : 'left']: valuePercent + '%', - transition: stepAnimation ? `left ease-in-out ${animationTime}` : 'none', - ...state.thumb.style, + const thumbVariables = { + '--slider-thumb-position': valuePercent + '%', + '--slider-thumb-transition': stepAnimation ? `left ease-in-out ${animationTime}` : 'none', }; - const trackStyles = { - [vertical ? 'top' : dir === 'rtl' ? 'right' : 'left']: - origin !== undefined ? `${Math.min(valuePercent, originPercent)}%` : 0, - [vertical ? 'height' : 'width']: + const trackVariables = { + '--slider-track-offset': origin !== undefined ? `${Math.min(valuePercent, originPercent)}%` : 0, + '--slider-track-progress': origin !== undefined ? `${Math.max(originPercent - valuePercent, valuePercent - originPercent)}%` : `${valuePercent}%`, - borderRadius: getTrackBorderRadius(), - // When a transition is applied with the origin, a visible animation plays when it goes below the min. - transition: stepAnimation + '--slider-track-transition': stepAnimation ? `${vertical ? 'height' : 'width'} ease-in-out ${animationTime}${ origin !== undefined ? ', ' + vertical @@ -183,22 +165,34 @@ export const useSliderState = (state: SliderState) => { : '' }` : 'none', - ...state.track.style, + '--slider-track-border-radius': + // if has !origin, border on one end + origin !== undefined && origin !== (max || min) + ? // top or bottom if vertical + vertical + ? originPercent > valuePercent + ? '99px 99px 0px 0px' + : '0px 0px 99px 99px' + : // left or right depending on rtl and values + (dir === 'rtl' ? valuePercent > originPercent : valuePercent < originPercent) + ? '99px 0px 0px 99px' + : '0px 99px 99px 0px' + : '99px', }; // Root props + state.root.style = { + ...thumbVariables, + ...trackVariables, + ...state.root.style, + }; + if (!disabled) { state.root.onPointerDown = onPointerDown; state.root.onKeyDown = onKeyDown; } - // Track Props - state.track.style = trackStyles; - - // Thumb Props - state.thumb.style = thumbStyles; - - // Active Rail Props + // Rail Props state.rail.ref = railRef; // Input Props diff --git a/packages/react-slider/src/components/Slider/useSliderStyles.ts b/packages/react-slider/src/components/Slider/useSliderStyles.ts index c2d139611ec83..e533d02f133a0 100644 --- a/packages/react-slider/src/components/Slider/useSliderStyles.ts +++ b/packages/react-slider/src/components/Slider/useSliderStyles.ts @@ -107,17 +107,22 @@ export const useRailStyles = makeStyles({ */ export const useTrackStyles = makeStyles({ track: theme => ({ - borderRadius: theme.borderRadiusXLarge, + borderRadius: `var(--slider-track-border-radius, ${theme.borderRadiusXLarge})`, gridArea: 'slider', position: 'relative', + transition: 'var(--slider-track-transition)', }), horizontal: { height: 'var(--slider-rail-size)', + left: 'var(--slider-track-offset)', + width: 'var(--slider-track-progress)', }, vertical: { width: 'var(--slider-rail-size)', + top: 'var(--slider-track-offset)', + height: 'var(--slider-track-progress)', }, enabled: theme => ({ @@ -141,8 +146,8 @@ export const useThumbStyles = makeStyles({ borderRadius: theme.borderRadiusCircular, boxShadow: `0 0 0 calc(var(--slider-thumb-size) * .2) ${theme.colorNeutralBackground1} inset`, transform: 'translateX(-50%)', - left: 0, + transition: 'var(--slider-thumb-transition)', ':before': { position: 'absolute', top: '0px', @@ -166,10 +171,13 @@ export const useThumbStyles = makeStyles({ border: `calc(var(--slider-thumb-size) * .05) solid ${theme.colorNeutralForegroundDisabled}`, }, }), - - vertical: theme => ({ + horizontal: { + left: 'var(--slider-thumb-position)', + }, + vertical: { transform: 'translateY(-50%)', - }), + top: 'var(--slider-thumb-position)', + }, }); /** @@ -227,7 +235,7 @@ export const useSliderStyles = (state: SliderState): SliderState => { state.thumb.className = mergeClasses( thumbClassName, thumbStyles.thumb, - state.vertical && thumbStyles.vertical, + state.vertical ? thumbStyles.vertical : thumbStyles.horizontal, state.disabled ? thumbStyles.disabled : thumbStyles.enabled, state.thumb.className, ); From 47e70797badc132e69b083161aaf2f7a4ab2f9a5 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Mon, 29 Nov 2021 10:11:35 -0800 Subject: [PATCH 10/53] add slider to react-components main export --- packages/react-components/src/index.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/react-components/src/index.ts b/packages/react-components/src/index.ts index baa5144aa5ce8..d93d9331102b2 100644 --- a/packages/react-components/src/index.ts +++ b/packages/react-components/src/index.ts @@ -363,6 +363,15 @@ export type { } from '@fluentui/react-popover'; export { Portal, elementContains, renderPortal, setVirtualParent, usePortal } from '@fluentui/react-portal'; export type { PortalCommons, PortalProps, PortalState } from '@fluentui/react-portal'; +export { + Slider, + renderSlider, + sliderClassName, + useSlider, + useSliderStyles, + useSliderState, +} from '@fluentui/react-slider'; +export type { SliderCommons, SliderProps, SliderSlots, SliderState } from '@fluentui/react-slider'; export { Body, Caption, From ae6f7edb7ca330f7c54fe16f3cbd49ce6b5e48ae Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Mon, 29 Nov 2021 17:01:26 -0800 Subject: [PATCH 11/53] cleanup and add ticks --- .../stories/SliderControlled.stories.tsx | 6 +-- .../Slider/stories/SliderStep.stories.tsx | 4 +- .../Slider/stories/SliderVertical.stories.tsx | 2 +- .../src/components/Slider/useSlider.ts | 2 +- .../src/components/Slider/useSliderState.tsx | 4 ++ .../src/components/Slider/useSliderStyles.ts | 37 ++++++++++++++++++- 6 files changed, 46 insertions(+), 9 deletions(-) diff --git a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx index 5783df44cf18c..1ea92aab9f6b2 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx @@ -16,10 +16,10 @@ export const Controlled = () => { diff --git a/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx index 9e8f3eb261f51..bb8306ac2a6de 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx @@ -6,7 +6,7 @@ import { Slider } from '../../../index'; export const Step = () => ( <> - - + + ); diff --git a/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx index 494da20d6b624..3f390e69d6be2 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx @@ -7,6 +7,6 @@ import { Slider } from '../../../index'; export const Vertical = () => ( <> - + ); diff --git a/packages/react-slider/src/components/Slider/useSlider.ts b/packages/react-slider/src/components/Slider/useSlider.ts index 28bfebe702a69..93b9c3f23727d 100644 --- a/packages/react-slider/src/components/Slider/useSlider.ts +++ b/packages/react-slider/src/components/Slider/useSlider.ts @@ -18,7 +18,7 @@ export const useSlider = (props: SliderProps, ref: React.Ref): Slid defaultValue, min, max, - step = 1, + step, keyboardStep, disabled, ariaValueText, diff --git a/packages/react-slider/src/components/Slider/useSliderState.tsx b/packages/react-slider/src/components/Slider/useSliderState.tsx index b836264e08384..4071da4bf4686 100644 --- a/packages/react-slider/src/components/Slider/useSliderState.tsx +++ b/packages/react-slider/src/components/Slider/useSliderState.tsx @@ -148,6 +148,9 @@ export const useSliderState = (state: SliderState) => { '--slider-thumb-transition': stepAnimation ? `left ease-in-out ${animationTime}` : 'none', }; + const rootVariables = { + '--slider-root-steps-percent': state.step ? `${(step * 100) / (max - min)}%` : '', + }; const trackVariables = { '--slider-track-offset': origin !== undefined ? `${Math.min(valuePercent, originPercent)}%` : 0, '--slider-track-progress': @@ -182,6 +185,7 @@ export const useSliderState = (state: SliderState) => { // Root props state.root.style = { + ...rootVariables, ...thumbVariables, ...trackVariables, ...state.root.style, diff --git a/packages/react-slider/src/components/Slider/useSliderStyles.ts b/packages/react-slider/src/components/Slider/useSliderStyles.ts index e533d02f133a0..ac8618be1d01e 100644 --- a/packages/react-slider/src/components/Slider/useSliderStyles.ts +++ b/packages/react-slider/src/components/Slider/useSliderStyles.ts @@ -7,18 +7,20 @@ export const sliderClassName = 'fui-Slider'; const thumbClassName = `${sliderClassName}-thumb`; const trackClassName = `${sliderClassName}-track`; const railClassName = `${sliderClassName}-rail`; +const stepIndex = 10; +const thumbIndex = 20; /** * Styles for the root slot */ export const useRootStyles = makeStyles({ - root: { + root: theme => ({ position: 'relative', display: 'inline-grid', gridTemplateAreas: '"slider"', userSelect: 'none', touchAction: 'none', - }, + }), small: { '--slider-thumb-size': '10px', @@ -81,6 +83,12 @@ export const useRailStyles = makeStyles({ borderRadius: theme.borderRadiusXLarge, pointerEvents: 'none', gridArea: 'slider', + position: 'relative', + ':before': { + content: "''", + position: 'absolute', + zIndex: stepIndex, + }, }), enabled: theme => ({ @@ -94,11 +102,35 @@ export const useRailStyles = makeStyles({ horizontal: { height: 'var(--slider-rail-size)', + ':before': { + left: '-1px', + right: '-1px', + height: 'var(--slider-rail-size)', + background: `repeating-linear-gradient( + 90deg, + #0000 0%, + #0000 calc(var(--slider-root-steps-percent) - 1px), + #fff calc(var(--slider-root-steps-percent) - 1px), + #fff var(--slider-root-steps-percent) + )`, + }, }, vertical: { width: 'var(--slider-rail-size)', height: '100%', + ':before': { + width: 'var(--slider-rail-size)', + top: '1px', + bottom: '-1px', + background: `repeating-linear-gradient( + 180deg, + #0000 0%, + #0000 calc(var(--slider-root-steps-percent) - 1px), + #fff calc(var(--slider-root-steps-percent) - 1px), + #fff var(--slider-root-steps-percent) + )`, + }, }, }); @@ -140,6 +172,7 @@ export const useTrackStyles = makeStyles({ export const useThumbStyles = makeStyles({ thumb: theme => ({ position: 'absolute', + zIndex: thumbIndex, width: 'var(--slider-thumb-size)', height: 'var(--slider-thumb-size)', outline: 'none', From ab2b781cb5d96549e4ac5ed4bee3d6bc655ba8d0 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Mon, 29 Nov 2021 17:02:42 -0800 Subject: [PATCH 12/53] change var name --- .../src/components/Slider/useSliderState.tsx | 6 +++--- .../src/components/Slider/useSliderStyles.ts | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/react-slider/src/components/Slider/useSliderState.tsx b/packages/react-slider/src/components/Slider/useSliderState.tsx index 4071da4bf4686..34bec29d556f2 100644 --- a/packages/react-slider/src/components/Slider/useSliderState.tsx +++ b/packages/react-slider/src/components/Slider/useSliderState.tsx @@ -148,8 +148,8 @@ export const useSliderState = (state: SliderState) => { '--slider-thumb-transition': stepAnimation ? `left ease-in-out ${animationTime}` : 'none', }; - const rootVariables = { - '--slider-root-steps-percent': state.step ? `${(step * 100) / (max - min)}%` : '', + const railVariables = { + '--slider-rail-steps-percent': state.step ? `${(step * 100) / (max - min)}%` : '', }; const trackVariables = { '--slider-track-offset': origin !== undefined ? `${Math.min(valuePercent, originPercent)}%` : 0, @@ -185,7 +185,7 @@ export const useSliderState = (state: SliderState) => { // Root props state.root.style = { - ...rootVariables, + ...railVariables, ...thumbVariables, ...trackVariables, ...state.root.style, diff --git a/packages/react-slider/src/components/Slider/useSliderStyles.ts b/packages/react-slider/src/components/Slider/useSliderStyles.ts index ac8618be1d01e..a856884186d83 100644 --- a/packages/react-slider/src/components/Slider/useSliderStyles.ts +++ b/packages/react-slider/src/components/Slider/useSliderStyles.ts @@ -109,9 +109,9 @@ export const useRailStyles = makeStyles({ background: `repeating-linear-gradient( 90deg, #0000 0%, - #0000 calc(var(--slider-root-steps-percent) - 1px), - #fff calc(var(--slider-root-steps-percent) - 1px), - #fff var(--slider-root-steps-percent) + #0000 calc(var(--slider-rail-steps-percent) - 1px), + #fff calc(var(--slider-rail-steps-percent) - 1px), + #fff var(--slider-rail-steps-percent) )`, }, }, @@ -126,9 +126,9 @@ export const useRailStyles = makeStyles({ background: `repeating-linear-gradient( 180deg, #0000 0%, - #0000 calc(var(--slider-root-steps-percent) - 1px), - #fff calc(var(--slider-root-steps-percent) - 1px), - #fff var(--slider-root-steps-percent) + #0000 calc(var(--slider-rail-steps-percent) - 1px), + #fff calc(var(--slider-rail-steps-percent) - 1px), + #fff var(--slider-rail-steps-percent) )`, }, }, From 663fe05027ed349e44f1d9296726be476b723e8a Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 30 Nov 2021 09:13:08 -0800 Subject: [PATCH 13/53] cleanup snaps --- .../Slider/__snapshots__/Slider.test.tsx.snap | 1109 +---------------- 1 file changed, 68 insertions(+), 1041 deletions(-) diff --git a/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap b/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap index dc317176e479e..f25a61b197765 100644 --- a/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap +++ b/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap @@ -1,477 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Slider Snapshot Tests renders Slider with custom mark labels correctly 1`] = ` -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- world -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-`; - -exports[`Slider Snapshot Tests renders Slider with custom marks correctly 1`] = ` -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- world -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-`; - -exports[`Slider Snapshot Tests renders Slider with marks correctly 1`] = ` -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-`; - exports[`Slider Snapshot Tests renders disabled Slider correctly 1`] = `
-
-
-
-
-
-
-
-
- -
-
-
-`; - -exports[`Slider Snapshot Tests renders disabled Slider with mark labels correctly 1`] = ` -
-
+ class="fui-Slider-rail" + />
-
-
-
-
-
-
- hello -
-
-
-
-
- world -
-
-
-
-
-
+ class="fui-Slider-track" + />
+ -
-
-
-
-
-
-
-
- -
+ disabled="" + max="10" + min="0" + step="1" + type="range" + value="5" + />
`; @@ -612,247 +35,49 @@ exports[`Slider Snapshot Tests renders horizontal Slider correctly 1`] = ` id="slider-1" >
-
-
-
-
-
-
-
-
- -
-
-
-`; - -exports[`Slider Snapshot Tests renders horizontal Slider with mark labels correctly 1`] = ` -
-
+ class="fui-Slider-rail" + />
-
-
-
-
-
-
- hello -
-
-
-
-
- world -
-
-
-
-
-
+ class="fui-Slider-track" + />
+ -
-
-
-
-
-
-
-
- -
+ max="10" + min="0" + step="1" + type="range" + value="5" + />
`; -exports[`Slider Snapshot Tests renders horizontal Slider with unique mark values correctly 1`] = ` +exports[`Slider Snapshot Tests renders horizontal origin Slider correctly 1`] = `
-
-
-
-
-
-
-
-
-
-
-
-
-
+ class="fui-Slider-rail" + />
-
-
-
-
-
-
-
-
- -
-
-
-`; - -exports[`Slider Snapshot Tests renders horizontal origin Slider correctly 1`] = ` -
-
+ class="fui-Slider-track" + />
+ -
-
-
-
-
-
-
-
- -
+ max="10" + min="0" + step="1" + type="range" + value="5" + />
`; @@ -864,247 +89,49 @@ exports[`Slider Snapshot Tests renders vertical Slider correctly 1`] = ` id="slider-1" >
-
-
-
-
-
-
-
-
- -
-
-
-`; - -exports[`Slider Snapshot Tests renders vertical Slider with mark labels correctly 1`] = ` -
-
+ class="fui-Slider-rail" + />
-
-
-
-
-
-
- hello -
-
-
-
-
- world -
-
-
-
-
-
+ class="fui-Slider-track" + />
+ -
-
-
-
-
-
-
-
- -
+ max="10" + min="0" + step="1" + type="range" + value="5" + />
`; -exports[`Slider Snapshot Tests renders vertical Slider with unique mark values correctly 1`] = ` +exports[`Slider Snapshot Tests renders vertical origin Slider correctly 1`] = `
-
-
-
-
-
-
-
-
-
-
-
-
-
+ class="fui-Slider-rail" + />
-
-
-
-
-
-
-
-
- -
-
-
-`; - -exports[`Slider Snapshot Tests renders vertical origin Slider correctly 1`] = ` -
-
+ class="fui-Slider-track" + />
+ -
-
-
-
-
-
-
-
- -
+ max="10" + min="0" + step="1" + type="range" + value="5" + />
`; From 66281f20dd8548dfd20c770fe934872a47e3b5b8 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 30 Nov 2021 09:37:25 -0800 Subject: [PATCH 14/53] remove ranged fixture --- packages/react-slider/bundle-size/RangedSlider.fixture.js | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 packages/react-slider/bundle-size/RangedSlider.fixture.js diff --git a/packages/react-slider/bundle-size/RangedSlider.fixture.js b/packages/react-slider/bundle-size/RangedSlider.fixture.js deleted file mode 100644 index 720b28a2d24c8..0000000000000 --- a/packages/react-slider/bundle-size/RangedSlider.fixture.js +++ /dev/null @@ -1,7 +0,0 @@ -import { RangedSlider } from '@fluentui/react-slider'; - -console.log(RangedSlider); - -export default { - name: 'RangedSlider', -}; From 964b130f602f23f05aa91dca273e684565f1e8fd Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Wed, 1 Dec 2021 13:29:43 -0800 Subject: [PATCH 15/53] fix package version --- packages/react-components/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-components/package.json b/packages/react-components/package.json index 3ff3bd986b5a9..986651e139c6a 100644 --- a/packages/react-components/package.json +++ b/packages/react-components/package.json @@ -53,7 +53,7 @@ "@fluentui/react-popover": "9.0.0-beta.5", "@fluentui/react-portal": "9.0.0-beta.5", "@fluentui/react-provider": "9.0.0-beta.5", - "@fluentui/react-slider": "9.0.0-beta.4", + "@fluentui/react-slider": "9.0.0-beta.5", "@fluentui/react-theme": "9.0.0-beta.4", "@fluentui/react-tooltip": "9.0.0-beta.5", "@fluentui/react-utilities": "9.0.0-beta.4", From 4d65009ce0ede8e87c8f4b97fecbfc237334550f Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Wed, 1 Dec 2021 15:24:44 -0800 Subject: [PATCH 16/53] fix prettier --- .../src/components/Slider/stories/SliderDefault.stories.tsx | 2 +- .../src/components/Slider/stories/SliderDisabled.stories.tsx | 2 +- .../src/components/Slider/stories/SliderOrigin.stories.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx index 67e6cdc1a3735..495d74be69e03 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx @@ -6,7 +6,7 @@ import { Slider } from '../../../index'; export const Default = () => ( <> - + ); diff --git a/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx index 08782344bd0aa..e71becdce997c 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx @@ -6,7 +6,7 @@ import { Slider } from '../../../index'; export const Disabled = () => ( <> - + ); diff --git a/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx index c9d0c9c4137db..3ab5c1e3f934d 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx @@ -6,7 +6,7 @@ import { Slider } from '../../../index'; export const Origin = () => ( <> - + ); From 5f2b8de244edd542ab8449a00724983a2d50ff7f Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Wed, 1 Dec 2021 16:16:36 -0800 Subject: [PATCH 17/53] add primary slot --- .../src/components/Slider/Slider.test.tsx | 3 ++- .../src/components/Slider/Slider.types.ts | 6 ++++- .../src/components/Slider/Spec.md | 1 - .../stories/SliderControlled.stories.tsx | 2 +- .../Slider/stories/SliderDefault.stories.tsx | 2 +- .../Slider/stories/SliderDisabled.stories.tsx | 2 +- .../Slider/stories/SliderOrigin.stories.tsx | 2 +- .../Slider/stories/SliderStep.stories.tsx | 2 +- .../Slider/stories/SliderVertical.stories.tsx | 2 +- .../src/components/Slider/useSlider.ts | 23 ++++++++++++++----- 10 files changed, 30 insertions(+), 15 deletions(-) diff --git a/packages/react-slider/src/components/Slider/Slider.test.tsx b/packages/react-slider/src/components/Slider/Slider.test.tsx index 58b28f0b62989..954e041456f4a 100644 --- a/packages/react-slider/src/components/Slider/Slider.test.tsx +++ b/packages/react-slider/src/components/Slider/Slider.test.tsx @@ -13,6 +13,7 @@ describe('Slider', () => { isConformant({ Component: Slider, displayName: 'Slider', + primarySlot: 'input', disabledTests: ['kebab-aria-attributes'], }); @@ -354,7 +355,7 @@ describe('Slider', () => { }); it('does not allow focus on disabled Slider', () => { - const sliderRef = React.createRef(); + const sliderRef = React.createRef(); const inputRef = React.createRef(); render(); diff --git a/packages/react-slider/src/components/Slider/Slider.types.ts b/packages/react-slider/src/components/Slider/Slider.types.ts index 377d4a6bfaa24..55ce69c3baf5f 100644 --- a/packages/react-slider/src/components/Slider/Slider.types.ts +++ b/packages/react-slider/src/components/Slider/Slider.types.ts @@ -4,6 +4,8 @@ import { ComponentState, ComponentProps, IntrinsicShorthandProps } from '@fluent export type SliderSlots = { /** * The root of the Slider. + * The root slot receives the `className` and `style` specified directly on the ``. + * All other native props will be applied to the primary slot: `input` */ root: IntrinsicShorthandProps<'div'>; @@ -25,6 +27,8 @@ export type SliderSlots = { /** * The hidden input for the Slider. + * This is the PRIMARY slot: all native properties specified directly on `` will be applied to this slot, + * except `className` and `style`, which remain on the root slot. */ input: IntrinsicShorthandProps<'input'>; }; @@ -109,6 +113,6 @@ export type SliderCommons = { ariaValueText?: (value: number) => string; }; -export type SliderProps = Omit, 'onChange' | 'defaultValue'> & SliderCommons; +export type SliderProps = Omit, 'onChange' | 'defaultValue'> & SliderCommons; export type SliderState = ComponentState & SliderCommons; diff --git a/packages/react-slider/src/components/Slider/Spec.md b/packages/react-slider/src/components/Slider/Spec.md index 1847d16de0f65..eb26168c0f78b 100644 --- a/packages/react-slider/src/components/Slider/Spec.md +++ b/packages/react-slider/src/components/Slider/Spec.md @@ -168,7 +168,6 @@ _Explain how the component will behave in use, including:_ - _Screen readers_ - **`root`:** - renders `as` div - - handles native props expected from the element type in `as` - **`hidden input element`:** - Handles aria for the Slider. diff --git a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx index 1ea92aab9f6b2..7041d555c84ae 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx @@ -21,7 +21,7 @@ export const Controlled = () => { step={20} keyboardStep={10} onChange={onSliderChange} - input={{ id: 'controlled-slider' }} + id="controlled-slider" /> ); diff --git a/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx index 495d74be69e03..9194decc73775 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx @@ -7,6 +7,6 @@ import { Slider } from '../../../index'; export const Default = () => ( <> - + ); diff --git a/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx index e71becdce997c..eae43f719efd5 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx @@ -7,6 +7,6 @@ import { Slider } from '../../../index'; export const Disabled = () => ( <> - + ); diff --git a/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx index 3ab5c1e3f934d..786fb6f1e0ab1 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx @@ -7,6 +7,6 @@ import { Slider } from '../../../index'; export const Origin = () => ( <> - + ); diff --git a/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx index bb8306ac2a6de..6d13047eb7d86 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx @@ -7,6 +7,6 @@ import { Slider } from '../../../index'; export const Step = () => ( <> - + ); diff --git a/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx index 3f390e69d6be2..9dbb13207a680 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx @@ -7,6 +7,6 @@ import { Slider } from '../../../index'; export const Vertical = () => ( <> - + ); diff --git a/packages/react-slider/src/components/Slider/useSlider.ts b/packages/react-slider/src/components/Slider/useSlider.ts index 93b9c3f23727d..7217ea724bb91 100644 --- a/packages/react-slider/src/components/Slider/useSlider.ts +++ b/packages/react-slider/src/components/Slider/useSlider.ts @@ -1,5 +1,5 @@ import * as React from 'react'; -import { getNativeElementProps, resolveShorthand, useId } from '@fluentui/react-utilities'; +import { getPartitionedNativeProps, resolveShorthand, useId } from '@fluentui/react-utilities'; import { useSliderState } from './useSliderState'; import { SliderProps, SliderSlots, SliderState } from './Slider.types'; @@ -11,7 +11,13 @@ export const sliderShorthandProps: (keyof SliderSlots)[] = ['root', 'input', 'ra /** * Given user props, returns state and render function for a Slider. */ -export const useSlider = (props: SliderProps, ref: React.Ref): SliderState => { +export const useSlider = (props: SliderProps, ref: React.Ref): SliderState => { + const nativeProps = getPartitionedNativeProps({ + props, + primarySlotTagName: 'input', + excludedPropNames: [], + }); + const { // Props value, @@ -28,6 +34,7 @@ export const useSlider = (props: SliderProps, ref: React.Ref): Slid origin, // Slots + root, input, rail, thumb, @@ -54,14 +61,18 @@ export const useSlider = (props: SliderProps, ref: React.Ref): Slid thumb: 'div', track: 'div', }, - root: getNativeElementProps('span', { - ref, - ...props, - id: useId('slider-', props.id), + root: resolveShorthand(root, { + required: true, + defaultProps: { + ...nativeProps.root, + }, }), input: resolveShorthand(input, { required: true, defaultProps: { + id: useId('slider-', props.id), + ref, + ...nativeProps.primary, type: 'range', }, }), From b93be27e6c8f7120d31b2b4f37a4ec2a612707ff Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Wed, 1 Dec 2021 16:43:06 -0800 Subject: [PATCH 18/53] api update --- packages/react-slider/etc/react-slider.api.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-slider/etc/react-slider.api.md b/packages/react-slider/etc/react-slider.api.md index f81ff6f6de627..483b5913e11ff 100644 --- a/packages/react-slider/etc/react-slider.api.md +++ b/packages/react-slider/etc/react-slider.api.md @@ -38,7 +38,7 @@ export type SliderCommons = { }; // @public (undocumented) -export type SliderProps = Omit, 'onChange' | 'defaultValue'> & SliderCommons; +export type SliderProps = Omit, 'onChange' | 'defaultValue'> & SliderCommons; // @public export const sliderShorthandProps: (keyof SliderSlots)[]; @@ -56,7 +56,7 @@ export type SliderSlots = { export type SliderState = ComponentState & SliderCommons; // @public -export const useSlider: (props: SliderProps, ref: React_2.Ref) => SliderState; +export const useSlider: (props: SliderProps, ref: React_2.Ref) => SliderState; // @public (undocumented) export const useSliderState: (state: SliderState) => SliderState; From ab02143de301defdae8c722ae2710df0e13f9cf3 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Wed, 1 Dec 2021 17:06:54 -0800 Subject: [PATCH 19/53] react components api --- .../etc/react-components.api.md | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/react-components/etc/react-components.api.md b/packages/react-components/etc/react-components.api.md index dd65b2a366fad..eeb95d56344ee 100644 --- a/packages/react-components/etc/react-components.api.md +++ b/packages/react-components/etc/react-components.api.md @@ -273,6 +273,7 @@ import { renderPopover } from '@fluentui/react-popover'; import { renderPopoverSurface } from '@fluentui/react-popover'; import { renderPopoverTrigger } from '@fluentui/react-popover'; import { renderPortal } from '@fluentui/react-portal'; +import { renderSlider } from '@fluentui/react-slider'; import { renderSplitButton } from '@fluentui/react-button'; import { renderText } from '@fluentui/react-text'; import { renderToggleButton } from '@fluentui/react-button'; @@ -283,6 +284,12 @@ import { setVirtualParent } from '@fluentui/react-portal'; import { ShadowBrandTokens } from '@fluentui/react-theme'; import { ShadowTokens } from '@fluentui/react-theme'; import { shorthands } from '@fluentui/react-make-styles'; +import { Slider } from '@fluentui/react-slider'; +import { sliderClassName } from '@fluentui/react-slider'; +import { SliderCommons } from '@fluentui/react-slider'; +import { SliderProps } from '@fluentui/react-slider'; +import { SliderSlots } from '@fluentui/react-slider'; +import { SliderState } from '@fluentui/react-slider'; import { SplitButton } from '@fluentui/react-button'; import { splitButtonClassName } from '@fluentui/react-button'; import { SplitButtonProps } from '@fluentui/react-button'; @@ -392,6 +399,9 @@ import { usePopoverTrigger } from '@fluentui/react-popover'; import { usePortal } from '@fluentui/react-portal'; import { usePresenceBadge } from '@fluentui/react-badge'; import { useRenderer } from '@fluentui/react-make-styles'; +import { useSlider } from '@fluentui/react-slider'; +import { useSliderState } from '@fluentui/react-slider'; +import { useSliderStyles } from '@fluentui/react-slider'; import { useSplitButton } from '@fluentui/react-button'; import { useSplitButtonStyles } from '@fluentui/react-button'; import { useText } from '@fluentui/react-text'; @@ -943,6 +953,8 @@ export { renderPopoverTrigger } export { renderPortal } +export { renderSlider } + export { renderSplitButton } export { renderText } @@ -963,6 +975,18 @@ export { ShadowTokens } export { shorthands } +export { Slider } + +export { sliderClassName } + +export { SliderCommons } + +export { SliderProps } + +export { SliderSlots } + +export { SliderState } + export { SplitButton } export { splitButtonClassName } @@ -1181,6 +1205,12 @@ export { usePresenceBadge } export { useRenderer } +export { useSlider } + +export { useSliderState } + +export { useSliderStyles } + export { useSplitButton } export { useSplitButtonStyles } From 48ab826c0c175cc63c8ccd3d22feaae4f46a5b04 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 7 Dec 2021 08:52:47 -0800 Subject: [PATCH 20/53] revert react components to remove slider --- packages/react-components/.storybook/main.js | 6 +++- .../etc/react-components.api.md | 30 ------------------- packages/react-components/package.json | 1 - packages/react-components/src/index.ts | 9 ------ 4 files changed, 5 insertions(+), 41 deletions(-) diff --git a/packages/react-components/.storybook/main.js b/packages/react-components/.storybook/main.js index 63b66e7805a12..491aecb2d0a41 100644 --- a/packages/react-components/.storybook/main.js +++ b/packages/react-components/.storybook/main.js @@ -9,7 +9,11 @@ module.exports = /** @type {Omit { const localConfig = { ...rootMain.webpackFinal(config, options) }; diff --git a/packages/react-components/etc/react-components.api.md b/packages/react-components/etc/react-components.api.md index eeb95d56344ee..dd65b2a366fad 100644 --- a/packages/react-components/etc/react-components.api.md +++ b/packages/react-components/etc/react-components.api.md @@ -273,7 +273,6 @@ import { renderPopover } from '@fluentui/react-popover'; import { renderPopoverSurface } from '@fluentui/react-popover'; import { renderPopoverTrigger } from '@fluentui/react-popover'; import { renderPortal } from '@fluentui/react-portal'; -import { renderSlider } from '@fluentui/react-slider'; import { renderSplitButton } from '@fluentui/react-button'; import { renderText } from '@fluentui/react-text'; import { renderToggleButton } from '@fluentui/react-button'; @@ -284,12 +283,6 @@ import { setVirtualParent } from '@fluentui/react-portal'; import { ShadowBrandTokens } from '@fluentui/react-theme'; import { ShadowTokens } from '@fluentui/react-theme'; import { shorthands } from '@fluentui/react-make-styles'; -import { Slider } from '@fluentui/react-slider'; -import { sliderClassName } from '@fluentui/react-slider'; -import { SliderCommons } from '@fluentui/react-slider'; -import { SliderProps } from '@fluentui/react-slider'; -import { SliderSlots } from '@fluentui/react-slider'; -import { SliderState } from '@fluentui/react-slider'; import { SplitButton } from '@fluentui/react-button'; import { splitButtonClassName } from '@fluentui/react-button'; import { SplitButtonProps } from '@fluentui/react-button'; @@ -399,9 +392,6 @@ import { usePopoverTrigger } from '@fluentui/react-popover'; import { usePortal } from '@fluentui/react-portal'; import { usePresenceBadge } from '@fluentui/react-badge'; import { useRenderer } from '@fluentui/react-make-styles'; -import { useSlider } from '@fluentui/react-slider'; -import { useSliderState } from '@fluentui/react-slider'; -import { useSliderStyles } from '@fluentui/react-slider'; import { useSplitButton } from '@fluentui/react-button'; import { useSplitButtonStyles } from '@fluentui/react-button'; import { useText } from '@fluentui/react-text'; @@ -953,8 +943,6 @@ export { renderPopoverTrigger } export { renderPortal } -export { renderSlider } - export { renderSplitButton } export { renderText } @@ -975,18 +963,6 @@ export { ShadowTokens } export { shorthands } -export { Slider } - -export { sliderClassName } - -export { SliderCommons } - -export { SliderProps } - -export { SliderSlots } - -export { SliderState } - export { SplitButton } export { splitButtonClassName } @@ -1205,12 +1181,6 @@ export { usePresenceBadge } export { useRenderer } -export { useSlider } - -export { useSliderState } - -export { useSliderStyles } - export { useSplitButton } export { useSplitButtonStyles } diff --git a/packages/react-components/package.json b/packages/react-components/package.json index 986651e139c6a..f27420a704198 100644 --- a/packages/react-components/package.json +++ b/packages/react-components/package.json @@ -53,7 +53,6 @@ "@fluentui/react-popover": "9.0.0-beta.5", "@fluentui/react-portal": "9.0.0-beta.5", "@fluentui/react-provider": "9.0.0-beta.5", - "@fluentui/react-slider": "9.0.0-beta.5", "@fluentui/react-theme": "9.0.0-beta.4", "@fluentui/react-tooltip": "9.0.0-beta.5", "@fluentui/react-utilities": "9.0.0-beta.4", diff --git a/packages/react-components/src/index.ts b/packages/react-components/src/index.ts index 6156fd3ab144e..dc00ebbefdd1a 100644 --- a/packages/react-components/src/index.ts +++ b/packages/react-components/src/index.ts @@ -362,15 +362,6 @@ export type { } from '@fluentui/react-popover'; export { Portal, elementContains, renderPortal, setVirtualParent, usePortal } from '@fluentui/react-portal'; export type { PortalCommons, PortalProps, PortalState } from '@fluentui/react-portal'; -export { - Slider, - renderSlider, - sliderClassName, - useSlider, - useSliderStyles, - useSliderState, -} from '@fluentui/react-slider'; -export type { SliderCommons, SliderProps, SliderSlots, SliderState } from '@fluentui/react-slider'; export { Body, Caption, From e4c542b7541a380af8af6d43c7c6c0eb3e5086b7 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 7 Dec 2021 12:19:00 -0800 Subject: [PATCH 21/53] merge conflicts --- .../src/components/Slider/Slider.types.ts | 23 +-- .../src/components/Slider/renderSlider.tsx | 1 - .../stories/SliderControlled.stories.tsx | 20 +- .../src/components/Slider/useSlider.ts | 9 +- .../src/components/Slider/useSliderState.tsx | 182 ++---------------- .../src/components/Slider/useSliderStyles.ts | 148 ++++++-------- 6 files changed, 80 insertions(+), 303 deletions(-) diff --git a/packages/react-slider/src/components/Slider/Slider.types.ts b/packages/react-slider/src/components/Slider/Slider.types.ts index 55ce69c3baf5f..64b7756677576 100644 --- a/packages/react-slider/src/components/Slider/Slider.types.ts +++ b/packages/react-slider/src/components/Slider/Slider.types.ts @@ -14,11 +14,6 @@ export type SliderSlots = { */ rail: IntrinsicShorthandProps<'div'>; - /** - * The bar showing the current selected area adjacent to the Slider's thumb. - */ - track: IntrinsicShorthandProps<'div'>; - /** * The draggable icon used to select a given value from the Slider. * This is the element containing `role = 'slider'`. @@ -65,15 +60,6 @@ export type SliderCommons = { */ step?: number; - /** - * The number of steps that the Slider's value will change by during a key press. When provided, the `keyboardSteps` - * will be separated from the pointer `steps` allowing for the value to go outside of pointer related - * snapping values. - * - * @default `step` or 1 - */ - keyboardStep?: number; - /** * Whether to render the Slider as disabled. * @@ -102,10 +88,7 @@ export type SliderCommons = { /** * Triggers a callback when the value has been changed. This will be called on every individual step. */ - onChange?: ( - ev: React.PointerEvent | React.KeyboardEvent, - data: { value: number }, - ) => void; + onChange?: (ev: React.ChangeEvent, data: SliderOnChangeData) => void; /** * The Slider's current value label to be read by the screen reader. @@ -113,6 +96,10 @@ export type SliderCommons = { ariaValueText?: (value: number) => string; }; +interface SliderOnChangeData { + value: number; +} + export type SliderProps = Omit, 'onChange' | 'defaultValue'> & SliderCommons; export type SliderState = ComponentState & SliderCommons; diff --git a/packages/react-slider/src/components/Slider/renderSlider.tsx b/packages/react-slider/src/components/Slider/renderSlider.tsx index e6f871de558f2..dc1571f4395d5 100644 --- a/packages/react-slider/src/components/Slider/renderSlider.tsx +++ b/packages/react-slider/src/components/Slider/renderSlider.tsx @@ -12,7 +12,6 @@ export const renderSlider = (state: SliderState) => { return ( - diff --git a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx index 7041d555c84ae..57e7cf6579d58 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx @@ -6,23 +6,15 @@ import { Slider, SliderProps } from '../../../index'; export const Controlled = () => { const [sliderValue, setSliderValue] = React.useState(160); - const onSliderChange: SliderProps['onChange'] = ( - ev: React.PointerEvent | React.KeyboardEvent, - data: { value: number }, - ) => setSliderValue(data.value); + const onSliderChange: SliderProps['onChange'] = (ev, data) => setSliderValue(data.value); return ( <> - - + + + + + ); }; diff --git a/packages/react-slider/src/components/Slider/useSlider.ts b/packages/react-slider/src/components/Slider/useSlider.ts index 7217ea724bb91..cbaf7e40ac10c 100644 --- a/packages/react-slider/src/components/Slider/useSlider.ts +++ b/packages/react-slider/src/components/Slider/useSlider.ts @@ -6,7 +6,7 @@ import { SliderProps, SliderSlots, SliderState } from './Slider.types'; /** * Array of all shorthand properties listed in sliderShorthandProps */ -export const sliderShorthandProps: (keyof SliderSlots)[] = ['root', 'input', 'rail', 'thumb', 'track']; +export const sliderShorthandProps: (keyof SliderSlots)[] = ['root', 'input', 'rail', 'thumb']; /** * Given user props, returns state and render function for a Slider. @@ -15,7 +15,7 @@ export const useSlider = (props: SliderProps, ref: React.Ref): const nativeProps = getPartitionedNativeProps({ props, primarySlotTagName: 'input', - excludedPropNames: [], + excludedPropNames: ['onChange'], }); const { @@ -25,7 +25,6 @@ export const useSlider = (props: SliderProps, ref: React.Ref): min, max, step, - keyboardStep, disabled, ariaValueText, onChange, @@ -38,14 +37,12 @@ export const useSlider = (props: SliderProps, ref: React.Ref): input, rail, thumb, - track, } = props; const state: SliderState = { ariaValueText, defaultValue, disabled, - keyboardStep, max, min, onChange, @@ -59,7 +56,6 @@ export const useSlider = (props: SliderProps, ref: React.Ref): rail: 'div', root: 'div', thumb: 'div', - track: 'div', }, root: resolveShorthand(root, { required: true, @@ -78,7 +74,6 @@ export const useSlider = (props: SliderProps, ref: React.Ref): }), rail: resolveShorthand(rail, { required: true }), thumb: resolveShorthand(thumb, { required: true }), - track: resolveShorthand(track, { required: true }), }; useSliderState(state); diff --git a/packages/react-slider/src/components/Slider/useSliderState.tsx b/packages/react-slider/src/components/Slider/useSliderState.tsx index 34bec29d556f2..6c249a63da589 100644 --- a/packages/react-slider/src/components/Slider/useSliderState.tsx +++ b/packages/react-slider/src/components/Slider/useSliderState.tsx @@ -1,206 +1,48 @@ import * as React from 'react'; -import { useFluent } from '@fluentui/react-shared-contexts'; -import { - clamp, - useBoolean, - useControllableState, - useEventCallback, - useUnmount, - useMergedRefs, -} from '@fluentui/react-utilities'; -import { calculateSteps, getKeydownValue, getPercent, on } from '../../utils/index'; +import { clamp, useControllableState } from '@fluentui/react-utilities'; +import { getPercent } from '../../utils/index'; import type { SliderState } from './Slider.types'; -// TODO: Awaiting animation time from design spec. -export const animationTime = '0.1s'; - export const useSliderState = (state: SliderState) => { - const { - value, - defaultValue = 0, - min = 0, - max = 100, - step = 1, - keyboardStep = state.step || 1, - disabled = false, - ariaValueText, - onChange, - vertical = false, - origin, - } = state; - const { onPointerDown: onPointerDownCallback, onKeyDown: onKeyDownCallback } = state.root; - - const { dir } = useFluent(); + const { value, defaultValue = 0, min = 0, max = 100, step = 1, disabled = false, ariaValueText, origin } = state; - const [stepAnimation, { setTrue: showStepAnimation, setFalse: hideStepAnimation }] = useBoolean(false); - const [renderedPosition, setRenderedPosition] = React.useState(value ? value : defaultValue); const [currentValue, setCurrentValue] = useControllableState({ state: value && clamp(value, min, max), defaultState: clamp(defaultValue, min, max), initialState: 0, }); - const railRef = React.useRef(null); - const inputRef = React.useRef(null); - const disposables = React.useRef<(() => void)[]>([]); - - const valuePercent = getPercent(renderedPosition !== undefined ? renderedPosition : currentValue, min, max); + const valuePercent = getPercent(currentValue, min, max); const originPercent = React.useMemo(() => { return origin !== undefined ? getPercent(origin, min, max) : 0; }, [max, min, origin]); - /** - * Updates the controlled `currentValue` to the new `incomingValue` and clamps it. - */ - const updateValue = useEventCallback( - (incomingValue: number, ev: React.PointerEvent | React.KeyboardEvent): void => { - const clampedValue = clamp(incomingValue, min, max); - - if (clampedValue !== min && clampedValue !== max) { - ev.stopPropagation(); - if (ev.type === 'keydown') { - ev.preventDefault(); - } - } - - onChange?.(ev, { value: clampedValue }); - setCurrentValue(clampedValue); - }, - ); - - /** - * Updates the controlled `currentValue` and `renderedPosition` of the Slider. - */ - const updatePosition = React.useCallback( - (incomingValue: number, ev) => { - setRenderedPosition(clamp(incomingValue, min, max)); - updateValue(incomingValue, ev); - }, - [max, min, updateValue], - ); + const onInputChange: React.ChangeEventHandler = ev => { + const newValue = Number(ev.target.value); + setCurrentValue(newValue); - const onInputChange = (ev: React.ChangeEvent) => { - updatePosition(Number(ev.target.value), ev); + state.onChange && state.onChange(ev, { value: newValue }); }; - const onPointerMove = React.useCallback( - (ev: React.PointerEvent): void => { - const position = calculateSteps(ev, railRef, min, max, step, vertical, dir); - const currentStepPosition = Math.round(position / step) * step; - setRenderedPosition(position); - updateValue(currentStepPosition, ev); - }, - [dir, max, min, step, updateValue, vertical], - ); - - const onPointerUp = React.useCallback( - (ev: React.PointerEvent): void => { - disposables.current.forEach(dispose => dispose()); - disposables.current = []; - showStepAnimation(); - // When undefined, the position fallbacks to the currentValue state. - setRenderedPosition(undefined); - inputRef.current!.focus(); - }, - [showStepAnimation], - ); - - const onPointerDown = React.useCallback( - (ev: React.PointerEvent): void => { - const { pointerId } = ev; - const target = ev.target as HTMLElement; - - target.setPointerCapture?.(pointerId); - - hideStepAnimation(); - onPointerDownCallback?.(ev); - - // eslint-disable-next-line deprecation/deprecation -- Should be remove an replaced with a useEvent hook. - disposables.current.push(on(target, 'pointermove', onPointerMove), on(target, 'pointerup', onPointerUp), () => { - target.releasePointerCapture?.(pointerId); - }); - - onPointerMove(ev); - }, - [hideStepAnimation, onPointerDownCallback, onPointerMove, onPointerUp], - ); - - const onKeyDown = React.useCallback( - (ev: React.KeyboardEvent): void => { - hideStepAnimation(); - onKeyDownCallback?.(ev); - const incomingValue = getKeydownValue(ev, currentValue, min, max, dir, keyboardStep); - - if (currentValue !== incomingValue) { - updatePosition(incomingValue, ev); - } - }, - [currentValue, dir, hideStepAnimation, keyboardStep, max, min, onKeyDownCallback, updatePosition], - ); - - useUnmount(() => { - disposables.current.forEach(dispose => dispose()); - disposables.current = []; - }); - - const thumbVariables = { + const rootVariables = { '--slider-thumb-position': valuePercent + '%', - '--slider-thumb-transition': stepAnimation ? `left ease-in-out ${animationTime}` : 'none', - }; - - const railVariables = { '--slider-rail-steps-percent': state.step ? `${(step * 100) / (max - min)}%` : '', - }; - const trackVariables = { - '--slider-track-offset': origin !== undefined ? `${Math.min(valuePercent, originPercent)}%` : 0, - '--slider-track-progress': + '--slider-rail-offset': origin !== undefined ? `${Math.min(valuePercent, originPercent)}%` : '0%', + '--slider-rail-progress': origin !== undefined ? `${Math.max(originPercent - valuePercent, valuePercent - originPercent)}%` : `${valuePercent}%`, - '--slider-track-transition': stepAnimation - ? `${vertical ? 'height' : 'width'} ease-in-out ${animationTime}${ - origin !== undefined - ? ', ' + vertical - ? 'top' - : dir === 'rtl' - ? 'right' - : 'left' + 'ease-in-out ' + animationTime - : '' - }` - : 'none', - '--slider-track-border-radius': - // if has !origin, border on one end - origin !== undefined && origin !== (max || min) - ? // top or bottom if vertical - vertical - ? originPercent > valuePercent - ? '99px 99px 0px 0px' - : '0px 0px 99px 99px' - : // left or right depending on rtl and values - (dir === 'rtl' ? valuePercent > originPercent : valuePercent < originPercent) - ? '99px 0px 0px 99px' - : '0px 99px 99px 0px' - : '99px', }; // Root props state.root.style = { - ...railVariables, - ...thumbVariables, - ...trackVariables, + ...rootVariables, ...state.root.style, }; - if (!disabled) { - state.root.onPointerDown = onPointerDown; - state.root.onKeyDown = onKeyDown; - } - // Rail Props - state.rail.ref = railRef; // Input Props - state.input.ref = useMergedRefs(state.input.ref, inputRef); state.input.value = currentValue; state.input.min = min; state.input.max = max; diff --git a/packages/react-slider/src/components/Slider/useSliderStyles.ts b/packages/react-slider/src/components/Slider/useSliderStyles.ts index 0cede2dc71300..4426732def9da 100644 --- a/packages/react-slider/src/components/Slider/useSliderStyles.ts +++ b/packages/react-slider/src/components/Slider/useSliderStyles.ts @@ -5,22 +5,19 @@ import type { SliderState } from './Slider.types'; export const sliderClassName = 'fui-Slider'; const thumbClassName = `${sliderClassName}-thumb`; -const trackClassName = `${sliderClassName}-track`; const railClassName = `${sliderClassName}-rail`; -const stepIndex = 10; -const thumbIndex = 20; /** * Styles for the root slot */ export const useRootStyles = makeStyles({ - root: theme => ({ + root: { position: 'relative', display: 'inline-grid', gridTemplateAreas: '"slider"', userSelect: 'none', touchAction: 'none', - }), + }, small: { '--slider-thumb-size': '10px', @@ -39,7 +36,6 @@ export const useRootStyles = makeStyles({ }, vertical: { - transform: 'scaleY(-1)', width: 'var(--slider-thumb-size)', minHeight: '120px', justifyItems: 'center', @@ -47,32 +43,37 @@ export const useRootStyles = makeStyles({ }, enabled: theme => ({ - cursor: 'grab', + '--slider-rail-color': theme.colorNeutralStrokeAccessible, + '--slider-progress-color': theme.colorCompoundBrandBackground, ':hover': { [`& .${thumbClassName}`]: { background: theme.colorBrandBackgroundHover, }, - [`& .${trackClassName}`]: { - background: theme.colorBrandBackgroundHover, - }, + '--slider-progress-color': theme.colorBrandBackgroundHover, }, ':active': { - cursor: 'grabbing', [`& .${thumbClassName}`]: { background: theme.colorBrandBackgroundPressed, }, - [`& .${trackClassName}`]: { - background: theme.colorBrandBackgroundPressed, - }, + '--slider-progress-color': theme.colorBrandBackgroundPressed, }, }), disabled: theme => ({ - cursor: 'not-allowed', + '--slider-rail-color': theme.colorNeutralBackgroundDisabled, + '--slider-progress-color': theme.colorNeutralForegroundDisabled, }), - focusIndicator: theme => - createFocusOutlineStyle(theme, { selector: 'focus-within', style: { outlineOffset: '6px' } }), + focusIndicatorHorizontal: theme => + createFocusOutlineStyle(theme, { + selector: 'focus-within', + style: { outlineOffset: { top: '6px', bottom: '6px', left: '10px', right: '10px' } }, + }), + focusIndicatorVertical: theme => + createFocusOutlineStyle(theme, { + selector: 'focus-within', + style: { outlineOffset: { top: '10px', bottom: '10px', left: '6px', right: '6px' } }, + }), }); /** @@ -84,103 +85,65 @@ export const useRailStyles = makeStyles({ pointerEvents: 'none', gridArea: 'slider', position: 'relative', + background: `linear-gradient( + var(--slider-rail-direction), + var(--slider-rail-color) 0%, + var(--slider-rail-color) var(--slider-rail-offset), + var(--slider-progress-color) var(--slider-rail-offset), + var(--slider-progress-color) calc(var(--slider-rail-offset) + var(--slider-rail-progress)), + var(--slider-rail-color) calc(var(--slider-rail-offset) + var(--slider-rail-progress)) + )`, ':before': { content: "''", position: 'absolute', - zIndex: stepIndex, + background: `repeating-linear-gradient( + var(--slider-rail-direction), + #0000 0%, + #0000 calc(var(--slider-rail-steps-percent) - 1px), + #fff calc(var(--slider-rail-steps-percent) - 1px), + #fff var(--slider-rail-steps-percent) + )`, }, }), - enabled: theme => ({ - backgroundColor: theme.colorNeutralStrokeAccessible, - }), - disabled: theme => ({ - backgroundColor: theme.colorNeutralBackgroundDisabled, ...shorthands.border('1px', 'solid', theme.colorTransparentStrokeDisabled), }), horizontal: { + '--slider-rail-direction': '90deg', height: 'var(--slider-rail-size)', ':before': { left: '-1px', right: '-1px', height: 'var(--slider-rail-size)', - background: `repeating-linear-gradient( - 90deg, - #0000 0%, - #0000 calc(var(--slider-rail-steps-percent) - 1px), - #fff calc(var(--slider-rail-steps-percent) - 1px), - #fff var(--slider-rail-steps-percent) - )`, }, }, vertical: { + '--slider-rail-direction': '0deg', width: 'var(--slider-rail-size)', height: '100%', ':before': { width: 'var(--slider-rail-size)', - top: '1px', - bottom: '-1px', - background: `repeating-linear-gradient( - 180deg, - #0000 0%, - #0000 calc(var(--slider-rail-steps-percent) - 1px), - #fff calc(var(--slider-rail-steps-percent) - 1px), - #fff var(--slider-rail-steps-percent) - )`, + top: '-1px', + bottom: '1px', }, }, }); -/** - * Styles for the track slot - */ -export const useTrackStyles = makeStyles({ - track: theme => ({ - ...shorthands.borderRadius(`var(--slider-track-border-radius, ${theme.borderRadiusXLarge})`), - gridArea: 'slider', - position: 'relative', - transition: 'var(--slider-track-transition)', - }), - - horizontal: { - height: 'var(--slider-rail-size)', - left: 'var(--slider-track-offset)', - width: 'var(--slider-track-progress)', - }, - - vertical: { - width: 'var(--slider-rail-size)', - top: 'var(--slider-track-offset)', - height: 'var(--slider-track-progress)', - }, - - enabled: theme => ({ - backgroundColor: theme.colorCompoundBrandBackground, - }), - - disabled: theme => ({ - backgroundColor: theme.colorNeutralForegroundDisabled, - }), -}); - /** * Styles for the thumb slot */ export const useThumbStyles = makeStyles({ thumb: theme => ({ position: 'absolute', - zIndex: thumbIndex, width: 'var(--slider-thumb-size)', height: 'var(--slider-thumb-size)', outline: 'none', ...shorthands.borderRadius(theme.borderRadiusCircular), boxShadow: `0 0 0 calc(var(--slider-thumb-size) * .2) ${theme.colorNeutralBackground1} inset`, transform: 'translateX(-50%)', - - transition: 'var(--slider-thumb-transition)', ':before': { position: 'absolute', top: '0px', @@ -208,8 +171,8 @@ export const useThumbStyles = makeStyles({ left: 'var(--slider-thumb-position)', }, vertical: { - transform: 'translateY(-50%)', - top: 'var(--slider-thumb-position)', + transform: 'translateY(50%)', + bottom: 'var(--slider-thumb-position)', }, }); @@ -220,11 +183,16 @@ const useInputStyles = makeStyles({ input: { opacity: 0, gridArea: 'slider', - ...shorthands.padding('0'), - ...shorthands.margin('0'), + ...shorthands.padding(0), + ...shorthands.margin(0), + }, + horizontal: { height: 'var(--slider-thumb-size)', - touchAction: 'none', - pointerEvents: 'none', + }, + vertical: { + writingMode: 'bt-lr', + '-webkit-appearance': 'slider-vertical', + width: 'var(--slider-thumb-size)', }, }); @@ -234,18 +202,16 @@ const useInputStyles = makeStyles({ export const useSliderStyles = (state: SliderState): SliderState => { const rootStyles = useRootStyles(); const railStyles = useRailStyles(); - const trackStyles = useTrackStyles(); const thumbStyles = useThumbStyles(); const inputStyles = useInputStyles(); state.root.className = mergeClasses( sliderClassName, rootStyles.root, - rootStyles.focusIndicator, + state.vertical ? rootStyles.focusIndicatorVertical : rootStyles.focusIndicatorHorizontal, rootStyles[state.size!], state.vertical ? rootStyles.vertical : rootStyles.horizontal, state.disabled ? rootStyles.disabled : rootStyles.enabled, - rootStyles.focusIndicator, state.root.className, ); @@ -253,18 +219,10 @@ export const useSliderStyles = (state: SliderState): SliderState => { railClassName, railStyles.rail, state.vertical ? railStyles.vertical : railStyles.horizontal, - state.disabled ? railStyles.disabled : railStyles.enabled, + state.disabled && railStyles.disabled, state.rail.className, ); - state.track.className = mergeClasses( - trackClassName, - trackStyles.track, - state.vertical ? trackStyles.vertical : trackStyles.horizontal, - state.disabled ? trackStyles.disabled : trackStyles.enabled, - state.track.className, - ); - state.thumb.className = mergeClasses( thumbClassName, thumbStyles.thumb, @@ -273,7 +231,11 @@ export const useSliderStyles = (state: SliderState): SliderState => { state.thumb.className, ); - state.input.className = mergeClasses(inputStyles.input, state.input.className); + state.input.className = mergeClasses( + inputStyles.input, + state.vertical ? inputStyles.vertical : inputStyles.horizontal, + state.input.className, + ); return state; }; From 334e6329353fa2d0da9f526fce82f9831245100e Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 7 Dec 2021 13:24:29 -0800 Subject: [PATCH 22/53] update api md --- packages/react-slider/etc/react-slider.api.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/react-slider/etc/react-slider.api.md b/packages/react-slider/etc/react-slider.api.md index 483b5913e11ff..98d0628e80c7e 100644 --- a/packages/react-slider/etc/react-slider.api.md +++ b/packages/react-slider/etc/react-slider.api.md @@ -26,14 +26,11 @@ export type SliderCommons = { min?: number; max?: number; step?: number; - keyboardStep?: number; disabled?: boolean; vertical?: boolean; origin?: number; size?: 'small' | 'medium'; - onChange?: (ev: React_2.PointerEvent | React_2.KeyboardEvent, data: { - value: number; - }) => void; + onChange?: (ev: React_2.ChangeEvent, data: SliderOnChangeData) => void; ariaValueText?: (value: number) => string; }; @@ -47,7 +44,6 @@ export const sliderShorthandProps: (keyof SliderSlots)[]; export type SliderSlots = { root: IntrinsicShorthandProps<'div'>; rail: IntrinsicShorthandProps<'div'>; - track: IntrinsicShorthandProps<'div'>; thumb: IntrinsicShorthandProps<'div'>; input: IntrinsicShorthandProps<'input'>; }; @@ -64,6 +60,10 @@ export const useSliderState: (state: SliderState) => SliderState; // @public export const useSliderStyles: (state: SliderState) => SliderState; +// Warnings were encountered during analysis: +// +// lib/components/Slider/Slider.types.d.ts:77:5 - (ae-forgotten-export) The symbol "SliderOnChangeData" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` From efec9c4be3f2aaa714a667d9c7d0f3bf3057685b Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 7 Dec 2021 15:24:30 -0800 Subject: [PATCH 23/53] Delete @fluentui-react-components-2b36a74e-2147-4df0-a4f2-ab6c12c56033.json --- ...ct-components-2b36a74e-2147-4df0-a4f2-ab6c12c56033.json | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 change/@fluentui-react-components-2b36a74e-2147-4df0-a4f2-ab6c12c56033.json diff --git a/change/@fluentui-react-components-2b36a74e-2147-4df0-a4f2-ab6c12c56033.json b/change/@fluentui-react-components-2b36a74e-2147-4df0-a4f2-ab6c12c56033.json deleted file mode 100644 index 7f05f8e9393e4..0000000000000 --- a/change/@fluentui-react-components-2b36a74e-2147-4df0-a4f2-ab6c12c56033.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "type": "prerelease", - "comment": "Export slider package", - "packageName": "@fluentui/react-components", - "email": "mgodbolt@microsoft.com", - "dependentChangeType": "patch" -} From b2a4ced52752314b7bdc2a83a2494568e4a9ac3e Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 7 Dec 2021 15:34:21 -0800 Subject: [PATCH 24/53] cleanup testing --- .../src/components/Slider/Slider.test.tsx | 237 +++--------------- .../src/components/Slider/Slider.types.ts | 4 +- .../Slider/__snapshots__/Slider.test.tsx.snap | 25 +- .../src/components/Slider/useSliderState.tsx | 7 +- 4 files changed, 39 insertions(+), 234 deletions(-) diff --git a/packages/react-slider/src/components/Slider/Slider.test.tsx b/packages/react-slider/src/components/Slider/Slider.test.tsx index 954e041456f4a..e3f6ca1bd69b1 100644 --- a/packages/react-slider/src/components/Slider/Slider.test.tsx +++ b/packages/react-slider/src/components/Slider/Slider.test.tsx @@ -1,9 +1,7 @@ import * as React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import { resetIdsForTests } from '@fluentui/react-utilities'; -// TODO: Find a way to use pointer events with testing-library and remove enzyme. -// github.com/microsoft/fluentui/issues/19977 -import { mount, ReactWrapper } from 'enzyme'; +import { ArrowLeft, ArrowRight, ArrowDown, PageDown, PageUp, ArrowUp, Home, End } from '@fluentui/keyboard-keys'; import { Slider } from './Slider'; import { isConformant } from '../../common/isConformant'; @@ -50,46 +48,21 @@ describe('Slider', () => { describe('Unit Tests', () => { it('handles id prop', () => { - render(); + render(); const sliderRoot = screen.getByTestId('test'); expect(sliderRoot.getAttribute('id')).toEqual('test_id'); }); - it('slides to the correct position when dragged in-between steps', () => { - const inputRef = React.createRef(); + it.skip('calls onChange when pointerDown', () => { const onChange = jest.fn(); - const wrapper: ReactWrapper = mount( - , + const { getByTestId } = render( + , ); - const activeRail = wrapper.find('.active-rail'); - - activeRail.getDOMNode().getBoundingClientRect = () => - ({ left: 0, top: 0, right: 100, bottom: 40, width: 100, height: 40 } as DOMRect); - - wrapper.simulate('pointerdown', { type: 'pointermove', clientX: 45, clientY: 0 }); - expect(onChange).toBeCalledTimes(1); - expect(onChange.mock.calls[0][1]).toEqual({ value: 50 }); - expect(inputRef.current?.value).toEqual('50'); - expect(wrapper.find('.fui-Slider-track').props().style?.width).toEqual('45%'); - - wrapper.simulate('pointerdown', { type: 'pointermove', clientX: 24, clientY: 0 }); - expect(onChange).toBeCalledTimes(2); - expect(onChange.mock.calls[1][1]).toEqual({ value: 20 }); - expect(inputRef.current?.value).toEqual('20'); - expect(wrapper.find('.fui-Slider-track').props().style?.width).toEqual('24%'); - }); - - it('calls onChange when pointerDown', () => { - const onChange = jest.fn(); - - render(); - - const sliderRoot = screen.getByTestId('test'); - expect(onChange).toBeCalledTimes(0); - fireEvent.pointerDown(sliderRoot, { clientX: 0, clientY: 0 }); - expect(onChange).toBeCalledTimes(1); + expect(onChange).toHaveBeenCalledTimes(0); + fireEvent.pointerDown(getByTestId('test'), { clientX: 0, clientY: 0 }); + expect(onChange).toHaveBeenCalledTimes(1); expect(onChange.mock.calls[0][1]).toEqual({ value: 0 }); }); @@ -135,106 +108,60 @@ describe('Slider', () => { expect(inputRef.current?.value).toEqual('0'); }); - it('slides to min/max and executes onChange', () => { - const inputRef = React.createRef(); - const onChange = jest.fn(); - - const wrapper: ReactWrapper = mount(); - const sliderRoot = wrapper.first(); - - expect(onChange).toBeCalledTimes(0); - - sliderRoot.getDOMNode().getBoundingClientRect = () => - ({ left: 0, top: 0, right: 100, bottom: 40, width: 100, height: 40 } as DOMRect); - - sliderRoot.simulate('pointerdown', { type: 'pointermove', clientX: 110, clientY: 0 }); - expect(onChange).toBeCalledTimes(1); - expect(onChange.mock.calls[0][1]).toEqual({ value: 100 }); - expect(inputRef.current?.value).toEqual('100'); - expect(wrapper.find('.fui-Slider-track').props().style?.width).toEqual('100%'); - - sliderRoot.simulate('pointerdown', { type: 'pointermove', clientX: -10, clientY: 0 }); - expect(onChange).toBeCalledTimes(2); - expect(onChange.mock.calls[1][1]).toEqual({ value: 0 }); - expect(inputRef.current?.value).toEqual('0'); - expect(wrapper.find('.fui-Slider-track').props().style?.width).toEqual('0%'); - - wrapper.unmount(); - }); - - it('clamps to the correct value when dragged in-between steps', () => { - const inputRef = React.createRef(); - const onChange = jest.fn(); - const wrapper: ReactWrapper = mount( - , - ); - - const activeRail = wrapper.find('.active-rail'); - - activeRail.getDOMNode().getBoundingClientRect = () => - ({ left: 0, top: 0, right: 100, bottom: 40, width: 100, height: 40 } as DOMRect); - - wrapper.simulate('pointerdown', { type: 'pointermove', clientX: 45, clientY: 0 }, { type: 'pointerup' }); - expect(onChange).toBeCalledTimes(1); - expect(onChange.mock.calls[0][1]).toEqual({ value: 50 }); - expect(inputRef.current?.value).toEqual('50'); - expect(wrapper.find('.fui-Slider-track').props().style?.width).toEqual('45%'); - - wrapper.simulate('pointerdown', { type: 'pointermove', clientX: 24, clientY: 0 }, { type: 'pointerup' }); - expect(onChange).toBeCalledTimes(2); - expect(onChange.mock.calls[1][1]).toEqual({ value: 20 }); - expect(inputRef.current?.value).toEqual('20'); - expect(wrapper.find('.fui-Slider-track').props().style?.width).toEqual('24%'); - }); - - it('handles keydown events', () => { + it.skip('handles keydown events', () => { const inputRef = React.createRef(); const onChange = jest.fn(); render( - , + , ); - const sliderRoot = screen.getByTestId('test'); + const sliderInput = screen.getByTestId('test'); expect(onChange).toBeCalledTimes(0); - fireEvent.keyDown(sliderRoot, { key: 'ArrowDown' }); + fireEvent.keyDown(sliderInput, { key: ArrowDown }); expect(onChange.mock.calls[0][1]).toEqual({ value: 49 }); expect(inputRef.current?.value).toEqual('49'); - fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); + fireEvent.keyDown(sliderInput, { key: ArrowUp }); expect(onChange.mock.calls[1][1]).toEqual({ value: 50 }); expect(inputRef.current?.value).toEqual('50'); - fireEvent.keyDown(sliderRoot, { key: 'ArrowLeft' }); + fireEvent.keyDown(sliderInput, { key: ArrowLeft }); expect(onChange.mock.calls[2][1]).toEqual({ value: 49 }); expect(inputRef.current?.value).toEqual('49'); - fireEvent.keyDown(sliderRoot, { key: 'ArrowRight' }); + fireEvent.keyDown(sliderInput, { key: ArrowRight }); expect(onChange.mock.calls[3][1]).toEqual({ value: 50 }); expect(inputRef.current?.value).toEqual('50'); - fireEvent.keyDown(sliderRoot, { key: 'PageUp' }); + fireEvent.keyDown(sliderInput, { key: PageUp }); expect(onChange.mock.calls[4][1]).toEqual({ value: 60 }); expect(inputRef.current?.value).toEqual('60'); - fireEvent.keyDown(sliderRoot, { key: 'PageDown' }); + fireEvent.keyDown(sliderInput, { key: PageDown }); expect(onChange.mock.calls[5][1]).toEqual({ value: 50 }); expect(inputRef.current?.value).toEqual('50'); - fireEvent.keyDown(sliderRoot, { key: 'Home' }); + fireEvent.keyDown(sliderInput, { key: Home }); expect(onChange.mock.calls[6][1]).toEqual({ value: 0 }); expect(inputRef.current?.value).toEqual('0'); - fireEvent.keyDown(sliderRoot, { key: 'End' }); + fireEvent.keyDown(sliderInput, { key: End }); expect(onChange.mock.calls[7][1]).toEqual({ value: 100 }); expect(inputRef.current?.value).toEqual('100'); - fireEvent.keyDown(sliderRoot, { key: 'ArrowLeft', shiftKey: true }); + fireEvent.keyDown(sliderInput, { key: ArrowLeft, shiftKey: true }); expect(onChange.mock.calls[8][1]).toEqual({ value: 90 }); expect(inputRef.current?.value).toEqual('90'); - fireEvent.keyDown(sliderRoot, { key: 'ArrowRight', shiftKey: true }); + fireEvent.keyDown(sliderInput, { key: ArrowRight, shiftKey: true }); expect(onChange.mock.calls[9][1]).toEqual({ value: 100 }); expect(inputRef.current?.value).toEqual('100'); @@ -250,7 +177,7 @@ describe('Slider', () => { expect(inputRef.current?.value).toBe('50'); }); - it('calls onChange with the correct value', () => { + it.skip('calls onChange with the correct value', () => { const onChange = jest.fn(); render(); @@ -264,89 +191,6 @@ describe('Slider', () => { expect(onChange.mock.calls[2][1]).toEqual({ value: 51 }); }); - it('correctly calculates the horizontal origin border-radius', () => { - const { container } = render(); - - const sliderRoot = screen.getByTestId('test'); - const sliderTrack = container.querySelector('.fui-Slider-track'); - - fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); - expect(sliderTrack?.getAttribute('style')).toContain('0px 99px 99px 0px'); - fireEvent.keyDown(sliderRoot, { key: 'ArrowDown' }); - fireEvent.keyDown(sliderRoot, { key: 'ArrowDown' }); - expect(sliderTrack?.getAttribute('style')).toContain('99px 0px 0px 99px'); - }); - - it('correctly calculates the vertical origin border-radius', () => { - const { container } = render(); - - const sliderRoot = screen.getByTestId('test'); - const sliderTrack = container.querySelector('.fui-Slider-track'); - - fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); - expect(sliderTrack?.getAttribute('style')).toContain('0px 0px 99px 99px'); - fireEvent.keyDown(sliderRoot, { key: 'ArrowDown' }); - fireEvent.keyDown(sliderRoot, { key: 'ArrowDown' }); - expect(sliderTrack?.getAttribute('style')).toContain('99px 99px 0px 0px'); - }); - - it('correctly calculates the origin border-radius when given min as the origin', () => { - const { container } = render(); - const sliderTrack = container.querySelector('.fui-Slider-track'); - expect(sliderTrack?.getAttribute('style')).toContain('99px'); - }); - - it('correctly calculates the origin border-radius when given max as the origin', () => { - const { container } = render(); - const sliderTrack = container.querySelector('.fui-Slider-track'); - expect(sliderTrack?.getAttribute('style')).toContain('99px'); - }); - - it('handles a keyboardStep prop', () => { - const inputRef = React.createRef(); - const onChange = jest.fn(); - - render( - , - ); - const sliderRoot = screen.getByTestId('test'); - - fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); - expect(onChange.mock.calls[0][1]).toEqual({ value: 25 }); - expect(inputRef.current?.value).toBe('25'); - }); - - it('handles a negative step prop', () => { - const inputRef = React.createRef(); - const onChange = jest.fn(); - - render(); - const sliderRoot = screen.getByTestId('test'); - - fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); - expect(onChange.mock.calls[0][1]).toEqual({ value: 17 }); - expect(inputRef.current?.value).toBe('17'); - }); - - it('handles a decimal step prop', () => { - const inputRef = React.createRef(); - const onChange = jest.fn(); - - render(); - const sliderRoot = screen.getByTestId('test'); - - fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); - expect(onChange.mock.calls[0][1]).toEqual({ value: 0.001 }); - expect(inputRef.current?.value).toBe('0.001'); - }); - it('applies focus to the hidden input', () => { const inputRef = React.createRef(); render(); @@ -379,31 +223,6 @@ describe('Slider', () => { fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); expect(eventHandler).toBeCalledTimes(0); }); - - it('handles onKeyDown callback', () => { - const eventHandler = jest.fn(); - - render(); - const sliderRoot = screen.getByTestId('test'); - - expect(eventHandler).toBeCalledTimes(0); - - fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); - expect(eventHandler).toBeCalledTimes(1); - }); - - it('handles onPointerDown callback', () => { - const eventHandler = jest.fn(); - - const wrapper: ReactWrapper = mount(); - const sliderRoot = wrapper.first(); - - expect(eventHandler).toBeCalledTimes(0); - sliderRoot.simulate('pointerdown', { type: 'pointerMove', clientX: 87, clientY: 32 }); - expect(eventHandler).toBeCalledTimes(1); - - wrapper.unmount(); - }); }); describe('Accessibility Tests', () => { diff --git a/packages/react-slider/src/components/Slider/Slider.types.ts b/packages/react-slider/src/components/Slider/Slider.types.ts index 64b7756677576..f11eb627d77b9 100644 --- a/packages/react-slider/src/components/Slider/Slider.types.ts +++ b/packages/react-slider/src/components/Slider/Slider.types.ts @@ -100,6 +100,8 @@ interface SliderOnChangeData { value: number; } -export type SliderProps = Omit, 'onChange' | 'defaultValue'> & SliderCommons; +type SlotProps = Omit, 'onChange' | 'defaultValue'>; + +export type SliderProps = SlotProps & SliderCommons; export type SliderState = ComponentState & SliderCommons; diff --git a/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap b/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap index f25a61b197765..97715bb761228 100644 --- a/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap +++ b/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap @@ -4,20 +4,17 @@ exports[`Slider Snapshot Tests renders disabled Slider correctly 1`] = `
-
-
-
-
-
{ return origin !== undefined ? getPercent(origin, min, max) : 0; }, [max, min, origin]); - const onInputChange: React.ChangeEventHandler = ev => { + const onChange: React.ChangeEventHandler = ev => { const newValue = Number(ev.target.value); setCurrentValue(newValue); - state.onChange && state.onChange(ev, { value: newValue }); }; const rootVariables = { '--slider-thumb-position': valuePercent + '%', - '--slider-rail-steps-percent': state.step ? `${(step * 100) / (max - min)}%` : '', + '--slider-rail-steps-percent': state.step && state.step > 0 ? `${(step * 100) / (max - min)}%` : '', '--slider-rail-offset': origin !== undefined ? `${Math.min(valuePercent, originPercent)}%` : '0%', '--slider-rail-progress': origin !== undefined @@ -49,7 +48,7 @@ export const useSliderState = (state: SliderState) => { ariaValueText && (state.input['aria-valuetext'] = ariaValueText(currentValue!)); state.input.disabled = disabled; state.input.step = step; - state.input.onChange = onInputChange; + state.input.onChange = onChange; return state; }; From ac453d30306aa074a840160e1e6862694a86984e Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 7 Dec 2021 15:59:35 -0800 Subject: [PATCH 25/53] slider types --- packages/react-slider/src/components/Slider/Slider.types.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/react-slider/src/components/Slider/Slider.types.ts b/packages/react-slider/src/components/Slider/Slider.types.ts index f11eb627d77b9..64b7756677576 100644 --- a/packages/react-slider/src/components/Slider/Slider.types.ts +++ b/packages/react-slider/src/components/Slider/Slider.types.ts @@ -100,8 +100,6 @@ interface SliderOnChangeData { value: number; } -type SlotProps = Omit, 'onChange' | 'defaultValue'>; - -export type SliderProps = SlotProps & SliderCommons; +export type SliderProps = Omit, 'onChange' | 'defaultValue'> & SliderCommons; export type SliderState = ComponentState & SliderCommons; From 30ce476c7a183522a182f565d3d08c55f5520bfa Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 7 Dec 2021 17:14:26 -0800 Subject: [PATCH 26/53] simplify onChange prop --- .../src/stories/SliderConverged.stories.tsx | 190 +----------------- .../src/components/Slider/Slider.types.ts | 6 +- .../stories/SliderControlled.stories.tsx | 2 +- .../Slider/stories/SliderDefault.stories.tsx | 2 +- .../src/components/Slider/useSlider.ts | 4 +- .../src/components/Slider/useSliderState.tsx | 35 +++- 6 files changed, 34 insertions(+), 205 deletions(-) diff --git a/apps/vr-tests/src/stories/SliderConverged.stories.tsx b/apps/vr-tests/src/stories/SliderConverged.stories.tsx index e4f714d1ace55..dcdc2dff50d1c 100644 --- a/apps/vr-tests/src/stories/SliderConverged.stories.tsx +++ b/apps/vr-tests/src/stories/SliderConverged.stories.tsx @@ -3,18 +3,6 @@ import Screener, { Steps } from 'screener-storybook/src/screener'; import { storiesOf } from '@storybook/react'; import { Slider } from '@fluentui/react-slider'; -const LabelComponent = () =>
; - -const MarkComponent = () => ( -
-); - storiesOf('Slider Converged', module) .addDecorator(story => ( ) .addStory('Origin Vertical (max)', () => ( - )) - .addStory('Marks', () => , { - includeRtl: true, - includeHighContrast: true, - includeDarkMode: true, - }) - .addStory( - 'Marks Vertical', - () => , - { - includeRtl: true, - }, - ) - .addStory( - 'Marks Custom Value', - () => , - { - includeRtl: true, - }, - ) - .addStory( - 'Marks Custom Value Vertical', - () => ( - - ), - { - includeRtl: true, - }, - ) - .addStory( - 'Marks Label Value', - () => ( - - ), - { - includeRtl: true, - }, - ) - .addStory( - 'Marks Label Vertical', - () => ( - - ), - { - includeRtl: true, - }, - ) - .addStory('Marks Label Disabled', () => ( - - )) - .addStory( - 'Marks Custom Label Value', - () => ( - , - }, - { value: 4, label: 'world' }, - 8, - ]} - /> - ), - { - includeRtl: true, - }, - ) - .addStory( - 'Marks Custom Label Vertical', - () => ( - , - }, - { value: 4, label: 'world' }, - 8, - ]} - /> - ), - { - includeRtl: true, - }, - ) - .addStory( - 'Custom Marks', - () => ( - , - mark: , - }, - { value: 4, label: 'world' }, - 8, - ]} - /> - ), - { - includeRtl: true, - }, - ) - .addStory( - 'Custom Marks Vertical', - () => ( - , - mark: , - }, - { value: 4, label: 'world' }, - 8, - ]} - /> - ), - { - includeRtl: true, - }, - ); + )); diff --git a/packages/react-slider/src/components/Slider/Slider.types.ts b/packages/react-slider/src/components/Slider/Slider.types.ts index 64b7756677576..0849c5021ddee 100644 --- a/packages/react-slider/src/components/Slider/Slider.types.ts +++ b/packages/react-slider/src/components/Slider/Slider.types.ts @@ -88,7 +88,7 @@ export type SliderCommons = { /** * Triggers a callback when the value has been changed. This will be called on every individual step. */ - onChange?: (ev: React.ChangeEvent, data: SliderOnChangeData) => void; + onChange?: (ev: React.ChangeEvent) => void; /** * The Slider's current value label to be read by the screen reader. @@ -96,10 +96,6 @@ export type SliderCommons = { ariaValueText?: (value: number) => string; }; -interface SliderOnChangeData { - value: number; -} - export type SliderProps = Omit, 'onChange' | 'defaultValue'> & SliderCommons; export type SliderState = ComponentState & SliderCommons; diff --git a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx index 57e7cf6579d58..59377ee9a856a 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx @@ -6,7 +6,7 @@ import { Slider, SliderProps } from '../../../index'; export const Controlled = () => { const [sliderValue, setSliderValue] = React.useState(160); - const onSliderChange: SliderProps['onChange'] = (ev, data) => setSliderValue(data.value); + const onSliderChange: SliderProps['onChange'] = ev => setSliderValue(Number(ev.target.value)); return ( <> diff --git a/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx index 9194decc73775..372298d2e6b4d 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx @@ -7,6 +7,6 @@ import { Slider } from '../../../index'; export const Default = () => ( <> - + ); diff --git a/packages/react-slider/src/components/Slider/useSlider.ts b/packages/react-slider/src/components/Slider/useSlider.ts index cbaf7e40ac10c..26a64157f5e77 100644 --- a/packages/react-slider/src/components/Slider/useSlider.ts +++ b/packages/react-slider/src/components/Slider/useSlider.ts @@ -15,7 +15,7 @@ export const useSlider = (props: SliderProps, ref: React.Ref): const nativeProps = getPartitionedNativeProps({ props, primarySlotTagName: 'input', - excludedPropNames: ['onChange'], + excludedPropNames: [], }); const { @@ -27,7 +27,6 @@ export const useSlider = (props: SliderProps, ref: React.Ref): step, disabled, ariaValueText, - onChange, vertical, size = 'medium', origin, @@ -45,7 +44,6 @@ export const useSlider = (props: SliderProps, ref: React.Ref): disabled, max, min, - onChange, origin, size, step, diff --git a/packages/react-slider/src/components/Slider/useSliderState.tsx b/packages/react-slider/src/components/Slider/useSliderState.tsx index 768a8e4e73c9b..165cd51a6aca3 100644 --- a/packages/react-slider/src/components/Slider/useSliderState.tsx +++ b/packages/react-slider/src/components/Slider/useSliderState.tsx @@ -3,8 +3,34 @@ import { clamp, useControllableState } from '@fluentui/react-utilities'; import { getPercent } from '../../utils/index'; import type { SliderState } from './Slider.types'; +/** + * Combine up to two event callbacks into a single function that calls them in order + */ +const useMergedCallbacks = ( + callback1: ((ev: Event) => void) | undefined, + callback2: ((ev: Event) => void) | undefined, +) => { + return React.useCallback( + (ev: Event) => { + callback1?.(ev); + callback2?.(ev); + }, + [callback1, callback2], + ); +}; + export const useSliderState = (state: SliderState) => { - const { value, defaultValue = 0, min = 0, max = 100, step = 1, disabled = false, ariaValueText, origin } = state; + const { + value, + defaultValue = 0, + min = 0, + max = 100, + step = 1, + disabled = false, + ariaValueText, + origin, + input, + } = state; const [currentValue, setCurrentValue] = useControllableState({ state: value && clamp(value, min, max), @@ -17,10 +43,9 @@ export const useSliderState = (state: SliderState) => { return origin !== undefined ? getPercent(origin, min, max) : 0; }, [max, min, origin]); - const onChange: React.ChangeEventHandler = ev => { + const onInputChange: React.ChangeEventHandler = ev => { const newValue = Number(ev.target.value); setCurrentValue(newValue); - state.onChange && state.onChange(ev, { value: newValue }); }; const rootVariables = { @@ -39,8 +64,6 @@ export const useSliderState = (state: SliderState) => { ...state.root.style, }; - // Rail Props - // Input Props state.input.value = currentValue; state.input.min = min; @@ -48,7 +71,7 @@ export const useSliderState = (state: SliderState) => { ariaValueText && (state.input['aria-valuetext'] = ariaValueText(currentValue!)); state.input.disabled = disabled; state.input.step = step; - state.input.onChange = onChange; + state.input.onChange = useMergedCallbacks(onInputChange, input.onChange); return state; }; From 2d620ec024ca1d5a09f2366fb7121a034a7c793c Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Wed, 8 Dec 2021 08:24:02 -0800 Subject: [PATCH 27/53] update api --- packages/react-slider/etc/react-slider.api.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/react-slider/etc/react-slider.api.md b/packages/react-slider/etc/react-slider.api.md index 98d0628e80c7e..960f5d903dab3 100644 --- a/packages/react-slider/etc/react-slider.api.md +++ b/packages/react-slider/etc/react-slider.api.md @@ -30,7 +30,7 @@ export type SliderCommons = { vertical?: boolean; origin?: number; size?: 'small' | 'medium'; - onChange?: (ev: React_2.ChangeEvent, data: SliderOnChangeData) => void; + onChange?: (ev: React_2.ChangeEvent) => void; ariaValueText?: (value: number) => string; }; @@ -60,10 +60,6 @@ export const useSliderState: (state: SliderState) => SliderState; // @public export const useSliderStyles: (state: SliderState) => SliderState; -// Warnings were encountered during analysis: -// -// lib/components/Slider/Slider.types.d.ts:77:5 - (ae-forgotten-export) The symbol "SliderOnChangeData" needs to be exported by the entry point index.d.ts - // (No @packageDocumentation comment for this package) ``` From 49003d254a735e17a165a3164e744bcceeb4062a Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Wed, 8 Dec 2021 12:25:08 -0800 Subject: [PATCH 28/53] move back to onChange with data --- packages/react-slider/etc/react-slider.api.md | 6 ++- .../src/components/Slider/Slider.types.ts | 6 ++- .../stories/SliderControlled.stories.tsx | 4 +- .../src/components/Slider/useSlider.ts | 5 ++- .../src/components/Slider/useSliderState.tsx | 41 ++++++------------- 5 files changed, 27 insertions(+), 35 deletions(-) diff --git a/packages/react-slider/etc/react-slider.api.md b/packages/react-slider/etc/react-slider.api.md index 960f5d903dab3..98d0628e80c7e 100644 --- a/packages/react-slider/etc/react-slider.api.md +++ b/packages/react-slider/etc/react-slider.api.md @@ -30,7 +30,7 @@ export type SliderCommons = { vertical?: boolean; origin?: number; size?: 'small' | 'medium'; - onChange?: (ev: React_2.ChangeEvent) => void; + onChange?: (ev: React_2.ChangeEvent, data: SliderOnChangeData) => void; ariaValueText?: (value: number) => string; }; @@ -60,6 +60,10 @@ export const useSliderState: (state: SliderState) => SliderState; // @public export const useSliderStyles: (state: SliderState) => SliderState; +// Warnings were encountered during analysis: +// +// lib/components/Slider/Slider.types.d.ts:77:5 - (ae-forgotten-export) The symbol "SliderOnChangeData" needs to be exported by the entry point index.d.ts + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/react-slider/src/components/Slider/Slider.types.ts b/packages/react-slider/src/components/Slider/Slider.types.ts index 0849c5021ddee..64b7756677576 100644 --- a/packages/react-slider/src/components/Slider/Slider.types.ts +++ b/packages/react-slider/src/components/Slider/Slider.types.ts @@ -88,7 +88,7 @@ export type SliderCommons = { /** * Triggers a callback when the value has been changed. This will be called on every individual step. */ - onChange?: (ev: React.ChangeEvent) => void; + onChange?: (ev: React.ChangeEvent, data: SliderOnChangeData) => void; /** * The Slider's current value label to be read by the screen reader. @@ -96,6 +96,10 @@ export type SliderCommons = { ariaValueText?: (value: number) => string; }; +interface SliderOnChangeData { + value: number; +} + export type SliderProps = Omit, 'onChange' | 'defaultValue'> & SliderCommons; export type SliderState = ComponentState & SliderCommons; diff --git a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx index 59377ee9a856a..f99d6c710197e 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx @@ -6,12 +6,12 @@ import { Slider, SliderProps } from '../../../index'; export const Controlled = () => { const [sliderValue, setSliderValue] = React.useState(160); - const onSliderChange: SliderProps['onChange'] = ev => setSliderValue(Number(ev.target.value)); + const onSliderChange: SliderProps['onChange'] = (_, data) => setSliderValue(data.value); return ( <> - + diff --git a/packages/react-slider/src/components/Slider/useSlider.ts b/packages/react-slider/src/components/Slider/useSlider.ts index 26a64157f5e77..30667323a3a0c 100644 --- a/packages/react-slider/src/components/Slider/useSlider.ts +++ b/packages/react-slider/src/components/Slider/useSlider.ts @@ -15,7 +15,7 @@ export const useSlider = (props: SliderProps, ref: React.Ref): const nativeProps = getPartitionedNativeProps({ props, primarySlotTagName: 'input', - excludedPropNames: [], + excludedPropNames: ['onChange'], }); const { @@ -30,7 +30,7 @@ export const useSlider = (props: SliderProps, ref: React.Ref): vertical, size = 'medium', origin, - + onChange, // Slots root, input, @@ -49,6 +49,7 @@ export const useSlider = (props: SliderProps, ref: React.Ref): step, vertical, value, + onChange, components: { input: 'input', rail: 'div', diff --git a/packages/react-slider/src/components/Slider/useSliderState.tsx b/packages/react-slider/src/components/Slider/useSliderState.tsx index 165cd51a6aca3..55706214345d1 100644 --- a/packages/react-slider/src/components/Slider/useSliderState.tsx +++ b/packages/react-slider/src/components/Slider/useSliderState.tsx @@ -3,34 +3,8 @@ import { clamp, useControllableState } from '@fluentui/react-utilities'; import { getPercent } from '../../utils/index'; import type { SliderState } from './Slider.types'; -/** - * Combine up to two event callbacks into a single function that calls them in order - */ -const useMergedCallbacks = ( - callback1: ((ev: Event) => void) | undefined, - callback2: ((ev: Event) => void) | undefined, -) => { - return React.useCallback( - (ev: Event) => { - callback1?.(ev); - callback2?.(ev); - }, - [callback1, callback2], - ); -}; - export const useSliderState = (state: SliderState) => { - const { - value, - defaultValue = 0, - min = 0, - max = 100, - step = 1, - disabled = false, - ariaValueText, - origin, - input, - } = state; + const { value, defaultValue = 0, min = 0, max = 100, step = 1, disabled = false, ariaValueText, origin } = state; const [currentValue, setCurrentValue] = useControllableState({ state: value && clamp(value, min, max), @@ -43,9 +17,18 @@ export const useSliderState = (state: SliderState) => { return origin !== undefined ? getPercent(origin, min, max) : 0; }, [max, min, origin]); - const onInputChange: React.ChangeEventHandler = ev => { + const onChange: React.ChangeEventHandler = ev => { const newValue = Number(ev.target.value); setCurrentValue(newValue); + + const inputOnChange = state.input.onChange; + const propsOnChange = state.onChange; + + if (inputOnChange && inputOnChange !== propsOnChange) { + inputOnChange(ev); + } else if (propsOnChange) { + propsOnChange(ev, { value: newValue }); + } }; const rootVariables = { @@ -71,7 +54,7 @@ export const useSliderState = (state: SliderState) => { ariaValueText && (state.input['aria-valuetext'] = ariaValueText(currentValue!)); state.input.disabled = disabled; state.input.step = step; - state.input.onChange = useMergedCallbacks(onInputChange, input.onChange); + state.input.onChange = onChange; return state; }; From 1c9db3cb6905c5f685a925e37e3222d24bcd38c9 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Wed, 8 Dec 2021 12:48:42 -0800 Subject: [PATCH 29/53] remove unused stuff --- .../stories/RangedSliderConverged.stories.tsx | 185 ------------------ .../react-slider/src/utils/calculateSteps.ts | 34 ---- .../src/utils/findClosestThumb.ts | 8 - .../react-slider/src/utils/getKeydownValue.ts | 38 ---- packages/react-slider/src/utils/index.ts | 4 - packages/react-slider/src/utils/on.ts | 19 -- 6 files changed, 288 deletions(-) delete mode 100644 apps/vr-tests/src/stories/RangedSliderConverged.stories.tsx delete mode 100644 packages/react-slider/src/utils/calculateSteps.ts delete mode 100644 packages/react-slider/src/utils/findClosestThumb.ts delete mode 100644 packages/react-slider/src/utils/getKeydownValue.ts delete mode 100644 packages/react-slider/src/utils/on.ts diff --git a/apps/vr-tests/src/stories/RangedSliderConverged.stories.tsx b/apps/vr-tests/src/stories/RangedSliderConverged.stories.tsx deleted file mode 100644 index a5d9271d38d4d..0000000000000 --- a/apps/vr-tests/src/stories/RangedSliderConverged.stories.tsx +++ /dev/null @@ -1,185 +0,0 @@ -import * as React from 'react'; -import Screener, { Steps } from 'screener-storybook/src/screener'; -import { storiesOf } from '@storybook/react'; -import { RangedSlider } from '@fluentui/react-slider'; - -const LabelComponent = () =>
; - -const MarkComponent = () => ( -
-); - -storiesOf('RangedSlider Converged', module) - .addDecorator(story => ( - - {story()} - - )) - .addStory('Root', () => , { - includeRtl: true, - includeHighContrast: true, - includeDarkMode: true, - }) - .addStory('Vertical', () => , { - includeRtl: true, - }) - .addStory( - 'Disabled', - () => , - { includeHighContrast: true, includeDarkMode: true }, - ) - .addStory('Disabled Vertical', () => ( - - )) - .addStory( - 'Marks', - () => , - { - includeRtl: true, - includeHighContrast: true, - includeDarkMode: true, - }, - ) - .addStory( - 'Marks Vertical', - () => , - { - includeRtl: true, - }, - ) - .addStory( - 'Marks Custom', - () => ( - - ), - { - includeRtl: true, - }, - ) - .addStory( - 'Marks Custom Vertical', - () => ( - - ), - { - includeRtl: true, - }, - ) - .addStory( - 'Marks Custom Label Value', - () => ( - , - }, - { value: 4, label: 'world' }, - 8, - ]} - /> - ), - { - includeRtl: true, - }, - ) - .addStory( - 'Marks Custom Label Vertical', - () => ( - , - }, - { value: 4, label: 'world' }, - 8, - ]} - /> - ), - { - includeRtl: true, - }, - ) - .addStory( - 'Custom Marks', - () => ( - , - mark: , - }, - { value: 4, label: 'world' }, - 8, - ]} - /> - ), - { - includeRtl: true, - }, - ) - .addStory( - 'Custom Marks Vertical', - () => ( - , - mark: , - }, - { value: 4, label: 'world' }, - 8, - ]} - /> - ), - { - includeRtl: true, - }, - ); diff --git a/packages/react-slider/src/utils/calculateSteps.ts b/packages/react-slider/src/utils/calculateSteps.ts deleted file mode 100644 index ae93b5feda8d6..0000000000000 --- a/packages/react-slider/src/utils/calculateSteps.ts +++ /dev/null @@ -1,34 +0,0 @@ -import * as React from 'react'; -import { clamp } from '@fluentui/react-utilities'; - -/** - * Calculates the `step` position based off of a `Mouse` or `Touch` event relative to the size of the rail. - */ -export const calculateSteps = ( - ev: React.PointerEvent, - railRef: React.RefObject, - min: number, - max: number, - step: number, - vertical: boolean, - dir: 'ltr' | 'rtl', -): number => { - const currentBounds = railRef?.current?.getBoundingClientRect(); - const sliderSize = (vertical ? currentBounds!.height : currentBounds!.width) || 0; - let position; - - if (vertical) { - position = currentBounds!.bottom; - } else if (dir === 'rtl') { - position = currentBounds!.right; - } else { - position = currentBounds!.left; - } - - const totalSteps = (max - min) / step; - const stepLength = sliderSize / totalSteps; - const thumbPosition = vertical ? ev.clientY : ev.clientX; - const distance = dir === 'rtl' || vertical ? position - thumbPosition : thumbPosition - position; - - return clamp(min + step * (distance / stepLength), min, max); -}; diff --git a/packages/react-slider/src/utils/findClosestThumb.ts b/packages/react-slider/src/utils/findClosestThumb.ts deleted file mode 100644 index 94942d069bc3e..0000000000000 --- a/packages/react-slider/src/utils/findClosestThumb.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Finds the closest thumb based of a given value and returns it's key. - */ -export const findClosestThumb = (thumbArray: [number, number], incomingValue: number) => { - return Math.abs(incomingValue - thumbArray[0]) <= Math.abs(thumbArray[1] - incomingValue) - ? 'lowerValue' - : 'upperValue'; -}; diff --git a/packages/react-slider/src/utils/getKeydownValue.ts b/packages/react-slider/src/utils/getKeydownValue.ts deleted file mode 100644 index 10e4a7490f0c6..0000000000000 --- a/packages/react-slider/src/utils/getKeydownValue.ts +++ /dev/null @@ -1,38 +0,0 @@ -import * as React from 'react'; -import { getRTLSafeKey } from '@fluentui/react-utilities'; - -/** - * Determines the incoming value for the Slider based off of a keyboard event. - * It automatically flips the key direction if the dir parameter is rtl. - */ -export const getKeydownValue = ( - ev: React.KeyboardEvent, - currentValue: number, - min: number, - max: number, - dir: 'ltr' | 'rtl', - keyboardStep: number, -): number => { - const normalizedKey = getRTLSafeKey(ev.key, dir); - - const arrowStep = ev.shiftKey ? keyboardStep * 10 : keyboardStep; - - switch (normalizedKey) { - case 'ArrowDown': - case 'ArrowLeft': - return currentValue - arrowStep; - case 'ArrowUp': - case 'ArrowRight': - return currentValue + arrowStep; - case 'PageDown': - return currentValue - keyboardStep * 10; - case 'PageUp': - return currentValue + keyboardStep * 10; - case 'Home': - return min; - case 'End': - return max; - } - - return currentValue; -}; diff --git a/packages/react-slider/src/utils/index.ts b/packages/react-slider/src/utils/index.ts index 40662173c57ef..dfbbf04ab242d 100644 --- a/packages/react-slider/src/utils/index.ts +++ b/packages/react-slider/src/utils/index.ts @@ -1,5 +1 @@ -export * from './calculateSteps'; -export * from './findClosestThumb'; -export * from './getKeydownValue'; export * from './getPercent'; -export * from './on'; diff --git a/packages/react-slider/src/utils/on.ts b/packages/react-slider/src/utils/on.ts deleted file mode 100644 index ea7c030fff808..0000000000000 --- a/packages/react-slider/src/utils/on.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Adds and removes a given event listener on an element. - * - * @param element the element to listen to. - * @param eventName the event to watch. - * @param callback the callback to call when the eventName is triggered. - * @param useCapture whether the events should be dispatched to the callback first before other EventTargets. - * - * This should be replaced with a `useEvent` hook. - */ -export const on = ( - element: Element | Window | Document, - eventName: string, - callback: (ev: T) => void, - useCapture?: boolean, -) => { - element.addEventListener(eventName, (callback as unknown) as (ev: Event) => void, useCapture); - return () => element.removeEventListener(eventName, (callback as unknown) as (ev: Event) => void, useCapture); -}; From f2a5f749ab23037e94681cff33783147fe5f5941 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Fri, 10 Dec 2021 09:15:57 -0800 Subject: [PATCH 30/53] Update packages/react-slider/src/components/Slider/Slider.types.ts Co-authored-by: Ben Howell <48106640+behowell@users.noreply.github.com> --- packages/react-slider/src/components/Slider/Slider.types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-slider/src/components/Slider/Slider.types.ts b/packages/react-slider/src/components/Slider/Slider.types.ts index 64b7756677576..a09b42386fadd 100644 --- a/packages/react-slider/src/components/Slider/Slider.types.ts +++ b/packages/react-slider/src/components/Slider/Slider.types.ts @@ -96,7 +96,7 @@ export type SliderCommons = { ariaValueText?: (value: number) => string; }; -interface SliderOnChangeData { +export type SliderOnChangeData = { value: number; } From 1295f392189471a2e08b9ba84a2437c8ae6c0e63 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Fri, 10 Dec 2021 10:39:53 -0800 Subject: [PATCH 31/53] Update packages/react-slider/src/components/Slider/Slider.types.ts Co-authored-by: Elizabeth Craig --- packages/react-slider/src/components/Slider/Slider.types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-slider/src/components/Slider/Slider.types.ts b/packages/react-slider/src/components/Slider/Slider.types.ts index a09b42386fadd..9ba1517da728d 100644 --- a/packages/react-slider/src/components/Slider/Slider.types.ts +++ b/packages/react-slider/src/components/Slider/Slider.types.ts @@ -5,7 +5,7 @@ export type SliderSlots = { /** * The root of the Slider. * The root slot receives the `className` and `style` specified directly on the ``. - * All other native props will be applied to the primary slot: `input` + * All other native props will be applied to the primary slot, `input`. */ root: IntrinsicShorthandProps<'div'>; From 9c4b7d68f21a39fea14d97d545c0130857f96ae7 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Fri, 10 Dec 2021 13:19:19 -0800 Subject: [PATCH 32/53] refactor css variables --- .../src/components/Slider/Slider.stories.tsx | 1 + .../src/components/Slider/Slider.test.tsx | 440 +++++++++--------- .../src/components/Slider/Slider.types.ts | 7 +- .../stories/SliderControlled.stories.tsx | 2 - .../Slider/stories/SliderDefault.stories.tsx | 2 - .../Slider/stories/SliderDisabled.stories.tsx | 2 - .../Slider/stories/SliderOrigin.stories.tsx | 4 +- .../Slider/stories/SliderSmall.stories.tsx | 10 + .../Slider/stories/SliderStep.stories.tsx | 2 - .../Slider/stories/SliderVertical.stories.tsx | 2 - .../src/components/Slider/useSlider.ts | 8 +- .../src/components/Slider/useSliderState.tsx | 29 +- .../src/components/Slider/useSliderStyles.ts | 117 +++-- 13 files changed, 307 insertions(+), 319 deletions(-) create mode 100644 packages/react-slider/src/components/Slider/stories/SliderSmall.stories.tsx diff --git a/packages/react-slider/src/components/Slider/Slider.stories.tsx b/packages/react-slider/src/components/Slider/Slider.stories.tsx index 7852ed6c82b2c..2566be948df25 100644 --- a/packages/react-slider/src/components/Slider/Slider.stories.tsx +++ b/packages/react-slider/src/components/Slider/Slider.stories.tsx @@ -3,6 +3,7 @@ import { Slider } from '../../index'; import type { Meta } from '@storybook/react'; export * from './stories/SliderDefault.stories'; +export * from './stories/SliderSmall.stories'; export * from './stories/SliderControlled.stories'; export * from './stories/SliderStep.stories'; export * from './stories/SliderOrigin.stories'; diff --git a/packages/react-slider/src/components/Slider/Slider.test.tsx b/packages/react-slider/src/components/Slider/Slider.test.tsx index e3f6ca1bd69b1..9385317b0ac75 100644 --- a/packages/react-slider/src/components/Slider/Slider.test.tsx +++ b/packages/react-slider/src/components/Slider/Slider.test.tsx @@ -19,240 +19,234 @@ describe('Slider', () => { resetIdsForTests(); }); - describe('Snapshot Tests', () => { - it('renders horizontal Slider correctly', () => { - const { container } = render(); - expect(container).toMatchSnapshot(); - }); - - it('renders vertical Slider correctly', () => { - const { container } = render(); - expect(container).toMatchSnapshot(); - }); - - it('renders disabled Slider correctly', () => { - const { container } = render(); - expect(container).toMatchSnapshot(); - }); - - it('renders horizontal origin Slider correctly', () => { - const { container } = render(); - expect(container).toMatchSnapshot(); - }); - - it('renders vertical origin Slider correctly', () => { - const { container } = render(); - expect(container).toMatchSnapshot(); - }); + // Snapshot tests + it('renders horizontal Slider correctly', () => { + const { container } = render(); + expect(container).toMatchSnapshot(); }); - describe('Unit Tests', () => { - it('handles id prop', () => { - render(); - const sliderRoot = screen.getByTestId('test'); - expect(sliderRoot.getAttribute('id')).toEqual('test_id'); - }); - - it.skip('calls onChange when pointerDown', () => { - const onChange = jest.fn(); - - const { getByTestId } = render( - , - ); - - expect(onChange).toHaveBeenCalledTimes(0); - fireEvent.pointerDown(getByTestId('test'), { clientX: 0, clientY: 0 }); - expect(onChange).toHaveBeenCalledTimes(1); - expect(onChange.mock.calls[0][1]).toEqual({ value: 0 }); - }); - - it('applies the defaultValue prop', () => { - const inputRef = React.createRef(); - render(); - expect(inputRef.current?.value).toEqual('10'); - }); - - it('applies the value prop', () => { - const inputRef = React.createRef(); - render(); - expect(inputRef.current?.value).toEqual('10'); - }); - - it('applies the disabled prop', () => { - const inputRef = React.createRef(); - render(); - expect(inputRef.current?.disabled).toEqual(true); - }); - - it('applies the min prop', () => { - const inputRef = React.createRef(); - render(); - expect(inputRef.current?.min).toEqual('11'); - }); - - it('applies the max prop', () => { - const inputRef = React.createRef(); - render(); - expect(inputRef.current?.max).toEqual('11'); - }); - - it('applies the step prop', () => { - const inputRef = React.createRef(); - render(); - expect(inputRef.current?.step).toEqual('11'); - }); - - it('clamps an initial defaultValue that is out of bounds', () => { - const inputRef = React.createRef(); - render(); - expect(inputRef.current?.value).toEqual('0'); - }); - - it.skip('handles keydown events', () => { - const inputRef = React.createRef(); - const onChange = jest.fn(); - - render( - , - ); - - const sliderInput = screen.getByTestId('test'); - expect(onChange).toBeCalledTimes(0); - - fireEvent.keyDown(sliderInput, { key: ArrowDown }); - expect(onChange.mock.calls[0][1]).toEqual({ value: 49 }); - expect(inputRef.current?.value).toEqual('49'); - - fireEvent.keyDown(sliderInput, { key: ArrowUp }); - expect(onChange.mock.calls[1][1]).toEqual({ value: 50 }); - expect(inputRef.current?.value).toEqual('50'); - - fireEvent.keyDown(sliderInput, { key: ArrowLeft }); - expect(onChange.mock.calls[2][1]).toEqual({ value: 49 }); - expect(inputRef.current?.value).toEqual('49'); - - fireEvent.keyDown(sliderInput, { key: ArrowRight }); - expect(onChange.mock.calls[3][1]).toEqual({ value: 50 }); - expect(inputRef.current?.value).toEqual('50'); - - fireEvent.keyDown(sliderInput, { key: PageUp }); - expect(onChange.mock.calls[4][1]).toEqual({ value: 60 }); - expect(inputRef.current?.value).toEqual('60'); - - fireEvent.keyDown(sliderInput, { key: PageDown }); - expect(onChange.mock.calls[5][1]).toEqual({ value: 50 }); - expect(inputRef.current?.value).toEqual('50'); - - fireEvent.keyDown(sliderInput, { key: Home }); - expect(onChange.mock.calls[6][1]).toEqual({ value: 0 }); - expect(inputRef.current?.value).toEqual('0'); - - fireEvent.keyDown(sliderInput, { key: End }); - expect(onChange.mock.calls[7][1]).toEqual({ value: 100 }); - expect(inputRef.current?.value).toEqual('100'); - - fireEvent.keyDown(sliderInput, { key: ArrowLeft, shiftKey: true }); - expect(onChange.mock.calls[8][1]).toEqual({ value: 90 }); - expect(inputRef.current?.value).toEqual('90'); - - fireEvent.keyDown(sliderInput, { key: ArrowRight, shiftKey: true }); - expect(onChange.mock.calls[9][1]).toEqual({ value: 100 }); - expect(inputRef.current?.value).toEqual('100'); - - expect(onChange).toBeCalledTimes(10); - }); - - it('does not update when the controlled value prop is provided', () => { - const inputRef = React.createRef(); - render(); - const sliderRoot = screen.getByTestId('test'); - - fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); - expect(inputRef.current?.value).toBe('50'); - }); - - it.skip('calls onChange with the correct value', () => { - const onChange = jest.fn(); - - render(); - - const sliderRoot = screen.getByTestId('test'); - - fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); - fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); - fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); - - expect(onChange.mock.calls[2][1]).toEqual({ value: 51 }); - }); - - it('applies focus to the hidden input', () => { - const inputRef = React.createRef(); - render(); - inputRef?.current?.focus(); - expect(document.activeElement).toEqual(inputRef.current); - }); + it('renders vertical Slider correctly', () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it('renders disabled Slider correctly', () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it('renders horizontal origin Slider correctly', () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it('renders vertical origin Slider correctly', () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + // Unit tests + it('handles id prop', () => { + render(); + const sliderRoot = screen.getByTestId('test'); + expect(sliderRoot.getAttribute('id')).toEqual('test_id'); + }); + + it.skip('calls onChange when pointerDown', () => { + const onChange = jest.fn(); + + const { getByTestId } = render( + , + ); + + expect(onChange).toHaveBeenCalledTimes(0); + fireEvent.pointerDown(getByTestId('test'), { clientX: 0, clientY: 0 }); + expect(onChange).toHaveBeenCalledTimes(1); + expect(onChange.mock.calls[0][1]).toEqual({ value: 0 }); + }); + + it('applies the defaultValue prop', () => { + const inputRef = React.createRef(); + render(); + expect(inputRef.current?.value).toEqual('10'); + }); + + it('applies the value prop', () => { + const inputRef = React.createRef(); + render(); + expect(inputRef.current?.value).toEqual('10'); + }); + + it('applies the disabled prop', () => { + const inputRef = React.createRef(); + render(); + expect(inputRef.current?.disabled).toEqual(true); + }); + + it('applies the min prop', () => { + const inputRef = React.createRef(); + render(); + expect(inputRef.current?.min).toEqual('11'); + }); + + it('applies the max prop', () => { + const inputRef = React.createRef(); + render(); + expect(inputRef.current?.max).toEqual('11'); + }); + + it('applies the step prop', () => { + const inputRef = React.createRef(); + render(); + expect(inputRef.current?.step).toEqual('11'); + }); + + it('clamps an initial defaultValue that is out of bounds', () => { + const inputRef = React.createRef(); + render(); + expect(inputRef.current?.value).toEqual('0'); + }); + + it.skip('handles keydown events', () => { + const inputRef = React.createRef(); + const onChange = jest.fn(); + + render( + , + ); + + const sliderInput = screen.getByTestId('test'); + expect(onChange).toBeCalledTimes(0); + + fireEvent.keyDown(sliderInput, { key: ArrowDown }); + expect(onChange.mock.calls[0][1]).toEqual({ value: 49 }); + expect(inputRef.current?.value).toEqual('49'); - it('does not allow focus on disabled Slider', () => { - const sliderRef = React.createRef(); - const inputRef = React.createRef(); + fireEvent.keyDown(sliderInput, { key: ArrowUp }); + expect(onChange.mock.calls[1][1]).toEqual({ value: 50 }); + expect(inputRef.current?.value).toEqual('50'); - render(); + fireEvent.keyDown(sliderInput, { key: ArrowLeft }); + expect(onChange.mock.calls[2][1]).toEqual({ value: 49 }); + expect(inputRef.current?.value).toEqual('49'); - expect(document.activeElement).toEqual(document.body); - sliderRef?.current?.focus(); - expect(document.activeElement).toEqual(document.body); - inputRef?.current?.focus(); - expect(document.activeElement).toEqual(document.body); - }); + fireEvent.keyDown(sliderInput, { key: ArrowRight }); + expect(onChange.mock.calls[3][1]).toEqual({ value: 50 }); + expect(inputRef.current?.value).toEqual('50'); - it('does not allow change on disabled Slider', () => { - const eventHandler = jest.fn(); + fireEvent.keyDown(sliderInput, { key: PageUp }); + expect(onChange.mock.calls[4][1]).toEqual({ value: 60 }); + expect(inputRef.current?.value).toEqual('60'); - render(); + fireEvent.keyDown(sliderInput, { key: PageDown }); + expect(onChange.mock.calls[5][1]).toEqual({ value: 50 }); + expect(inputRef.current?.value).toEqual('50'); - const sliderRoot = screen.getByTestId('test'); + fireEvent.keyDown(sliderInput, { key: Home }); + expect(onChange.mock.calls[6][1]).toEqual({ value: 0 }); + expect(inputRef.current?.value).toEqual('0'); - expect(eventHandler).toBeCalledTimes(0); + fireEvent.keyDown(sliderInput, { key: End }); + expect(onChange.mock.calls[7][1]).toEqual({ value: 100 }); + expect(inputRef.current?.value).toEqual('100'); - fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); - expect(eventHandler).toBeCalledTimes(0); - }); + fireEvent.keyDown(sliderInput, { key: ArrowLeft, shiftKey: true }); + expect(onChange.mock.calls[8][1]).toEqual({ value: 90 }); + expect(inputRef.current?.value).toEqual('90'); + + fireEvent.keyDown(sliderInput, { key: ArrowRight, shiftKey: true }); + expect(onChange.mock.calls[9][1]).toEqual({ value: 100 }); + expect(inputRef.current?.value).toEqual('100'); + + expect(onChange).toBeCalledTimes(10); + }); + + it('does not update when the controlled value prop is provided', () => { + const inputRef = React.createRef(); + render(); + const sliderRoot = screen.getByTestId('test'); + + fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); + expect(inputRef.current?.value).toBe('50'); + }); + + it.skip('calls onChange with the correct value', () => { + const onChange = jest.fn(); + + render(); + + const sliderRoot = screen.getByTestId('test'); + + fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); + fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); + fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); + + expect(onChange.mock.calls[2][1]).toEqual({ value: 51 }); + }); + + it('applies focus to the hidden input', () => { + const inputRef = React.createRef(); + render(); + inputRef?.current?.focus(); + expect(document.activeElement).toEqual(inputRef.current); }); - describe('Accessibility Tests', () => { - it('handles role prop', () => { - render(); - const sliderRoot = screen.getByTestId('test'); - expect(sliderRoot.getAttribute('role')).toEqual('test'); - }); - - it('renders the input slot as input', () => { - const { container } = render(); - const inputElement = container.querySelector('.test'); - expect(inputElement?.tagName).toEqual('INPUT'); - }); - - it('provides the input slot with a type of range', () => { - const { container } = render(); - const inputElement = container.querySelector('.test'); - expect(inputElement?.getAttribute('type')).toEqual('range'); - }); - - it('applies ariaValueText', () => { - const values = ['small', 'medium', 'large']; - const defaultValue = 1; - const getTextValue = (value: number) => values[value]; - - render(); - const sliderInput = screen.getByRole('slider'); - - expect(sliderInput.getAttribute('aria-valuetext')).toEqual(values[defaultValue]); - }); + it('does not allow focus on disabled Slider', () => { + const sliderRef = React.createRef(); + + render(); + + expect(document.activeElement).toEqual(document.body); + sliderRef?.current?.focus(); + expect(document.activeElement).toEqual(document.body); + }); + + it('does not allow change on disabled Slider', () => { + const eventHandler = jest.fn(); + + render(); + + const sliderRoot = screen.getByTestId('test'); + + expect(eventHandler).toBeCalledTimes(0); + + fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); + expect(eventHandler).toBeCalledTimes(0); + }); + + // Accessibility tests + it('handles role prop', () => { + render(); + const sliderRoot = screen.getByTestId('test'); + expect(sliderRoot.getAttribute('role')).toEqual('test'); + }); + + it('renders the input slot as input', () => { + const { container } = render(); + const inputElement = container.querySelector('.test'); + expect(inputElement?.tagName).toEqual('INPUT'); + }); + + it('provides the input slot with a type of range', () => { + const { container } = render(); + const inputElement = container.querySelector('.test'); + expect(inputElement?.getAttribute('type')).toEqual('range'); + }); + + it('applies ariaValueText', () => { + const values = ['small', 'medium', 'large']; + const defaultValue = 1; + const getTextValue = (value: number) => values[value]; + + render(); + const sliderInput = screen.getByRole('slider'); + + expect(sliderInput.getAttribute('aria-valuetext')).toEqual(values[defaultValue]); }); }); diff --git a/packages/react-slider/src/components/Slider/Slider.types.ts b/packages/react-slider/src/components/Slider/Slider.types.ts index a09b42386fadd..336291e4c3ec7 100644 --- a/packages/react-slider/src/components/Slider/Slider.types.ts +++ b/packages/react-slider/src/components/Slider/Slider.types.ts @@ -93,13 +93,14 @@ export type SliderCommons = { /** * The Slider's current value label to be read by the screen reader. */ - ariaValueText?: (value: number) => string; + getAriaValueText?: (value: number) => string; }; export type SliderOnChangeData = { value: number; -} +}; -export type SliderProps = Omit, 'onChange' | 'defaultValue'> & SliderCommons; +export type SliderProps = Omit, 'onChange' | 'defaultValue' | 'size' | 'value'> & + SliderCommons; export type SliderState = ComponentState & SliderCommons; diff --git a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx index f99d6c710197e..dbc85013888b8 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx @@ -1,6 +1,4 @@ import * as React from 'react'; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore import { Label } from '@fluentui/react-label'; import { Slider, SliderProps } from '../../../index'; diff --git a/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx index 372298d2e6b4d..22dedd1fcad7e 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx @@ -1,6 +1,4 @@ import * as React from 'react'; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore import { Label } from '@fluentui/react-label'; import { Slider } from '../../../index'; diff --git a/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx index eae43f719efd5..60c88f43cc226 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx @@ -1,6 +1,4 @@ import * as React from 'react'; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// ts-ignore import { Label } from '@fluentui/react-label'; import { Slider } from '../../../index'; diff --git a/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx index 786fb6f1e0ab1..87bcf2c81ab93 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx @@ -1,12 +1,10 @@ import * as React from 'react'; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// ts-ignore import { Label } from '@fluentui/react-label'; import { Slider } from '../../../index'; export const Origin = () => ( <> - + ); diff --git a/packages/react-slider/src/components/Slider/stories/SliderSmall.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderSmall.stories.tsx new file mode 100644 index 0000000000000..447be179eb2a9 --- /dev/null +++ b/packages/react-slider/src/components/Slider/stories/SliderSmall.stories.tsx @@ -0,0 +1,10 @@ +import * as React from 'react'; +import { Label } from '@fluentui/react-label'; +import { Slider } from '../../../index'; + +export const Small = () => ( + <> + + + +); diff --git a/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx index 6d13047eb7d86..1bf23d724ece1 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx @@ -1,6 +1,4 @@ import * as React from 'react'; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// ts-ignore import { Label } from '@fluentui/react-label'; import { Slider } from '../../../index'; diff --git a/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx index 9dbb13207a680..0b0fb198ca63e 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx @@ -1,6 +1,4 @@ import * as React from 'react'; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// ts-ignore import { Label } from '@fluentui/react-label'; import { Slider } from '../../../index'; diff --git a/packages/react-slider/src/components/Slider/useSlider.ts b/packages/react-slider/src/components/Slider/useSlider.ts index 30667323a3a0c..b77bae0a2f661 100644 --- a/packages/react-slider/src/components/Slider/useSlider.ts +++ b/packages/react-slider/src/components/Slider/useSlider.ts @@ -22,11 +22,11 @@ export const useSlider = (props: SliderProps, ref: React.Ref): // Props value, defaultValue, - min, - max, + min = 0, + max = 100, step, disabled, - ariaValueText, + getAriaValueText, vertical, size = 'medium', origin, @@ -39,7 +39,7 @@ export const useSlider = (props: SliderProps, ref: React.Ref): } = props; const state: SliderState = { - ariaValueText, + getAriaValueText, defaultValue, disabled, max, diff --git a/packages/react-slider/src/components/Slider/useSliderState.tsx b/packages/react-slider/src/components/Slider/useSliderState.tsx index 55706214345d1..ad457b72a70af 100644 --- a/packages/react-slider/src/components/Slider/useSliderState.tsx +++ b/packages/react-slider/src/components/Slider/useSliderState.tsx @@ -1,10 +1,11 @@ import * as React from 'react'; -import { clamp, useControllableState } from '@fluentui/react-utilities'; +import { clamp, useControllableState, useEventCallback } from '@fluentui/react-utilities'; import { getPercent } from '../../utils/index'; +import { railOffsetVar, railStepsPercentVar, railProgressVar, thumbPositionVar } from './useSliderStyles'; import type { SliderState } from './Slider.types'; export const useSliderState = (state: SliderState) => { - const { value, defaultValue = 0, min = 0, max = 100, step = 1, disabled = false, ariaValueText, origin } = state; + const { value, defaultValue = 0, min = 0, max = 100, step = 1, getAriaValueText, origin } = state; const [currentValue, setCurrentValue] = useControllableState({ state: value && clamp(value, min, max), @@ -17,25 +18,25 @@ export const useSliderState = (state: SliderState) => { return origin !== undefined ? getPercent(origin, min, max) : 0; }, [max, min, origin]); - const onChange: React.ChangeEventHandler = ev => { + const inputOnChange = state.input.onChange; + const propsOnChange = state.onChange; + + const onChange: React.ChangeEventHandler = useEventCallback(ev => { const newValue = Number(ev.target.value); setCurrentValue(newValue); - const inputOnChange = state.input.onChange; - const propsOnChange = state.onChange; - if (inputOnChange && inputOnChange !== propsOnChange) { inputOnChange(ev); } else if (propsOnChange) { propsOnChange(ev, { value: newValue }); } - }; + }); const rootVariables = { - '--slider-thumb-position': valuePercent + '%', - '--slider-rail-steps-percent': state.step && state.step > 0 ? `${(step * 100) / (max - min)}%` : '', - '--slider-rail-offset': origin !== undefined ? `${Math.min(valuePercent, originPercent)}%` : '0%', - '--slider-rail-progress': + [thumbPositionVar]: valuePercent + '%', + [railStepsPercentVar]: state.step && state.step > 0 ? `${(step * 100) / (max - min)}%` : '', + [railOffsetVar]: origin !== undefined ? `${Math.min(valuePercent, originPercent)}%` : '0%', + [railProgressVar]: origin !== undefined ? `${Math.max(originPercent - valuePercent, valuePercent - originPercent)}%` : `${valuePercent}%`, @@ -49,11 +50,7 @@ export const useSliderState = (state: SliderState) => { // Input Props state.input.value = currentValue; - state.input.min = min; - state.input.max = max; - ariaValueText && (state.input['aria-valuetext'] = ariaValueText(currentValue!)); - state.input.disabled = disabled; - state.input.step = step; + getAriaValueText && (state.input['aria-valuetext'] = getAriaValueText(currentValue!)); state.input.onChange = onChange; return state; diff --git a/packages/react-slider/src/components/Slider/useSliderStyles.ts b/packages/react-slider/src/components/Slider/useSliderStyles.ts index 4426732def9da..22277a7adf2d5 100644 --- a/packages/react-slider/src/components/Slider/useSliderStyles.ts +++ b/packages/react-slider/src/components/Slider/useSliderStyles.ts @@ -4,8 +4,19 @@ import type { SliderState } from './Slider.types'; export const sliderClassName = 'fui-Slider'; -const thumbClassName = `${sliderClassName}-thumb`; -const railClassName = `${sliderClassName}-rail`; +const thumbSizeVar = `--${sliderClassName}-thumb-size`; +const railSizeVar = `--${sliderClassName}-rail-size`; +const railDirectionVar = `--${sliderClassName}-rail-direction`; +const railColorVar = `--${sliderClassName}-rail-color`; +const progressColorVar = `--${sliderClassName}-progress-color`; +const thumbColorVar = `--${sliderClassName}-thumb-color`; + +export const railOffsetVar = `--${sliderClassName}-rail-offset`; +export const railProgressVar = `--${sliderClassName}-rail-progress`; +export const railStepsPercentVar = `--${sliderClassName}-rail-steps-percent`; +export const thumbPositionVar = `--${sliderClassName}-thumb-position`; + +const get = (value: string) => `var(${value})`; /** * Styles for the root slot @@ -20,48 +31,48 @@ export const useRootStyles = makeStyles({ }, small: { - '--slider-thumb-size': '10px', - '--slider-rail-size': '2px', + [thumbSizeVar]: '16px', + [railSizeVar]: '2px', }, medium: { - '--slider-thumb-size': '20px', - '--slider-rail-size': '4px', + [thumbSizeVar]: '20px', + [railSizeVar]: '4px', }, horizontal: { + [railDirectionVar]: '90deg', minWidth: '120px', - height: 'var(--slider-thumb-size)', + height: get(thumbSizeVar), alignItems: 'center', }, vertical: { - width: 'var(--slider-thumb-size)', + [railDirectionVar]: '0deg', + width: get(thumbSizeVar), minHeight: '120px', justifyItems: 'center', - gridTemplateColumns: 'var(--slider-thumb-size)', + gridTemplateColumns: get(thumbSizeVar), }, enabled: theme => ({ - '--slider-rail-color': theme.colorNeutralStrokeAccessible, - '--slider-progress-color': theme.colorCompoundBrandBackground, + [railColorVar]: theme.colorNeutralStrokeAccessible, + [progressColorVar]: theme.colorCompoundBrandBackground, + [thumbColorVar]: theme.colorCompoundBrandBackground, ':hover': { - [`& .${thumbClassName}`]: { - background: theme.colorBrandBackgroundHover, - }, - '--slider-progress-color': theme.colorBrandBackgroundHover, + [thumbColorVar]: theme.colorBrandBackgroundHover, + [progressColorVar]: theme.colorBrandBackgroundHover, }, ':active': { - [`& .${thumbClassName}`]: { - background: theme.colorBrandBackgroundPressed, - }, - '--slider-progress-color': theme.colorBrandBackgroundPressed, + [thumbColorVar]: theme.colorBrandBackgroundPressed, + [progressColorVar]: theme.colorBrandBackgroundPressed, }, }), disabled: theme => ({ - '--slider-rail-color': theme.colorNeutralBackgroundDisabled, - '--slider-progress-color': theme.colorNeutralForegroundDisabled, + [thumbColorVar]: theme.colorNeutralForegroundDisabled, + [railColorVar]: theme.colorNeutralBackgroundDisabled, + [progressColorVar]: theme.colorNeutralForegroundDisabled, }), focusIndicatorHorizontal: theme => @@ -86,46 +97,40 @@ export const useRailStyles = makeStyles({ gridArea: 'slider', position: 'relative', background: `linear-gradient( - var(--slider-rail-direction), - var(--slider-rail-color) 0%, - var(--slider-rail-color) var(--slider-rail-offset), - var(--slider-progress-color) var(--slider-rail-offset), - var(--slider-progress-color) calc(var(--slider-rail-offset) + var(--slider-rail-progress)), - var(--slider-rail-color) calc(var(--slider-rail-offset) + var(--slider-rail-progress)) + var(${railDirectionVar}), + var(${railColorVar}) 0%, + var(${railColorVar}) var(${railOffsetVar}), + var(${progressColorVar}) var(${railOffsetVar}), + var(${progressColorVar}) calc(var(${railOffsetVar}) + var(${railProgressVar})), + var(${railColorVar}) calc(var(${railOffsetVar}) + var(${railProgressVar})) )`, ':before': { content: "''", position: 'absolute', background: `repeating-linear-gradient( - var(--slider-rail-direction), + var(${railDirectionVar}), #0000 0%, - #0000 calc(var(--slider-rail-steps-percent) - 1px), - #fff calc(var(--slider-rail-steps-percent) - 1px), - #fff var(--slider-rail-steps-percent) + #0000 calc(var(${railStepsPercentVar}) - 1px), + ${theme.colorNeutralBackground1} calc(var(${railStepsPercentVar}) - 1px), + ${theme.colorNeutralBackground1} var(${railStepsPercentVar}) )`, }, }), - disabled: theme => ({ - ...shorthands.border('1px', 'solid', theme.colorTransparentStrokeDisabled), - }), - horizontal: { - '--slider-rail-direction': '90deg', - height: 'var(--slider-rail-size)', + height: get(railSizeVar), ':before': { left: '-1px', right: '-1px', - height: 'var(--slider-rail-size)', + height: get(railSizeVar), }, }, vertical: { - '--slider-rail-direction': '0deg', - width: 'var(--slider-rail-size)', + width: get(railSizeVar), height: '100%', ':before': { - width: 'var(--slider-rail-size)', + width: get(railSizeVar), top: '-1px', bottom: '1px', }, @@ -138,11 +143,12 @@ export const useRailStyles = makeStyles({ export const useThumbStyles = makeStyles({ thumb: theme => ({ position: 'absolute', - width: 'var(--slider-thumb-size)', - height: 'var(--slider-thumb-size)', + width: get(thumbSizeVar), + height: get(thumbSizeVar), outline: 'none', ...shorthands.borderRadius(theme.borderRadiusCircular), - boxShadow: `0 0 0 calc(var(--slider-thumb-size) * .2) ${theme.colorNeutralBackground1} inset`, + boxShadow: `0 0 0 calc(var(${thumbSizeVar}) * .2) ${theme.colorNeutralBackground1} inset`, + background: get(thumbColorVar), transform: 'translateX(-50%)', ':before': { position: 'absolute', @@ -153,26 +159,20 @@ export const useThumbStyles = makeStyles({ ...shorthands.borderRadius(theme.borderRadiusCircular), boxSizing: 'border-box', content: "''", - ...shorthands.border('calc(var(--slider-thumb-size) * .05)', 'solid', theme.colorNeutralStroke1), + ...shorthands.border(`calc(var(${thumbSizeVar}) * .05)`, 'solid', theme.colorNeutralStroke1), }, }), - - enabled: theme => ({ - backgroundColor: theme.colorCompoundBrandBackground, - }), - disabled: theme => ({ - backgroundColor: theme.colorNeutralForegroundDisabled, ':before': { - ...shorthands.border('calc(var(--slider-thumb-size) * .05)', 'solid', theme.colorNeutralForegroundDisabled), + ...shorthands.border(`calc(var(${thumbSizeVar}) * .05)`, 'solid', theme.colorNeutralForegroundDisabled), }, }), horizontal: { - left: 'var(--slider-thumb-position)', + left: get(thumbPositionVar), }, vertical: { transform: 'translateY(50%)', - bottom: 'var(--slider-thumb-position)', + bottom: get(thumbPositionVar), }, }); @@ -187,12 +187,12 @@ const useInputStyles = makeStyles({ ...shorthands.margin(0), }, horizontal: { - height: 'var(--slider-thumb-size)', + height: get(thumbSizeVar), }, vertical: { writingMode: 'bt-lr', '-webkit-appearance': 'slider-vertical', - width: 'var(--slider-thumb-size)', + width: get(thumbSizeVar), }, }); @@ -216,18 +216,15 @@ export const useSliderStyles = (state: SliderState): SliderState => { ); state.rail.className = mergeClasses( - railClassName, railStyles.rail, state.vertical ? railStyles.vertical : railStyles.horizontal, - state.disabled && railStyles.disabled, state.rail.className, ); state.thumb.className = mergeClasses( - thumbClassName, thumbStyles.thumb, state.vertical ? thumbStyles.vertical : thumbStyles.horizontal, - state.disabled ? thumbStyles.disabled : thumbStyles.enabled, + state.disabled && thumbStyles.disabled, state.thumb.className, ); From e74747eaf7902efaac8b8cb756527b1c9a3162e7 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Fri, 10 Dec 2021 13:36:36 -0800 Subject: [PATCH 33/53] api update fix build --- packages/react-slider/etc/react-slider.api.md | 13 ++++++----- .../src/components/Slider/useSliderStyles.ts | 22 +++++++++---------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/react-slider/etc/react-slider.api.md b/packages/react-slider/etc/react-slider.api.md index 98d0628e80c7e..e6998ffb83efb 100644 --- a/packages/react-slider/etc/react-slider.api.md +++ b/packages/react-slider/etc/react-slider.api.md @@ -31,11 +31,16 @@ export type SliderCommons = { origin?: number; size?: 'small' | 'medium'; onChange?: (ev: React_2.ChangeEvent, data: SliderOnChangeData) => void; - ariaValueText?: (value: number) => string; + getAriaValueText?: (value: number) => string; }; // @public (undocumented) -export type SliderProps = Omit, 'onChange' | 'defaultValue'> & SliderCommons; +export type SliderOnChangeData = { + value: number; +}; + +// @public (undocumented) +export type SliderProps = Omit, 'onChange' | 'defaultValue' | 'size' | 'value'> & SliderCommons; // @public export const sliderShorthandProps: (keyof SliderSlots)[]; @@ -60,10 +65,6 @@ export const useSliderState: (state: SliderState) => SliderState; // @public export const useSliderStyles: (state: SliderState) => SliderState; -// Warnings were encountered during analysis: -// -// lib/components/Slider/Slider.types.d.ts:77:5 - (ae-forgotten-export) The symbol "SliderOnChangeData" needs to be exported by the entry point index.d.ts - // (No @packageDocumentation comment for this package) ``` diff --git a/packages/react-slider/src/components/Slider/useSliderStyles.ts b/packages/react-slider/src/components/Slider/useSliderStyles.ts index 22277a7adf2d5..14c0118861d56 100644 --- a/packages/react-slider/src/components/Slider/useSliderStyles.ts +++ b/packages/react-slider/src/components/Slider/useSliderStyles.ts @@ -4,17 +4,17 @@ import type { SliderState } from './Slider.types'; export const sliderClassName = 'fui-Slider'; -const thumbSizeVar = `--${sliderClassName}-thumb-size`; -const railSizeVar = `--${sliderClassName}-rail-size`; -const railDirectionVar = `--${sliderClassName}-rail-direction`; -const railColorVar = `--${sliderClassName}-rail-color`; -const progressColorVar = `--${sliderClassName}-progress-color`; -const thumbColorVar = `--${sliderClassName}-thumb-color`; - -export const railOffsetVar = `--${sliderClassName}-rail-offset`; -export const railProgressVar = `--${sliderClassName}-rail-progress`; -export const railStepsPercentVar = `--${sliderClassName}-rail-steps-percent`; -export const thumbPositionVar = `--${sliderClassName}-thumb-position`; +const thumbSizeVar = `--fui-slider-thumb-size`; +const railSizeVar = `--fui-slider-rail-size`; +const railDirectionVar = `--fui-slider-rail-direction`; +const railColorVar = `--fui-slider-rail-color`; +const progressColorVar = `--fui-slider-progress-color`; +const thumbColorVar = `--fui-slider-thumb-color`; + +export const railOffsetVar = `--fui-slider-rail-offset`; +export const railProgressVar = `--fui-slider-rail-progress`; +export const railStepsPercentVar = `--fui-slider-rail-steps-percent`; +export const thumbPositionVar = `--fui-slider-thumb-position`; const get = (value: string) => `var(${value})`; From eb37683f05d32ede4980270753dfef4f970f914d Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Fri, 10 Dec 2021 15:04:26 -0800 Subject: [PATCH 34/53] fix RTL issues --- .../src/components/Slider/useSliderState.tsx | 12 ++++++++++-- .../src/components/Slider/useSliderStyles.ts | 4 +--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/react-slider/src/components/Slider/useSliderState.tsx b/packages/react-slider/src/components/Slider/useSliderState.tsx index ad457b72a70af..5cbc32f600ab4 100644 --- a/packages/react-slider/src/components/Slider/useSliderState.tsx +++ b/packages/react-slider/src/components/Slider/useSliderState.tsx @@ -1,12 +1,19 @@ import * as React from 'react'; import { clamp, useControllableState, useEventCallback } from '@fluentui/react-utilities'; import { getPercent } from '../../utils/index'; -import { railOffsetVar, railStepsPercentVar, railProgressVar, thumbPositionVar } from './useSliderStyles'; +import { + railOffsetVar, + railStepsPercentVar, + railProgressVar, + thumbPositionVar, + railDirectionVar, +} from './useSliderStyles'; import type { SliderState } from './Slider.types'; +import { useFluent } from '@fluentui/react-shared-contexts'; export const useSliderState = (state: SliderState) => { const { value, defaultValue = 0, min = 0, max = 100, step = 1, getAriaValueText, origin } = state; - + const { dir } = useFluent(); const [currentValue, setCurrentValue] = useControllableState({ state: value && clamp(value, min, max), defaultState: clamp(defaultValue, min, max), @@ -33,6 +40,7 @@ export const useSliderState = (state: SliderState) => { }); const rootVariables = { + [railDirectionVar]: state.vertical ? '0deg' : dir === 'ltr' ? '90deg' : '270deg', [thumbPositionVar]: valuePercent + '%', [railStepsPercentVar]: state.step && state.step > 0 ? `${(step * 100) / (max - min)}%` : '', [railOffsetVar]: origin !== undefined ? `${Math.min(valuePercent, originPercent)}%` : '0%', diff --git a/packages/react-slider/src/components/Slider/useSliderStyles.ts b/packages/react-slider/src/components/Slider/useSliderStyles.ts index 14c0118861d56..ce547e7024238 100644 --- a/packages/react-slider/src/components/Slider/useSliderStyles.ts +++ b/packages/react-slider/src/components/Slider/useSliderStyles.ts @@ -6,11 +6,11 @@ export const sliderClassName = 'fui-Slider'; const thumbSizeVar = `--fui-slider-thumb-size`; const railSizeVar = `--fui-slider-rail-size`; -const railDirectionVar = `--fui-slider-rail-direction`; const railColorVar = `--fui-slider-rail-color`; const progressColorVar = `--fui-slider-progress-color`; const thumbColorVar = `--fui-slider-thumb-color`; +export const railDirectionVar = `--fui-slider-rail-direction`; export const railOffsetVar = `--fui-slider-rail-offset`; export const railProgressVar = `--fui-slider-rail-progress`; export const railStepsPercentVar = `--fui-slider-rail-steps-percent`; @@ -41,14 +41,12 @@ export const useRootStyles = makeStyles({ }, horizontal: { - [railDirectionVar]: '90deg', minWidth: '120px', height: get(thumbSizeVar), alignItems: 'center', }, vertical: { - [railDirectionVar]: '0deg', width: get(thumbSizeVar), minHeight: '120px', justifyItems: 'center', From 91989cc537cd940e69fa4019c2d2f2b9637dcca2 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Mon, 13 Dec 2021 09:33:33 -0800 Subject: [PATCH 35/53] remove utils, cleanup tests, update ids --- .../src/components/Slider/Slider.test.tsx | 136 ++---------------- .../Slider/__snapshots__/Slider.test.tsx.snap | 116 +++++++++++++++ .../stories/SliderControlled.stories.tsx | 12 +- .../Slider/stories/SliderDefault.stories.tsx | 16 ++- .../Slider/stories/SliderDisabled.stories.tsx | 16 ++- .../Slider/stories/SliderOrigin.stories.tsx | 16 ++- .../Slider/stories/SliderSmall.stories.tsx | 16 ++- .../Slider/stories/SliderStep.stories.tsx | 16 ++- .../Slider/stories/SliderVertical.stories.tsx | 16 ++- .../src/components/Slider/useSliderState.tsx | 7 +- packages/react-slider/src/utils/getPercent.ts | 10 -- packages/react-slider/src/utils/index.ts | 1 - 12 files changed, 201 insertions(+), 177 deletions(-) delete mode 100644 packages/react-slider/src/utils/getPercent.ts delete mode 100644 packages/react-slider/src/utils/index.ts diff --git a/packages/react-slider/src/components/Slider/Slider.test.tsx b/packages/react-slider/src/components/Slider/Slider.test.tsx index 9385317b0ac75..ae58f2157d7d5 100644 --- a/packages/react-slider/src/components/Slider/Slider.test.tsx +++ b/packages/react-slider/src/components/Slider/Slider.test.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; -import { fireEvent, render, screen } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import { resetIdsForTests } from '@fluentui/react-utilities'; -import { ArrowLeft, ArrowRight, ArrowDown, PageDown, PageUp, ArrowUp, Home, End } from '@fluentui/keyboard-keys'; import { Slider } from './Slider'; import { isConformant } from '../../common/isConformant'; @@ -47,152 +46,56 @@ describe('Slider', () => { // Unit tests it('handles id prop', () => { - render(); - const sliderRoot = screen.getByTestId('test'); + render(); + const sliderRoot = screen.getByRole('slider'); expect(sliderRoot.getAttribute('id')).toEqual('test_id'); }); - it.skip('calls onChange when pointerDown', () => { - const onChange = jest.fn(); - - const { getByTestId } = render( - , - ); - - expect(onChange).toHaveBeenCalledTimes(0); - fireEvent.pointerDown(getByTestId('test'), { clientX: 0, clientY: 0 }); - expect(onChange).toHaveBeenCalledTimes(1); - expect(onChange.mock.calls[0][1]).toEqual({ value: 0 }); - }); - it('applies the defaultValue prop', () => { const inputRef = React.createRef(); - render(); + render(); expect(inputRef.current?.value).toEqual('10'); }); it('applies the value prop', () => { const inputRef = React.createRef(); - render(); + render(); expect(inputRef.current?.value).toEqual('10'); }); it('applies the disabled prop', () => { const inputRef = React.createRef(); - render(); + render(); expect(inputRef.current?.disabled).toEqual(true); }); it('applies the min prop', () => { const inputRef = React.createRef(); - render(); + render(); expect(inputRef.current?.min).toEqual('11'); }); it('applies the max prop', () => { const inputRef = React.createRef(); - render(); + render(); expect(inputRef.current?.max).toEqual('11'); }); it('applies the step prop', () => { const inputRef = React.createRef(); - render(); + render(); expect(inputRef.current?.step).toEqual('11'); }); it('clamps an initial defaultValue that is out of bounds', () => { const inputRef = React.createRef(); - render(); + render(); expect(inputRef.current?.value).toEqual('0'); }); - it.skip('handles keydown events', () => { - const inputRef = React.createRef(); - const onChange = jest.fn(); - - render( - , - ); - - const sliderInput = screen.getByTestId('test'); - expect(onChange).toBeCalledTimes(0); - - fireEvent.keyDown(sliderInput, { key: ArrowDown }); - expect(onChange.mock.calls[0][1]).toEqual({ value: 49 }); - expect(inputRef.current?.value).toEqual('49'); - - fireEvent.keyDown(sliderInput, { key: ArrowUp }); - expect(onChange.mock.calls[1][1]).toEqual({ value: 50 }); - expect(inputRef.current?.value).toEqual('50'); - - fireEvent.keyDown(sliderInput, { key: ArrowLeft }); - expect(onChange.mock.calls[2][1]).toEqual({ value: 49 }); - expect(inputRef.current?.value).toEqual('49'); - - fireEvent.keyDown(sliderInput, { key: ArrowRight }); - expect(onChange.mock.calls[3][1]).toEqual({ value: 50 }); - expect(inputRef.current?.value).toEqual('50'); - - fireEvent.keyDown(sliderInput, { key: PageUp }); - expect(onChange.mock.calls[4][1]).toEqual({ value: 60 }); - expect(inputRef.current?.value).toEqual('60'); - - fireEvent.keyDown(sliderInput, { key: PageDown }); - expect(onChange.mock.calls[5][1]).toEqual({ value: 50 }); - expect(inputRef.current?.value).toEqual('50'); - - fireEvent.keyDown(sliderInput, { key: Home }); - expect(onChange.mock.calls[6][1]).toEqual({ value: 0 }); - expect(inputRef.current?.value).toEqual('0'); - - fireEvent.keyDown(sliderInput, { key: End }); - expect(onChange.mock.calls[7][1]).toEqual({ value: 100 }); - expect(inputRef.current?.value).toEqual('100'); - - fireEvent.keyDown(sliderInput, { key: ArrowLeft, shiftKey: true }); - expect(onChange.mock.calls[8][1]).toEqual({ value: 90 }); - expect(inputRef.current?.value).toEqual('90'); - - fireEvent.keyDown(sliderInput, { key: ArrowRight, shiftKey: true }); - expect(onChange.mock.calls[9][1]).toEqual({ value: 100 }); - expect(inputRef.current?.value).toEqual('100'); - - expect(onChange).toBeCalledTimes(10); - }); - - it('does not update when the controlled value prop is provided', () => { - const inputRef = React.createRef(); - render(); - const sliderRoot = screen.getByTestId('test'); - - fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); - expect(inputRef.current?.value).toBe('50'); - }); - - it.skip('calls onChange with the correct value', () => { - const onChange = jest.fn(); - - render(); - - const sliderRoot = screen.getByTestId('test'); - - fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); - fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); - fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); - - expect(onChange.mock.calls[2][1]).toEqual({ value: 51 }); - }); - it('applies focus to the hidden input', () => { const inputRef = React.createRef(); - render(); + render(); inputRef?.current?.focus(); expect(document.activeElement).toEqual(inputRef.current); }); @@ -207,24 +110,11 @@ describe('Slider', () => { expect(document.activeElement).toEqual(document.body); }); - it('does not allow change on disabled Slider', () => { - const eventHandler = jest.fn(); - - render(); - - const sliderRoot = screen.getByTestId('test'); - - expect(eventHandler).toBeCalledTimes(0); - - fireEvent.keyDown(sliderRoot, { key: 'ArrowUp' }); - expect(eventHandler).toBeCalledTimes(0); - }); - // Accessibility tests it('handles role prop', () => { render(); - const sliderRoot = screen.getByTestId('test'); - expect(sliderRoot.getAttribute('role')).toEqual('test'); + const sliderInput = screen.getByTestId('test'); + expect(sliderInput.getAttribute('role')).toEqual('test'); }); it('renders the input slot as input', () => { diff --git a/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap b/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap index 97715bb761228..35a06c18edce0 100644 --- a/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap +++ b/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap @@ -120,3 +120,119 @@ exports[`Slider Snapshot Tests renders vertical origin Slider correctly 1`] = `
`; + +exports[`Slider renders disabled Slider correctly 1`] = ` +
+
+
+
+ +
+
+`; + +exports[`Slider renders horizontal Slider correctly 1`] = ` +
+
+
+
+ +
+
+`; + +exports[`Slider renders horizontal origin Slider correctly 1`] = ` +
+
+
+
+ +
+
+`; + +exports[`Slider renders vertical Slider correctly 1`] = ` +
+
+
+
+ +
+
+`; + +exports[`Slider renders vertical origin Slider correctly 1`] = ` +
+
+
+
+ +
+
+`; diff --git a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx index dbc85013888b8..4f496d470641f 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx @@ -1,18 +1,20 @@ import * as React from 'react'; import { Label } from '@fluentui/react-label'; +import { useId } from '@fluentui/react-utilities'; import { Slider, SliderProps } from '../../../index'; export const Controlled = () => { const [sliderValue, setSliderValue] = React.useState(160); const onSliderChange: SliderProps['onChange'] = (_, data) => setSliderValue(data.value); - + const controlId = useId('control'); + const controlledId = useId('controled'); return ( <> - - + + - - + + ); }; diff --git a/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx index 22dedd1fcad7e..cdf501b8f3a2b 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx @@ -1,10 +1,14 @@ import * as React from 'react'; import { Label } from '@fluentui/react-label'; +import { useId } from '@fluentui/react-utilities'; import { Slider } from '../../../index'; -export const Default = () => ( - <> - - - -); +export const Default = () => { + const id = useId(); + return ( + <> + + + + ); +}; diff --git a/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx index 60c88f43cc226..69ad4228d04ec 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx @@ -1,10 +1,14 @@ import * as React from 'react'; import { Label } from '@fluentui/react-label'; +import { useId } from '@fluentui/react-utilities'; import { Slider } from '../../../index'; -export const Disabled = () => ( - <> - - - -); +export const Disabled = () => { + const id = useId(); + return ( + <> + + + + ); +}; diff --git a/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx index 87bcf2c81ab93..60e214285acda 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx @@ -1,10 +1,14 @@ import * as React from 'react'; import { Label } from '@fluentui/react-label'; +import { useId } from '@fluentui/react-utilities'; import { Slider } from '../../../index'; -export const Origin = () => ( - <> - - - -); +export const Origin = () => { + const id = useId(); + return ( + <> + + + + ); +}; diff --git a/packages/react-slider/src/components/Slider/stories/SliderSmall.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderSmall.stories.tsx index 447be179eb2a9..89acd1417eb39 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderSmall.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderSmall.stories.tsx @@ -1,10 +1,14 @@ import * as React from 'react'; import { Label } from '@fluentui/react-label'; +import { useId } from '@fluentui/react-utilities'; import { Slider } from '../../../index'; -export const Small = () => ( - <> - - - -); +export const Small = () => { + const id = useId(); + return ( + <> + + + + ); +}; diff --git a/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx index 1bf23d724ece1..439c49cbf36ed 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx @@ -1,10 +1,14 @@ import * as React from 'react'; import { Label } from '@fluentui/react-label'; +import { useId } from '@fluentui/react-utilities'; import { Slider } from '../../../index'; -export const Step = () => ( - <> - - - -); +export const Step = () => { + const id = useId(); + return ( + <> + + + + ); +}; diff --git a/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx index 0b0fb198ca63e..f8904307e7775 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx @@ -1,10 +1,14 @@ import * as React from 'react'; import { Label } from '@fluentui/react-label'; +import { useId } from '@fluentui/react-utilities'; import { Slider } from '../../../index'; -export const Vertical = () => ( - <> - - - -); +export const Vertical = () => { + const id = useId(); + return ( + <> + + + + ); +}; diff --git a/packages/react-slider/src/components/Slider/useSliderState.tsx b/packages/react-slider/src/components/Slider/useSliderState.tsx index 5cbc32f600ab4..4054f5a69ac04 100644 --- a/packages/react-slider/src/components/Slider/useSliderState.tsx +++ b/packages/react-slider/src/components/Slider/useSliderState.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { clamp, useControllableState, useEventCallback } from '@fluentui/react-utilities'; -import { getPercent } from '../../utils/index'; +import { useFluent } from '@fluentui/react-shared-contexts'; import { railOffsetVar, railStepsPercentVar, @@ -9,7 +9,10 @@ import { railDirectionVar, } from './useSliderStyles'; import type { SliderState } from './Slider.types'; -import { useFluent } from '@fluentui/react-shared-contexts'; + +const getPercent = (value: number, min: number, max: number) => { + return max === min ? 0 : ((value - min) / (max - min)) * 100; +}; export const useSliderState = (state: SliderState) => { const { value, defaultValue = 0, min = 0, max = 100, step = 1, getAriaValueText, origin } = state; diff --git a/packages/react-slider/src/utils/getPercent.ts b/packages/react-slider/src/utils/getPercent.ts deleted file mode 100644 index 93cb5a07a413e..0000000000000 --- a/packages/react-slider/src/utils/getPercent.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Gets the current percent of specified value between a min and max - * - * @param value - the value to find the percent - * @param min - the lowest valid value - * @param max - the highest valid value - */ -export const getPercent = (value: number, min: number, max: number) => { - return max === min ? 0 : ((value - min) / (max - min)) * 100; -}; diff --git a/packages/react-slider/src/utils/index.ts b/packages/react-slider/src/utils/index.ts deleted file mode 100644 index dfbbf04ab242d..0000000000000 --- a/packages/react-slider/src/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './getPercent'; From 40e3e85c9129b2531c8062aa892a1349d053c647 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Mon, 13 Dec 2021 09:35:22 -0800 Subject: [PATCH 36/53] remove unnecessary code --- packages/react-slider/src/components/Slider/Slider.test.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/react-slider/src/components/Slider/Slider.test.tsx b/packages/react-slider/src/components/Slider/Slider.test.tsx index ae58f2157d7d5..c8bb0543577c9 100644 --- a/packages/react-slider/src/components/Slider/Slider.test.tsx +++ b/packages/react-slider/src/components/Slider/Slider.test.tsx @@ -4,8 +4,6 @@ import { resetIdsForTests } from '@fluentui/react-utilities'; import { Slider } from './Slider'; import { isConformant } from '../../common/isConformant'; -/* eslint-disable @typescript-eslint/no-explicit-any */ - describe('Slider', () => { isConformant({ Component: Slider, From bcdc3b846223c231a9ef7414698370a0aceea079 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Mon, 13 Dec 2021 09:52:04 -0800 Subject: [PATCH 37/53] new snaps --- .../Slider/__snapshots__/Slider.test.tsx.snap | 121 ------------------ 1 file changed, 121 deletions(-) diff --git a/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap b/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap index 35a06c18edce0..2bfad569155a8 100644 --- a/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap +++ b/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap @@ -1,126 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Slider Snapshot Tests renders disabled Slider correctly 1`] = ` -
-
-
-
- -
-
-`; - -exports[`Slider Snapshot Tests renders horizontal Slider correctly 1`] = ` -
-
-
-
- -
-
-`; - -exports[`Slider Snapshot Tests renders horizontal origin Slider correctly 1`] = ` -
-
-
-
- -
-
-`; - -exports[`Slider Snapshot Tests renders vertical Slider correctly 1`] = ` -
-
-
-
- -
-
-`; - -exports[`Slider Snapshot Tests renders vertical origin Slider correctly 1`] = ` -
-
-
-
- -
-
-`; - exports[`Slider renders disabled Slider correctly 1`] = `
Date: Tue, 14 Dec 2021 10:12:45 -0800 Subject: [PATCH 38/53] fixed thumb alignment, moved input to bottom of DOM stack --- .../src/components/Slider/renderSlider.tsx | 2 +- .../src/components/Slider/useSliderStyles.ts | 38 ++++++++++--------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/packages/react-slider/src/components/Slider/renderSlider.tsx b/packages/react-slider/src/components/Slider/renderSlider.tsx index dc1571f4395d5..245fd351b86f8 100644 --- a/packages/react-slider/src/components/Slider/renderSlider.tsx +++ b/packages/react-slider/src/components/Slider/renderSlider.tsx @@ -11,9 +11,9 @@ export const renderSlider = (state: SliderState) => { return ( + - ); }; diff --git a/packages/react-slider/src/components/Slider/useSliderStyles.ts b/packages/react-slider/src/components/Slider/useSliderStyles.ts index ce547e7024238..1fbb7cd7423d4 100644 --- a/packages/react-slider/src/components/Slider/useSliderStyles.ts +++ b/packages/react-slider/src/components/Slider/useSliderStyles.ts @@ -16,8 +16,6 @@ export const railProgressVar = `--fui-slider-rail-progress`; export const railStepsPercentVar = `--fui-slider-rail-steps-percent`; export const thumbPositionVar = `--fui-slider-thumb-position`; -const get = (value: string) => `var(${value})`; - /** * Styles for the root slot */ @@ -28,6 +26,8 @@ export const useRootStyles = makeStyles({ gridTemplateAreas: '"slider"', userSelect: 'none', touchAction: 'none', + alignItems: 'center', + justifyItems: 'center', }, small: { @@ -42,15 +42,13 @@ export const useRootStyles = makeStyles({ horizontal: { minWidth: '120px', - height: get(thumbSizeVar), - alignItems: 'center', + height: `var(${thumbSizeVar})`, }, vertical: { - width: get(thumbSizeVar), + width: `var(${thumbSizeVar})`, minHeight: '120px', - justifyItems: 'center', - gridTemplateColumns: get(thumbSizeVar), + gridTemplateColumns: `var(${thumbSizeVar})`, }, enabled: theme => ({ @@ -116,19 +114,20 @@ export const useRailStyles = makeStyles({ }), horizontal: { - height: get(railSizeVar), + width: '100%', + height: `var(${railSizeVar})`, ':before': { left: '-1px', right: '-1px', - height: get(railSizeVar), + height: `var(${railSizeVar})`, }, }, vertical: { - width: get(railSizeVar), + width: `var(${railSizeVar})`, height: '100%', ':before': { - width: get(railSizeVar), + width: `var(${railSizeVar})`, top: '-1px', bottom: '1px', }, @@ -141,12 +140,13 @@ export const useRailStyles = makeStyles({ export const useThumbStyles = makeStyles({ thumb: theme => ({ position: 'absolute', - width: get(thumbSizeVar), - height: get(thumbSizeVar), + width: `var(${thumbSizeVar})`, + height: `var(${thumbSizeVar})`, + pointerEvents: 'none', outline: 'none', ...shorthands.borderRadius(theme.borderRadiusCircular), boxShadow: `0 0 0 calc(var(${thumbSizeVar}) * .2) ${theme.colorNeutralBackground1} inset`, - background: get(thumbColorVar), + background: `var(${thumbColorVar})`, transform: 'translateX(-50%)', ':before': { position: 'absolute', @@ -166,11 +166,11 @@ export const useThumbStyles = makeStyles({ }, }), horizontal: { - left: get(thumbPositionVar), + left: `var(${thumbPositionVar})`, }, vertical: { transform: 'translateY(50%)', - bottom: get(thumbPositionVar), + bottom: `var(${thumbPositionVar})`, }, }); @@ -185,12 +185,14 @@ const useInputStyles = makeStyles({ ...shorthands.margin(0), }, horizontal: { - height: get(thumbSizeVar), + height: `var(${thumbSizeVar})`, + width: `calc(100% + var(${thumbSizeVar}))`, }, vertical: { + height: `calc(100% + var(${thumbSizeVar}))`, + width: `var(${thumbSizeVar})`, writingMode: 'bt-lr', '-webkit-appearance': 'slider-vertical', - width: get(thumbSizeVar), }, }); From f9ab54153eebb197dc1e4a82320394d80adde8b1 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 14 Dec 2021 10:16:06 -0800 Subject: [PATCH 39/53] Update packages/react-slider/src/components/Slider/Slider.test.tsx Co-authored-by: Makoto Morimoto --- packages/react-slider/src/components/Slider/Slider.test.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/react-slider/src/components/Slider/Slider.test.tsx b/packages/react-slider/src/components/Slider/Slider.test.tsx index c8bb0543577c9..dc231d32c2d23 100644 --- a/packages/react-slider/src/components/Slider/Slider.test.tsx +++ b/packages/react-slider/src/components/Slider/Slider.test.tsx @@ -44,9 +44,10 @@ describe('Slider', () => { // Unit tests it('handles id prop', () => { - render(); + const testId = "test_id"; + render(); const sliderRoot = screen.getByRole('slider'); - expect(sliderRoot.getAttribute('id')).toEqual('test_id'); + expect(sliderRoot.getAttribute('id')).toEqual(testId); }); it('applies the defaultValue prop', () => { From dbd71a42c3458a677076c5af6ad5ef9e2a522d0d Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 14 Dec 2021 10:16:19 -0800 Subject: [PATCH 40/53] Update packages/react-slider/src/components/Slider/Slider.types.ts Co-authored-by: Makoto Morimoto --- packages/react-slider/src/components/Slider/Slider.types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-slider/src/components/Slider/Slider.types.ts b/packages/react-slider/src/components/Slider/Slider.types.ts index 023c969f9c996..dd6f07a5e73a8 100644 --- a/packages/react-slider/src/components/Slider/Slider.types.ts +++ b/packages/react-slider/src/components/Slider/Slider.types.ts @@ -100,7 +100,7 @@ export type SliderOnChangeData = { value: number; }; -export type SliderProps = Omit, 'onChange' | 'defaultValue' | 'size' | 'value'> & +export type SliderProps = Omit, 'defaultValue' | 'onChange' | 'size' | 'value'> & SliderCommons; export type SliderState = ComponentState & SliderCommons; From 8bc8246f1c8e84070d58e8c2c3b2f653ed84f0ef Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 14 Dec 2021 11:32:48 -0800 Subject: [PATCH 41/53] updated stories with descriptions, fixed stateful bug --- .../src/components/Slider/Slider.stories.tsx | 2 +- .../stories/SliderControlled.stories.tsx | 22 +++++++++++----- .../Slider/stories/SliderDefault.stories.tsx | 8 ++++++ .../Slider/stories/SliderDisabled.stories.tsx | 8 ++++++ .../Slider/stories/SliderOrigin.stories.tsx | 10 +++++++ .../Slider/stories/SliderSize.stories.tsx | 26 +++++++++++++++++++ .../Slider/stories/SliderSmall.stories.tsx | 14 ---------- .../Slider/stories/SliderStep.stories.tsx | 10 ++++++- .../Slider/stories/SliderVertical.stories.tsx | 8 ++++++ .../src/components/Slider/useSliderState.tsx | 4 +-- 10 files changed, 87 insertions(+), 25 deletions(-) create mode 100644 packages/react-slider/src/components/Slider/stories/SliderSize.stories.tsx delete mode 100644 packages/react-slider/src/components/Slider/stories/SliderSmall.stories.tsx diff --git a/packages/react-slider/src/components/Slider/Slider.stories.tsx b/packages/react-slider/src/components/Slider/Slider.stories.tsx index 2566be948df25..6f911e80056df 100644 --- a/packages/react-slider/src/components/Slider/Slider.stories.tsx +++ b/packages/react-slider/src/components/Slider/Slider.stories.tsx @@ -3,7 +3,7 @@ import { Slider } from '../../index'; import type { Meta } from '@storybook/react'; export * from './stories/SliderDefault.stories'; -export * from './stories/SliderSmall.stories'; +export * from './stories/SliderSize.stories'; export * from './stories/SliderControlled.stories'; export * from './stories/SliderStep.stories'; export * from './stories/SliderOrigin.stories'; diff --git a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx index 4f496d470641f..4df7c43ecc412 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderControlled.stories.tsx @@ -1,20 +1,28 @@ import * as React from 'react'; import { Label } from '@fluentui/react-label'; import { useId } from '@fluentui/react-utilities'; +import { Button } from '@fluentui/react-button'; import { Slider, SliderProps } from '../../../index'; export const Controlled = () => { + const id = useId(); const [sliderValue, setSliderValue] = React.useState(160); const onSliderChange: SliderProps['onChange'] = (_, data) => setSliderValue(data.value); - const controlId = useId('control'); - const controlledId = useId('controled'); + const resetSlider = () => setSliderValue(0); return ( <> - - - - - + + + ); }; + +Controlled.parameters = { + docs: { + description: { + story: + 'A slider can be a controlled input where the slider value is stored in state and updated with `onChange`.', + }, + }, +}; diff --git a/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx index cdf501b8f3a2b..525af940fe6f5 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderDefault.stories.tsx @@ -12,3 +12,11 @@ export const Default = () => { ); }; + +Default.parameters = { + docs: { + description: { + story: 'A default slider', + }, + }, +}; diff --git a/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx index 69ad4228d04ec..b726a3dfa7176 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderDisabled.stories.tsx @@ -12,3 +12,11 @@ export const Disabled = () => { ); }; + +Disabled.parameters = { + docs: { + description: { + story: 'A disabled slider will not change or fire events on click or keyboard press.', + }, + }, +}; diff --git a/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx index 60e214285acda..dcf93c7f39fbe 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderOrigin.stories.tsx @@ -12,3 +12,13 @@ export const Origin = () => { ); }; + +Origin.parameters = { + docs: { + description: { + story: `A slider's progress can be represented with an origin so that values below the + origin will have negative progress and those above will have positive progress. + Origin, however, has no effect on the actual value of the slider.`, + }, + }, +}; diff --git a/packages/react-slider/src/components/Slider/stories/SliderSize.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderSize.stories.tsx new file mode 100644 index 0000000000000..fcb5d9e5bf6d0 --- /dev/null +++ b/packages/react-slider/src/components/Slider/stories/SliderSize.stories.tsx @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { Label } from '@fluentui/react-label'; +import { useId } from '@fluentui/react-utilities'; +import { Slider } from '../../../index'; + +export const Size = () => { + const smallId = useId('small'); + const mediumId = useId('medium'); + return ( + <> + + + + + + + ); +}; + +Size.parameters = { + docs: { + description: { + story: `A slider comes in both medium and small size. Medium is the default.`, + }, + }, +}; diff --git a/packages/react-slider/src/components/Slider/stories/SliderSmall.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderSmall.stories.tsx deleted file mode 100644 index 89acd1417eb39..0000000000000 --- a/packages/react-slider/src/components/Slider/stories/SliderSmall.stories.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import * as React from 'react'; -import { Label } from '@fluentui/react-label'; -import { useId } from '@fluentui/react-utilities'; -import { Slider } from '../../../index'; - -export const Small = () => { - const id = useId(); - return ( - <> - - - - ); -}; diff --git a/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx index 439c49cbf36ed..e62402543089e 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderStep.stories.tsx @@ -7,8 +7,16 @@ export const Step = () => { const id = useId(); return ( <> - + ); }; + +Step.parameters = { + docs: { + description: { + story: `You can define the step value of a slider so that the value will always be a mutiple of that step`, + }, + }, +}; diff --git a/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx b/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx index f8904307e7775..8c40af814017f 100644 --- a/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx +++ b/packages/react-slider/src/components/Slider/stories/SliderVertical.stories.tsx @@ -12,3 +12,11 @@ export const Vertical = () => { ); }; + +Vertical.parameters = { + docs: { + description: { + story: `A slider can be rendered vertically where the max value is at the top of the slider.`, + }, + }, +}; diff --git a/packages/react-slider/src/components/Slider/useSliderState.tsx b/packages/react-slider/src/components/Slider/useSliderState.tsx index 4054f5a69ac04..cc00acb0a6792 100644 --- a/packages/react-slider/src/components/Slider/useSliderState.tsx +++ b/packages/react-slider/src/components/Slider/useSliderState.tsx @@ -18,7 +18,7 @@ export const useSliderState = (state: SliderState) => { const { value, defaultValue = 0, min = 0, max = 100, step = 1, getAriaValueText, origin } = state; const { dir } = useFluent(); const [currentValue, setCurrentValue] = useControllableState({ - state: value && clamp(value, min, max), + state: value !== undefined ? clamp(value, min, max) : undefined, defaultState: clamp(defaultValue, min, max), initialState: 0, }); @@ -33,7 +33,7 @@ export const useSliderState = (state: SliderState) => { const onChange: React.ChangeEventHandler = useEventCallback(ev => { const newValue = Number(ev.target.value); - setCurrentValue(newValue); + setCurrentValue(clamp(newValue, min, max)); if (inputOnChange && inputOnChange !== propsOnChange) { inputOnChange(ev); From 6143326da11f7ba1420dd37d8bddc046c0a2f3c4 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 14 Dec 2021 11:41:41 -0800 Subject: [PATCH 42/53] updated tests --- .../src/components/Slider/Slider.test.tsx | 12 ++++ .../Slider/__snapshots__/Slider.test.tsx.snap | 60 +++++++++---------- 2 files changed, 42 insertions(+), 30 deletions(-) diff --git a/packages/react-slider/src/components/Slider/Slider.test.tsx b/packages/react-slider/src/components/Slider/Slider.test.tsx index c8bb0543577c9..74d9c1751ae52 100644 --- a/packages/react-slider/src/components/Slider/Slider.test.tsx +++ b/packages/react-slider/src/components/Slider/Slider.test.tsx @@ -61,6 +61,18 @@ describe('Slider', () => { expect(inputRef.current?.value).toEqual('10'); }); + it('applies the correct value prop when min is set', () => { + const inputRef = React.createRef(); + render(); + expect(inputRef.current?.value).toEqual('20'); + }); + + it('applies the correct value prop when max is set', () => { + const inputRef = React.createRef(); + render(); + expect(inputRef.current?.value).toEqual('20'); + }); + it('applies the disabled prop', () => { const inputRef = React.createRef(); render(); diff --git a/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap b/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap index 2bfad569155a8..82120e3e26375 100644 --- a/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap +++ b/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap @@ -5,12 +5,6 @@ exports[`Slider renders disabled Slider correctly 1`] = `
-
-
+
+
`; @@ -29,12 +29,6 @@ exports[`Slider renders horizontal Slider correctly 1`] = `
-
-
+
+
`; @@ -52,12 +52,6 @@ exports[`Slider renders horizontal origin Slider correctly 1`] = `
-
-
+
+
`; @@ -75,12 +75,6 @@ exports[`Slider renders vertical Slider correctly 1`] = `
-
-
+
+
`; @@ -98,12 +98,6 @@ exports[`Slider renders vertical origin Slider correctly 1`] = `
-
-
+
+
`; From ea4a6c91cf87f97bd0ef08fda3508c6ee8e3aeb6 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 14 Dec 2021 12:00:26 -0800 Subject: [PATCH 43/53] fixed pretty --- packages/react-slider/src/components/Slider/Slider.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-slider/src/components/Slider/Slider.test.tsx b/packages/react-slider/src/components/Slider/Slider.test.tsx index 5d62b77d2ef07..f9056d4c1a58f 100644 --- a/packages/react-slider/src/components/Slider/Slider.test.tsx +++ b/packages/react-slider/src/components/Slider/Slider.test.tsx @@ -44,7 +44,7 @@ describe('Slider', () => { // Unit tests it('handles id prop', () => { - const testId = "test_id"; + const testId = 'test_id'; render(); const sliderRoot = screen.getByRole('slider'); expect(sliderRoot.getAttribute('id')).toEqual(testId); From 7de4704ed6f754498b45c7891d0461ab139f41fa Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 14 Dec 2021 16:16:17 -0800 Subject: [PATCH 44/53] api --- packages/react-slider/etc/react-slider.api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-slider/etc/react-slider.api.md b/packages/react-slider/etc/react-slider.api.md index e6998ffb83efb..95df15d00a6eb 100644 --- a/packages/react-slider/etc/react-slider.api.md +++ b/packages/react-slider/etc/react-slider.api.md @@ -40,7 +40,7 @@ export type SliderOnChangeData = { }; // @public (undocumented) -export type SliderProps = Omit, 'onChange' | 'defaultValue' | 'size' | 'value'> & SliderCommons; +export type SliderProps = Omit, 'defaultValue' | 'onChange' | 'size' | 'value'> & SliderCommons; // @public export const sliderShorthandProps: (keyof SliderSlots)[]; From 22d35aeacd90fa5419e2b5677353aa8bddb843a0 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Thu, 16 Dec 2021 13:19:21 -0800 Subject: [PATCH 45/53] remove old snaps --- .../Slider/__snapshots__/Slider.test.tsx.snap | 117 ------------------ 1 file changed, 117 deletions(-) delete mode 100644 packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap diff --git a/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap b/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap deleted file mode 100644 index 82120e3e26375..0000000000000 --- a/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap +++ /dev/null @@ -1,117 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Slider renders disabled Slider correctly 1`] = ` -
-
- -
-
-
-
-`; - -exports[`Slider renders horizontal Slider correctly 1`] = ` -
-
- -
-
-
-
-`; - -exports[`Slider renders horizontal origin Slider correctly 1`] = ` -
-
- -
-
-
-
-`; - -exports[`Slider renders vertical Slider correctly 1`] = ` -
-
- -
-
-
-
-`; - -exports[`Slider renders vertical origin Slider correctly 1`] = ` -
-
- -
-
-
-
-`; From dfc2d9ef03a9ea059fa5ea84b9763863c8f5653d Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Thu, 16 Dec 2021 13:36:58 -0800 Subject: [PATCH 46/53] snaps --- .../Slider/__snapshots__/Slider.test.tsx.snap | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap diff --git a/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap b/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap new file mode 100644 index 0000000000000..82120e3e26375 --- /dev/null +++ b/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap @@ -0,0 +1,117 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Slider renders disabled Slider correctly 1`] = ` +
+
+ +
+
+
+
+`; + +exports[`Slider renders horizontal Slider correctly 1`] = ` +
+
+ +
+
+
+
+`; + +exports[`Slider renders horizontal origin Slider correctly 1`] = ` +
+
+ +
+
+
+
+`; + +exports[`Slider renders vertical Slider correctly 1`] = ` +
+
+ +
+
+
+
+`; + +exports[`Slider renders vertical origin Slider correctly 1`] = ` +
+
+ +
+
+
+
+`; From 9ba550ae8f9a92fa6076a066ef05714bf375a870 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Thu, 16 Dec 2021 15:52:32 -0800 Subject: [PATCH 47/53] update snaps --- .../src/components/Slider/__snapshots__/Slider.test.tsx.snap | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap b/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap index 82120e3e26375..ff564d51ef450 100644 --- a/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap +++ b/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap @@ -4,6 +4,7 @@ exports[`Slider renders disabled Slider correctly 1`] = `
Date: Tue, 21 Dec 2021 12:31:59 -0800 Subject: [PATCH 48/53] test clean up --- .../src/components/Slider/Slider.test.tsx | 98 ++++++++----------- 1 file changed, 40 insertions(+), 58 deletions(-) diff --git a/packages/react-slider/src/components/Slider/Slider.test.tsx b/packages/react-slider/src/components/Slider/Slider.test.tsx index f9056d4c1a58f..a4e19face87e7 100644 --- a/packages/react-slider/src/components/Slider/Slider.test.tsx +++ b/packages/react-slider/src/components/Slider/Slider.test.tsx @@ -1,8 +1,10 @@ import * as React from 'react'; -import { render, screen } from '@testing-library/react'; +import { fireEvent, render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { resetIdsForTests } from '@fluentui/react-utilities'; import { Slider } from './Slider'; import { isConformant } from '../../common/isConformant'; +import { debug } from 'console'; describe('Slider', () => { isConformant({ @@ -44,110 +46,90 @@ describe('Slider', () => { // Unit tests it('handles id prop', () => { - const testId = 'test_id'; + const testId = 'test-id'; render(); - const sliderRoot = screen.getByRole('slider'); - expect(sliderRoot.getAttribute('id')).toEqual(testId); + expect(screen.getByRole('slider').getAttribute('id')).toEqual(testId); }); it('applies the defaultValue prop', () => { - const inputRef = React.createRef(); - render(); - expect(inputRef.current?.value).toEqual('10'); + render(); + expect(screen.getByRole('slider').getAttribute('value')).toEqual('10'); }); it('applies the value prop', () => { - const inputRef = React.createRef(); - render(); - expect(inputRef.current?.value).toEqual('10'); + render(); + expect(screen.getByRole('slider').getAttribute('value')).toEqual('10'); }); it('applies the correct value prop when min is set', () => { - const inputRef = React.createRef(); - render(); - expect(inputRef.current?.value).toEqual('20'); + render(); + expect(screen.getByRole('slider').getAttribute('value')).toEqual('20'); }); it('applies the correct value prop when max is set', () => { - const inputRef = React.createRef(); - render(); - expect(inputRef.current?.value).toEqual('20'); + render(); + expect(screen.getByRole('slider').getAttribute('value')).toEqual('20'); }); it('applies the disabled prop', () => { - const inputRef = React.createRef(); - render(); - expect(inputRef.current?.disabled).toEqual(true); + render(); + expect(screen.getByRole('slider').getAttribute('disabled')).toBeDefined(); }); it('applies the min prop', () => { - const inputRef = React.createRef(); - render(); - expect(inputRef.current?.min).toEqual('11'); + render(); + expect(screen.getByRole('slider').getAttribute('min')).toEqual('11'); }); it('applies the max prop', () => { - const inputRef = React.createRef(); - render(); - expect(inputRef.current?.max).toEqual('11'); + render(); + expect(screen.getByRole('slider').getAttribute('max')).toEqual('11'); }); it('applies the step prop', () => { - const inputRef = React.createRef(); - render(); - expect(inputRef.current?.step).toEqual('11'); + render(); + expect(screen.getByRole('slider').getAttribute('step')).toEqual('11'); }); it('clamps an initial defaultValue that is out of bounds', () => { - const inputRef = React.createRef(); - render(); - expect(inputRef.current?.value).toEqual('0'); + render(); + expect(screen.getByRole('slider').getAttribute('value')).toEqual('0'); }); it('applies focus to the hidden input', () => { - const inputRef = React.createRef(); - render(); - inputRef?.current?.focus(); - expect(document.activeElement).toEqual(inputRef.current); + render(); + const input = screen.getByRole('slider'); + + input.focus(); + expect(document.activeElement).toEqual(input); }); it('does not allow focus on disabled Slider', () => { - const sliderRef = React.createRef(); - - render(); - + render(); + const slider = screen.getByRole('slider'); expect(document.activeElement).toEqual(document.body); - sliderRef?.current?.focus(); + slider.focus(); expect(document.activeElement).toEqual(document.body); }); // Accessibility tests it('handles role prop', () => { - render(); - const sliderInput = screen.getByTestId('test'); - expect(sliderInput.getAttribute('role')).toEqual('test'); - }); - - it('renders the input slot as input', () => { - const { container } = render(); - const inputElement = container.querySelector('.test'); - expect(inputElement?.tagName).toEqual('INPUT'); + render(); + const customRole = screen.getByRole('test'); + expect(customRole).toBeDefined(); }); it('provides the input slot with a type of range', () => { - const { container } = render(); - const inputElement = container.querySelector('.test'); - expect(inputElement?.getAttribute('type')).toEqual('range'); + render(); + expect(screen.getByRole('slider').getAttribute('type')).toEqual('range'); }); it('applies ariaValueText', () => { - const values = ['small', 'medium', 'large']; - const defaultValue = 1; - const getTextValue = (value: number) => values[value]; + const testValue = 'test-value'; + const getTextValue = () => testValue; - render(); - const sliderInput = screen.getByRole('slider'); + render(); - expect(sliderInput.getAttribute('aria-valuetext')).toEqual(values[defaultValue]); + expect(screen.getByRole('slider').getAttribute('aria-valuetext')).toEqual(testValue); }); }); From 3ee76d248b28349a1ed395821cfa3aa1fbb4b50b Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 21 Dec 2021 12:32:28 -0800 Subject: [PATCH 49/53] remove unused methods --- packages/react-slider/src/components/Slider/Slider.test.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/react-slider/src/components/Slider/Slider.test.tsx b/packages/react-slider/src/components/Slider/Slider.test.tsx index a4e19face87e7..6613fbd310a8e 100644 --- a/packages/react-slider/src/components/Slider/Slider.test.tsx +++ b/packages/react-slider/src/components/Slider/Slider.test.tsx @@ -1,10 +1,8 @@ import * as React from 'react'; -import { fireEvent, render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; +import { render, screen } from '@testing-library/react'; import { resetIdsForTests } from '@fluentui/react-utilities'; import { Slider } from './Slider'; import { isConformant } from '../../common/isConformant'; -import { debug } from 'console'; describe('Slider', () => { isConformant({ From 45c399a0cac06f6e6c172dbae0c76fe5569f9343 Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Mon, 3 Jan 2022 14:46:56 -0800 Subject: [PATCH 50/53] change private to true --- packages/react-slider/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-slider/package.json b/packages/react-slider/package.json index 9d195a9a6b323..a3655d9fde1d6 100644 --- a/packages/react-slider/package.json +++ b/packages/react-slider/package.json @@ -4,6 +4,7 @@ "description": "Fluent UI React Slider component.", "main": "lib-commonjs/index.js", "module": "lib/index.js", + "private": "true", "typings": "lib/index.d.ts", "sideEffects": false, "repository": { From 67ddf8e1c9786aa53bc5a6a5798a3efd0cc2b33a Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 4 Jan 2022 10:23:08 -0800 Subject: [PATCH 51/53] add outline for HC --- packages/react-slider/src/components/Slider/useSliderStyles.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-slider/src/components/Slider/useSliderStyles.ts b/packages/react-slider/src/components/Slider/useSliderStyles.ts index 1fbb7cd7423d4..5fd0b226a1039 100644 --- a/packages/react-slider/src/components/Slider/useSliderStyles.ts +++ b/packages/react-slider/src/components/Slider/useSliderStyles.ts @@ -100,6 +100,7 @@ export const useRailStyles = makeStyles({ var(${progressColorVar}) calc(var(${railOffsetVar}) + var(${railProgressVar})), var(${railColorVar}) calc(var(${railOffsetVar}) + var(${railProgressVar})) )`, + outline: '1px solid transparent', ':before': { content: "''", position: 'absolute', From 4be06807886162a63f7156cbbe5facd9b38544ce Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 4 Jan 2022 12:00:10 -0800 Subject: [PATCH 52/53] cleanup linting --- .../src/components/Slider/useSlider.ts | 3 ++- .../src/components/Slider/useSliderStyles.ts | 23 ++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/react-slider/src/components/Slider/useSlider.ts b/packages/react-slider/src/components/Slider/useSlider.ts index b77bae0a2f661..c180c011a6ae0 100644 --- a/packages/react-slider/src/components/Slider/useSlider.ts +++ b/packages/react-slider/src/components/Slider/useSlider.ts @@ -69,7 +69,8 @@ export const useSlider = (props: SliderProps, ref: React.Ref): ref, ...nativeProps.primary, type: 'range', - }, + orient: vertical ? 'vertical' : '', + } as SliderSlots['input'], }), rail: resolveShorthand(rail, { required: true }), thumb: resolveShorthand(thumb, { required: true }), diff --git a/packages/react-slider/src/components/Slider/useSliderStyles.ts b/packages/react-slider/src/components/Slider/useSliderStyles.ts index 5fd0b226a1039..8f004583c89f2 100644 --- a/packages/react-slider/src/components/Slider/useSliderStyles.ts +++ b/packages/react-slider/src/components/Slider/useSliderStyles.ts @@ -90,9 +90,12 @@ export const useRailStyles = makeStyles({ rail: theme => ({ ...shorthands.borderRadius(theme.borderRadiusXLarge), pointerEvents: 'none', - gridArea: 'slider', + gridRowStart: 'slider', + gridRowEnd: 'slider', + gridColumnStart: 'slider', + gridColumnEnd: 'slider', position: 'relative', - background: `linear-gradient( + backgroundImage: `linear-gradient( var(${railDirectionVar}), var(${railColorVar}) 0%, var(${railColorVar}) var(${railOffsetVar}), @@ -100,11 +103,13 @@ export const useRailStyles = makeStyles({ var(${progressColorVar}) calc(var(${railOffsetVar}) + var(${railProgressVar})), var(${railColorVar}) calc(var(${railOffsetVar}) + var(${railProgressVar})) )`, - outline: '1px solid transparent', + outlineWidth: '1px', + outlineStyle: 'solid', + outlineColor: theme.colorTransparentStroke, ':before': { content: "''", position: 'absolute', - background: `repeating-linear-gradient( + backgroundImage: `repeating-linear-gradient( var(${railDirectionVar}), #0000 0%, #0000 calc(var(${railStepsPercentVar}) - 1px), @@ -144,10 +149,10 @@ export const useThumbStyles = makeStyles({ width: `var(${thumbSizeVar})`, height: `var(${thumbSizeVar})`, pointerEvents: 'none', - outline: 'none', + outlineStyle: 'none', ...shorthands.borderRadius(theme.borderRadiusCircular), boxShadow: `0 0 0 calc(var(${thumbSizeVar}) * .2) ${theme.colorNeutralBackground1} inset`, - background: `var(${thumbColorVar})`, + backgroundColor: `var(${thumbColorVar})`, transform: 'translateX(-50%)', ':before': { position: 'absolute', @@ -181,7 +186,10 @@ export const useThumbStyles = makeStyles({ const useInputStyles = makeStyles({ input: { opacity: 0, - gridArea: 'slider', + gridRowStart: 'slider', + gridRowEnd: 'slider', + gridColumnStart: 'slider', + gridColumnEnd: 'slider', ...shorthands.padding(0), ...shorthands.margin(0), }, @@ -192,7 +200,6 @@ const useInputStyles = makeStyles({ vertical: { height: `calc(100% + var(${thumbSizeVar}))`, width: `var(${thumbSizeVar})`, - writingMode: 'bt-lr', '-webkit-appearance': 'slider-vertical', }, }); From 7c824acd2c0a6e711f1072c3cd9c1fe464a51caf Mon Sep 17 00:00:00 2001 From: Micah Godbolt Date: Tue, 4 Jan 2022 12:44:29 -0800 Subject: [PATCH 53/53] snaps --- .../src/components/Slider/__snapshots__/Slider.test.tsx.snap | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap b/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap index ff564d51ef450..8ea26c5867481 100644 --- a/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap +++ b/packages/react-slider/src/components/Slider/__snapshots__/Slider.test.tsx.snap @@ -12,6 +12,7 @@ exports[`Slider renders disabled Slider correctly 1`] = ` id="slider-1" max="10" min="0" + orient="" type="range" value="5" /> @@ -36,6 +37,7 @@ exports[`Slider renders horizontal Slider correctly 1`] = ` id="slider-1" max="10" min="0" + orient="" type="range" value="5" /> @@ -60,6 +62,7 @@ exports[`Slider renders horizontal origin Slider correctly 1`] = ` id="slider-1" max="10" min="0" + orient="" type="range" value="5" /> @@ -84,6 +87,7 @@ exports[`Slider renders vertical Slider correctly 1`] = ` id="slider-1" max="10" min="0" + orient="vertical" type="range" value="5" /> @@ -108,6 +112,7 @@ exports[`Slider renders vertical origin Slider correctly 1`] = ` id="slider-1" max="10" min="0" + orient="vertical" type="range" value="5" />