Skip to content

Commit 256a3ab

Browse files
committed
Pass fieldControlValidation through context
1 parent a0cb5dc commit 256a3ab

File tree

5 files changed

+36
-39
lines changed

5 files changed

+36
-39
lines changed

packages/react/src/slider/control/SliderControl.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { useEventCallback } from '../../utils/useEventCallback';
88
import { useRenderElement } from '../../utils/useRenderElement';
99
import { valueToPercent } from '../../utils/valueToPercent';
1010
import { useDirection } from '../../direction-provider/DirectionContext';
11-
import { useFieldControlValidation } from '../../field/control/useFieldControlValidation';
1211
import { useSliderRootContext } from '../root/SliderRootContext';
1312
import { sliderStyleHookMapping } from '../root/styleHooks';
1413
import type { SliderRoot } from '../root/SliderRoot';
@@ -98,6 +97,7 @@ export const SliderControl = React.forwardRef(function SliderControl(
9897
active: activeThumbIndex,
9998
disabled,
10099
dragging,
100+
fieldControlValidation,
101101
lastChangedValueRef,
102102
max,
103103
min,
@@ -135,7 +135,6 @@ export const SliderControl = React.forwardRef(function SliderControl(
135135
const offsetRef = React.useRef(0);
136136

137137
const direction = useDirection();
138-
const { commitValidation } = useFieldControlValidation();
139138

140139
const getFingerState = useEventCallback(
141140
(
@@ -281,7 +280,7 @@ export const SliderControl = React.forwardRef(function SliderControl(
281280

282281
setActive(-1);
283282

284-
commitValidation(lastChangedValueRef.current ?? finger.value);
283+
fieldControlValidation.commitValidation(lastChangedValueRef.current ?? finger.value);
285284
onValueCommitted(lastChangedValueRef.current ?? finger.value, nativeEvent);
286285

287286
touchIdRef.current = null;

packages/react/src/slider/root/SliderRoot.test.tsx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { expect } from 'chai';
22
import * as React from 'react';
33
import { spy, stub } from 'sinon';
4-
import { act, fireEvent, screen } from '@mui/internal-test-utils';
4+
import { act, flushMicrotasks, fireEvent, screen } from '@mui/internal-test-utils';
55
import {
66
DirectionProvider,
77
type TextDirection,
@@ -1987,13 +1987,12 @@ describe.skipIf(typeof Touch === 'undefined')('<Slider.Root />', () => {
19871987
);
19881988

19891989
const input = screen.getByRole('slider');
1990-
const thumb = screen.getByTestId('thumb');
1991-
19921990
expect(input).not.to.have.attribute('aria-invalid');
19931991

1992+
const thumb = screen.getByTestId('thumb');
19941993
fireEvent.focus(thumb);
19951994
fireEvent.blur(thumb);
1996-
1995+
await flushMicrotasks();
19971996
expect(input).to.have.attribute('aria-invalid', 'true');
19981997
});
19991998

@@ -2014,11 +2013,10 @@ describe.skipIf(typeof Touch === 'undefined')('<Slider.Root />', () => {
20142013
);
20152014

20162015
const input = container.querySelector<HTMLInputElement>('input')!;
2017-
20182016
expect(input).not.to.have.attribute('aria-invalid');
20192017

20202018
fireEvent.change(input, { target: { value: '1' } });
2021-
2019+
await flushMicrotasks();
20222020
expect(input).to.have.attribute('aria-invalid', 'true');
20232021
});
20242022

@@ -2040,13 +2038,11 @@ describe.skipIf(typeof Touch === 'undefined')('<Slider.Root />', () => {
20402038
);
20412039

20422040
const input = screen.getByRole('slider');
2043-
const thumb = screen.getByTestId('thumb');
2044-
20452041
expect(input).not.to.have.attribute('aria-invalid');
20462042

20472043
fireEvent.change(input, { target: { value: '1' } });
2048-
fireEvent.blur(thumb);
2049-
2044+
fireEvent.blur(screen.getByTestId('thumb'));
2045+
await flushMicrotasks();
20502046
expect(input).to.have.attribute('aria-invalid', 'true');
20512047
});
20522048

packages/react/src/slider/root/SliderRoot.tsx

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,7 @@ export const SliderRoot = React.forwardRef(function SliderRoot<
9494
validationMode,
9595
} = useFieldRootContext();
9696

97-
const {
98-
getValidationProps,
99-
getInputValidationProps,
100-
inputRef: inputValidationRef,
101-
commitValidation,
102-
} = useFieldControlValidation();
97+
const fieldControlValidation = useFieldControlValidation();
10398

10499
const ariaLabelledby = ariaLabelledbyProp ?? labelId;
105100
const disabled = fieldDisabled || disabledProp;
@@ -115,11 +110,10 @@ export const SliderRoot = React.forwardRef(function SliderRoot<
115110

116111
const sliderRef = React.useRef<HTMLElement>(null);
117112
const controlRef = React.useRef<HTMLElement>(null);
118-
const hiddenInputRef = React.useRef<HTMLInputElement>(null);
119113
const thumbRefs = React.useRef<(HTMLElement | null)[]>([]);
114+
const inputRef = useForkRef(inputRefProp, fieldControlValidation.inputRef);
120115
const lastChangedValueRef = React.useRef<number | readonly number[] | null>(null);
121116
const formatOptionsRef = useLatestRef(format);
122-
const mergedInputRef = useForkRef(inputRefProp, hiddenInputRef, inputValidationRef);
123117

124118
// We can't use the :active browser pseudo-classes.
125119
// - The active state isn't triggered when clicking on the rail.
@@ -139,7 +133,7 @@ export const SliderRoot = React.forwardRef(function SliderRoot<
139133

140134
useField({
141135
id,
142-
commitValidation,
136+
commitValidation: fieldControlValidation.commitValidation,
143137
value: valueUnwrapped,
144138
controlRef,
145139
});
@@ -181,7 +175,7 @@ export const SliderRoot = React.forwardRef(function SliderRoot<
181175
lastChangedValueRef.current = newValue;
182176
onValueChange(newValue, clonedEvent, thumbIndex);
183177
clearErrors(name);
184-
commitValidation(newValue, true);
178+
fieldControlValidation.commitValidation(newValue, true);
185179
},
186180
);
187181

@@ -200,9 +194,9 @@ export const SliderRoot = React.forwardRef(function SliderRoot<
200194
clearErrors(name);
201195

202196
if (validationMode === 'onChange') {
203-
commitValidation(nextValue ?? newValue);
197+
fieldControlValidation.commitValidation(nextValue ?? newValue);
204198
} else {
205-
commitValidation(nextValue ?? newValue, true);
199+
fieldControlValidation.commitValidation(nextValue ?? newValue, true);
206200
}
207201
}
208202
},
@@ -265,6 +259,7 @@ export const SliderRoot = React.forwardRef(function SliderRoot<
265259
active,
266260
disabled,
267261
dragging,
262+
fieldControlValidation,
268263
formatOptionsRef,
269264
handleInputChange,
270265
labelId: ariaLabelledby,
@@ -294,6 +289,7 @@ export const SliderRoot = React.forwardRef(function SliderRoot<
294289
disabled,
295290
dragging,
296291
externalTabIndex,
292+
fieldControlValidation,
297293
formatOptionsRef,
298294
handleInputChange,
299295
largeStep,
@@ -321,9 +317,9 @@ export const SliderRoot = React.forwardRef(function SliderRoot<
321317
if (valueUnwrapped == null) {
322318
return ''; // avoid uncontrolled -> controlled error
323319
}
324-
if (!Array.isArray(valueUnwrapped)) {
325-
return valueUnwrapped;
326-
}
320+
// if (!Array.isArray(valueUnwrapped)) {
321+
// return valueUnwrapped;
322+
// }
327323
return JSON.stringify(valueUnwrapped);
328324
}, [valueUnwrapped]);
329325

@@ -336,7 +332,7 @@ export const SliderRoot = React.forwardRef(function SliderRoot<
336332
id,
337333
role: 'group',
338334
},
339-
getValidationProps(elementProps),
335+
fieldControlValidation.getValidationProps(elementProps),
340336
elementProps,
341337
],
342338
customStyleHookMapping: sliderStyleHookMapping,
@@ -347,13 +343,15 @@ export const SliderRoot = React.forwardRef(function SliderRoot<
347343
<CompositeList elementsRef={thumbRefs} onMapChange={setThumbMap}>
348344
{element}
349345
<input
350-
type="hidden"
351-
{...getInputValidationProps({
346+
{...fieldControlValidation.getInputValidationProps({
347+
type: 'hidden',
352348
disabled,
353349
name,
354-
ref: mergedInputRef,
350+
ref: inputRef,
355351
value: serializedValue,
356352
style: visuallyHidden,
353+
tabIndex: -1,
354+
'aria-hidden': true,
357355
})}
358356
/>
359357
</CompositeList>

packages/react/src/slider/root/SliderRootContext.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
'use client';
22
import * as React from 'react';
33
import type { Orientation } from '../../utils/types';
4-
import { type CompositeMetadata } from '../../composite/list/CompositeList';
4+
import type { CompositeMetadata } from '../../composite/list/CompositeList';
5+
import type { useFieldControlValidation } from '../../field/control/useFieldControlValidation';
56
import type { ThumbMetadata } from '../thumb/SliderThumb';
67
import type { SliderRoot } from './SliderRoot';
78

@@ -12,6 +13,7 @@ export interface SliderRootContext {
1213
active: number;
1314
dragging: boolean;
1415
disabled: boolean;
16+
fieldControlValidation: useFieldControlValidation.ReturnValue;
1517
formatOptionsRef: React.RefObject<Intl.NumberFormatOptions | undefined>;
1618
handleInputChange: (
1719
valueInput: number,
@@ -56,6 +58,7 @@ export interface SliderRootContext {
5658
* Whether the slider is a range slider.
5759
*/
5860
range: boolean;
61+
registerFieldControlRef: React.RefCallback<Element> | null;
5962
setActive: React.Dispatch<React.SetStateAction<number>>;
6063
setDragging: React.Dispatch<React.SetStateAction<boolean>>;
6164
/**

packages/react/src/slider/thumb/SliderThumb.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import {
2020
} from '../../composite/composite';
2121
import { useCompositeListItem } from '../../composite/list/useCompositeListItem';
2222
import { useDirection } from '../../direction-provider/DirectionContext';
23-
import { useFieldControlValidation } from '../../field/control/useFieldControlValidation';
2423
import { useFieldRootContext } from '../../field/root/FieldRootContext';
2524
import { getSliderValue } from '../utils/getSliderValue';
2625
import { roundValueToStep } from '../utils/roundValueToStep';
@@ -111,6 +110,7 @@ export const SliderThumb = React.forwardRef(function SliderThumb(
111110
active: activeIndex,
112111
handleInputChange,
113112
disabled: contextDisabled,
113+
fieldControlValidation,
114114
formatOptionsRef,
115115
labelId,
116116
largeStep,
@@ -137,7 +137,6 @@ export const SliderThumb = React.forwardRef(function SliderThumb(
137137

138138
const direction = useDirection();
139139
const { setTouched, setFocused, validationMode } = useFieldRootContext();
140-
const { commitValidation, getValidationProps } = useFieldControlValidation();
141140

142141
const thumbRef = React.useRef<HTMLElement>(null);
143142

@@ -205,9 +204,11 @@ export const SliderThumb = React.forwardRef(function SliderThumb(
205204
setFocused(false);
206205

207206
if (validationMode === 'onBlur') {
208-
commitValidation(
209-
getSliderValue(thumbValue, index, min, max, sliderValues.length > 1, sliderValues),
210-
);
207+
queueMicrotask(() => {
208+
fieldControlValidation.commitValidation(
209+
getSliderValue(thumbValue, index, min, max, sliderValues.length > 1, sliderValues),
210+
);
211+
});
211212
}
212213
},
213214
onKeyDown(event: React.KeyboardEvent) {
@@ -337,7 +338,7 @@ export const SliderThumb = React.forwardRef(function SliderThumb(
337338
type: 'range',
338339
value: thumbValue ?? '',
339340
},
340-
getValidationProps,
341+
fieldControlValidation.getValidationProps,
341342
);
342343

343344
if (typeof render === 'function') {

0 commit comments

Comments
 (0)