Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions packages/@react-aria/checkbox/src/useCheckbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
*/

import {AriaCheckboxProps} from '@react-types/checkbox';
import {FormValidationResult, useFormValidation} from '@react-aria/utils';
import {InputHTMLAttributes, RefObject, useEffect} from 'react';
import {ToggleState} from '@react-stately/toggle';
import {useToggle} from '@react-aria/toggle';

export interface CheckboxAria {
export interface CheckboxAria extends FormValidationResult {
/** Props for the input element. */
inputProps: InputHTMLAttributes<HTMLInputElement>,
/** Whether the checkbox is selected. */
Expand All @@ -37,7 +38,11 @@ export interface CheckboxAria {
* @param inputRef - A ref for the HTML input element.
*/
export function useCheckbox(props: AriaCheckboxProps, state: ToggleState, inputRef: RefObject<HTMLInputElement>): CheckboxAria {
let {inputProps, isSelected, isPressed, isDisabled, isReadOnly} = useToggle(props, state, inputRef);
let validation = useFormValidation(inputRef, props.validationState, '', props.validationBehavior, props.onValidationChange);
let {inputProps, isSelected, isPressed, isDisabled, isReadOnly} = useToggle({
...props,
validationState: validation.validationState
}, state, inputRef);

let {isIndeterminate} = props;
useEffect(() => {
Expand All @@ -56,6 +61,7 @@ export function useCheckbox(props: AriaCheckboxProps, state: ToggleState, inputR
isSelected,
isPressed,
isDisabled,
isReadOnly
isReadOnly,
...validation
};
}
15 changes: 8 additions & 7 deletions packages/@react-aria/checkbox/src/useCheckboxGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
*/

import {AriaCheckboxGroupProps} from '@react-types/checkbox';
import {checkboxGroupDescriptionIds, checkboxGroupErrorMessageIds, checkboxGroupNames} from './utils';
import {checkboxGroupData} from './utils';
import {CheckboxGroupState} from '@react-stately/checkbox';
import {DOMAttributes} from '@react-types/shared';
import {filterDOMProps, mergeProps} from '@react-aria/utils';
Expand All @@ -35,22 +35,23 @@ export interface CheckboxGroupAria {
* @param state - State for the checkbox group, as returned by `useCheckboxGroupState`.
*/
export function useCheckboxGroup(props: AriaCheckboxGroupProps, state: CheckboxGroupState): CheckboxGroupAria {
let {isDisabled, name} = props;
let {isDisabled, name, validationBehavior} = props;

let {labelProps, fieldProps, descriptionProps, errorMessageProps} = useField({
...props,
// Checkbox group is not an HTML input element so it
// shouldn't be labeled by a <label> element.
labelElementType: 'span'
});
checkboxGroupDescriptionIds.set(state, descriptionProps.id);
checkboxGroupErrorMessageIds.set(state, errorMessageProps.id);
checkboxGroupData.set(state, {
name,
descriptionId: descriptionProps.id,
errorMessageId: errorMessageProps.id,
validationBehavior
});

let domProps = filterDOMProps(props, {labelable: true});

// Pass name prop from group to all items by attaching to the state.
checkboxGroupNames.set(state, name);

return {
groupProps: mergeProps(domProps, {
role: 'group',
Expand Down
10 changes: 6 additions & 4 deletions packages/@react-aria/checkbox/src/useCheckboxGroupItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import {AriaCheckboxGroupItemProps} from '@react-types/checkbox';
import {CheckboxAria, useCheckbox} from './useCheckbox';
import {checkboxGroupDescriptionIds, checkboxGroupErrorMessageIds, checkboxGroupNames} from './utils';
import {checkboxGroupData} from './utils';
import {CheckboxGroupState} from '@react-stately/checkbox';
import {RefObject} from 'react';
import {useToggleState} from '@react-stately/toggle';
Expand Down Expand Up @@ -41,20 +41,22 @@ export function useCheckboxGroupItem(props: AriaCheckboxGroupItemProps, state: C
}
});

let {name, descriptionId, errorMessageId, validationBehavior} = checkboxGroupData.get(state)!;
let res = useCheckbox({
...props,
isReadOnly: props.isReadOnly || state.isReadOnly,
isDisabled: props.isDisabled || state.isDisabled,
name: props.name || checkboxGroupNames.get(state)
name: props.name || name,
validationBehavior: props.validationBehavior || validationBehavior
}, toggleState, inputRef);

return {
...res,
inputProps: {
...res.inputProps,
'aria-describedby': [
state.validationState === 'invalid' ? checkboxGroupErrorMessageIds.get(state) : null,
checkboxGroupDescriptionIds.get(state)
state.validationState === 'invalid' ? errorMessageId : null,
descriptionId
].filter(Boolean).join(' ') || undefined
}
};
Expand Down
11 changes: 8 additions & 3 deletions packages/@react-aria/checkbox/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@

import {CheckboxGroupState} from '@react-stately/checkbox';

export const checkboxGroupNames = new WeakMap<CheckboxGroupState, string>();
export const checkboxGroupDescriptionIds = new WeakMap<CheckboxGroupState, string>();
export const checkboxGroupErrorMessageIds = new WeakMap<CheckboxGroupState, string>();
interface CheckboxGroupData {
name: string,
descriptionId: string,
errorMessageId: string,
validationBehavior: 'aria' | 'native'
}

export const checkboxGroupData = new WeakMap<CheckboxGroupState, CheckboxGroupData>();
17 changes: 14 additions & 3 deletions packages/@react-aria/color/src/useColorArea.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import {AriaColorAreaProps, ColorChannel} from '@react-types/color';
import {ColorAreaState} from '@react-stately/color';
import {DOMAttributes} from '@react-types/shared';
import {focusWithoutScrolling, isAndroid, isIOS, mergeProps, useGlobalListeners, useLabels} from '@react-aria/utils';
import {focusWithoutScrolling, isAndroid, isIOS, mergeProps, useFormReset, useGlobalListeners, useLabels} from '@react-aria/utils';
// @ts-ignore
import intlMessages from '../intl/*.json';
import React, {ChangeEvent, InputHTMLAttributes, RefObject, useCallback, useRef, useState} from 'react';
Expand Down Expand Up @@ -54,7 +54,9 @@ export function useColorArea(props: AriaColorAreaOptions, state: ColorAreaState)
inputXRef,
inputYRef,
containerRef,
'aria-label': ariaLabel
'aria-label': ariaLabel,
xName,
yName
} = props;
let stringFormatter = useLocalizedStringFormatter(intlMessages);

Expand All @@ -68,8 +70,15 @@ export function useColorArea(props: AriaColorAreaOptions, state: ColorAreaState)
focusWithoutScrolling(inputRef.current);
}
}, [inputXRef]);
let [valueChangedViaKeyboard, setValueChangedViaKeyboard] = useState(false);

useFormReset(inputXRef, [state.xValue, state.yValue], ([x, y]) => {
let newColor = state.value
.withChannelValue(state.channels.xChannel, x)
.withChannelValue(state.channels.yChannel, y);
state.setValue(newColor);
});

let [valueChangedViaKeyboard, setValueChangedViaKeyboard] = useState(false);
let {xChannel, yChannel, zChannel} = state.channels;
let xChannelStep = state.xChannelStep;
let yChannelStep = state.yChannelStep;
Expand Down Expand Up @@ -410,6 +419,7 @@ export function useColorArea(props: AriaColorAreaOptions, state: ColorAreaState)
'aria-valuetext': getAriaValueTextForChannel(xChannel),
disabled: isDisabled,
value: state.value.getChannelValue(xChannel),
name: xName,
tabIndex: (isMobile || !focusedInput || focusedInput === 'x' ? undefined : -1),
/*
So that only a single "2d slider" control shows up when listing form elements for screen readers,
Expand All @@ -434,6 +444,7 @@ export function useColorArea(props: AriaColorAreaOptions, state: ColorAreaState)
'aria-orientation': 'vertical',
disabled: isDisabled,
value: state.value.getChannelValue(yChannel),
name: yName,
tabIndex: (isMobile || focusedInput === 'y' ? undefined : -1),
/*
So that only a single "2d slider" control shows up when listing form elements for screen readers,
Expand Down
10 changes: 5 additions & 5 deletions packages/@react-aria/color/src/useColorField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@

import {AriaColorFieldProps} from '@react-types/color';
import {ColorFieldState} from '@react-stately/color';
import {FormValidationResult, mergeProps, useId} from '@react-aria/utils';
import {
HTMLAttributes,
LabelHTMLAttributes,
RefObject,
useCallback,
useState
} from 'react';
import {mergeProps, useId} from '@react-aria/utils';
import {useFocusWithin, useScrollWheel} from '@react-aria/interactions';
import {useFormattedTextField} from '@react-aria/textfield';
import {useSpinButton} from '@react-aria/spinbutton';

export interface ColorFieldAria {
export interface ColorFieldAria extends FormValidationResult {
/** Props for the label element. */
labelProps: LabelHTMLAttributes<HTMLLabelElement>,
/** Props for the input element. */
Expand Down Expand Up @@ -96,7 +96,7 @@ export function useColorField(
}
};

let {labelProps, inputProps} = useFormattedTextField(
let {inputProps, ...otherFieldProps} = useFormattedTextField(
mergeProps(props, {
id: inputId,
value: inputValue,
Expand All @@ -107,7 +107,6 @@ export function useColorField(
}), state, ref);

return {
labelProps,
inputProps: mergeProps(inputProps, spinButtonProps, focusWithinProps, {
role: 'textbox',
'aria-valuemax': null,
Expand All @@ -117,6 +116,7 @@ export function useColorField(
autoCorrect: 'off',
spellCheck: 'false',
onBlur: commit
})
}),
...otherFieldProps
};
}
3 changes: 2 additions & 1 deletion packages/@react-aria/color/src/useColorSlider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export interface ColorSliderAria {
* Color sliders allow users to adjust an individual channel of a color value.
*/
export function useColorSlider(props: AriaColorSliderOptions, state: ColorSliderState): ColorSliderAria {
let {trackRef, inputRef, orientation, channel, 'aria-label': ariaLabel} = props;
let {trackRef, inputRef, orientation, channel, 'aria-label': ariaLabel, name} = props;

let {locale, direction} = useLocale();

Expand All @@ -58,6 +58,7 @@ export function useColorSlider(props: AriaColorSliderOptions, state: ColorSlider
index: 0,
orientation,
isDisabled: props.isDisabled,
name,
trackRef,
inputRef
}, state);
Expand Down
8 changes: 6 additions & 2 deletions packages/@react-aria/color/src/useColorWheel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import {AriaColorWheelProps} from '@react-types/color';
import {ColorWheelState} from '@react-stately/color';
import {DOMAttributes} from '@react-types/shared';
import {focusWithoutScrolling, mergeProps, useGlobalListeners, useLabels} from '@react-aria/utils';
import {focusWithoutScrolling, mergeProps, useFormReset, useGlobalListeners, useLabels} from '@react-aria/utils';
import React, {ChangeEvent, InputHTMLAttributes, RefObject, useCallback, useRef} from 'react';
import {useKeyboard, useMove} from '@react-aria/interactions';
import {useLocale} from '@react-aria/i18n';
Expand Down Expand Up @@ -43,7 +43,8 @@ export function useColorWheel(props: AriaColorWheelOptions, state: ColorWheelSta
isDisabled,
innerRadius,
outerRadius,
'aria-label': ariaLabel
'aria-label': ariaLabel,
name
} = props;

let {addGlobalListener, removeGlobalListener} = useGlobalListeners();
Expand All @@ -56,6 +57,8 @@ export function useColorWheel(props: AriaColorWheelOptions, state: ColorWheelSta
}
}, [inputRef]);

useFormReset(inputRef, state.hue, state.setHue);

let currentPosition = useRef<{x: number, y: number}>(null);

let {keyboardProps} = useKeyboard({
Expand Down Expand Up @@ -311,6 +314,7 @@ export function useColorWheel(props: AriaColorWheelOptions, state: ColorWheelSta
'aria-valuetext': state.value.formatChannelValue('hue', locale),
disabled: isDisabled,
value: `${state.value.getChannelValue('hue')}`,
name,
onChange: (e: ChangeEvent<HTMLInputElement>) => {
state.setHue(parseFloat(e.target.value));
}
Expand Down
9 changes: 4 additions & 5 deletions packages/@react-aria/combobox/src/useComboBox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {AriaComboBoxProps} from '@react-types/combobox';
import {ariaHideOutside} from '@react-aria/overlays';
import {AriaListBoxOptions, getItemId, listData} from '@react-aria/listbox';
import {BaseEvent, DOMAttributes, KeyboardDelegate, PressEvent} from '@react-types/shared';
import {chain, isAppleDevice, mergeProps, useLabels} from '@react-aria/utils';
import {chain, FormValidationResult, isAppleDevice, mergeProps, useLabels} from '@react-aria/utils';
import {ComboBoxState} from '@react-stately/combobox';
import {FocusEvent, InputHTMLAttributes, KeyboardEvent, RefObject, TouchEvent, useEffect, useMemo, useRef} from 'react';
import {getChildNodes, getItemCount} from '@react-stately/collections';
Expand All @@ -40,7 +40,7 @@ export interface AriaComboBoxOptions<T> extends Omit<AriaComboBoxProps<T>, 'chil
keyboardDelegate?: KeyboardDelegate
}

export interface ComboBoxAria<T> {
export interface ComboBoxAria<T> extends FormValidationResult {
/** Props for the label element. */
labelProps: DOMAttributes,
/** Props for the combo box input element. */
Expand Down Expand Up @@ -166,7 +166,7 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta
state.setFocused(true);
};

let {labelProps, inputProps, descriptionProps, errorMessageProps} = useTextField({
let {labelProps, inputProps, ...otherFieldProps} = useTextField({
...props,
onChange: state.setInputValue,
onKeyDown: !isReadOnly && chain(state.isOpen && collectionProps.onKeyDown, onKeyDown, props.onKeyDown),
Expand Down Expand Up @@ -332,7 +332,6 @@ export function useComboBox<T>(props: AriaComboBoxOptions<T>, state: ComboBoxSta
shouldSelectOnPressUp: true,
shouldFocusOnHover: true
}),
descriptionProps,
errorMessageProps
...otherFieldProps
};
}
Loading