Skip to content

Commit

Permalink
Forward form attribute to all form control elements (#3161)
Browse files Browse the repository at this point in the history
  • Loading branch information
chaance authored Oct 1, 2024
1 parent 09754e2 commit dcd7a35
Show file tree
Hide file tree
Showing 6 changed files with 27 additions and 7 deletions.
9 changes: 9 additions & 0 deletions .yarn/versions/2adbb5fd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
releases:
"@radix-ui/react-checkbox": patch
"@radix-ui/react-radio-group": patch
"@radix-ui/react-select": patch
"@radix-ui/react-slider": patch
"@radix-ui/react-switch": patch

declined:
- primitives
4 changes: 3 additions & 1 deletion packages/react/checkbox/src/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,14 @@ const Checkbox = React.forwardRef<CheckboxElement, CheckboxProps>(
disabled,
value = 'on',
onCheckedChange,
form,
...checkboxProps
} = props;
const [button, setButton] = React.useState<HTMLButtonElement | null>(null);
const composedRefs = useComposedRefs(forwardedRef, (node) => setButton(node));
const hasConsumerStoppedPropagationRef = React.useRef(false);
// We set this to true by default so that events bubble to forms without JS (SSR)
const isFormControl = button ? Boolean(button.closest('form')) : true;
const isFormControl = button ? form || !!button.closest('form') : true;
const [checked = false, setChecked] = useControllableState({
prop: checkedProp,
defaultProp: defaultChecked,
Expand Down Expand Up @@ -108,6 +109,7 @@ const Checkbox = React.forwardRef<CheckboxElement, CheckboxProps>(
checked={checked}
required={required}
disabled={disabled}
form={form}
// We transform because the input is absolutely positioned but we have
// rendered it **after** the button. This pulls it back to sit on top
// of the button.
Expand Down
4 changes: 3 additions & 1 deletion packages/react/radio-group/src/Radio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,14 @@ const Radio = React.forwardRef<RadioElement, RadioProps>(
disabled,
value = 'on',
onCheck,
form,
...radioProps
} = props;
const [button, setButton] = React.useState<HTMLButtonElement | null>(null);
const composedRefs = useComposedRefs(forwardedRef, (node) => setButton(node));
const hasConsumerStoppedPropagationRef = React.useRef(false);
// We set this to true by default so that events bubble to forms without JS (SSR)
const isFormControl = button ? Boolean(button.closest('form')) : true;
const isFormControl = button ? form || !!button.closest('form') : true;

return (
<RadioProvider scope={__scopeRadio} checked={checked} disabled={disabled}>
Expand Down Expand Up @@ -80,6 +81,7 @@ const Radio = React.forwardRef<RadioElement, RadioProps>(
checked={checked}
required={required}
disabled={disabled}
form={form}
// We transform because the input is absolutely positioned but we have
// rendered it **after** the button. This pulls it back to sit on top
// of the button.
Expand Down
2 changes: 1 addition & 1 deletion packages/react/select/src/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ const Select: React.FC<SelectProps> = (props: ScopedProps<SelectProps>) => {
const triggerPointerDownPosRef = React.useRef<{ x: number; y: number } | null>(null);

// We set this to true by default so that events bubble to forms without JS (SSR)
const isFormControl = trigger ? Boolean(trigger.closest('form')) || form : true;
const isFormControl = trigger ? form || !!trigger.closest('form') : true;
const [nativeOptionsSet, setNativeOptionsSet] = React.useState(new Set<NativeOption>());

// The native `select` only associates the correct default value if the corresponding
Expand Down
11 changes: 8 additions & 3 deletions packages/react/slider/src/Slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,15 @@ const [createSliderContext, createSliderScope] = createContextScope(SLIDER_NAME,
]);

type SliderContextValue = {
name?: string;
disabled?: boolean;
name: string | undefined;
disabled: boolean | undefined;
min: number;
max: number;
values: number[];
valueIndexToChangeRef: React.MutableRefObject<number>;
thumbs: Set<SliderThumbElement>;
orientation: SliderProps['orientation'];
form: string | undefined;
};

const [SliderProvider, useSliderContext] = createSliderContext<SliderContextValue>(SLIDER_NAME);
Expand All @@ -71,6 +72,7 @@ interface SliderProps
onValueChange?(value: number[]): void;
onValueCommit?(value: number[]): void;
inverted?: boolean;
form?: string;
}

const Slider = React.forwardRef<SliderElement, SliderProps>(
Expand All @@ -88,6 +90,7 @@ const Slider = React.forwardRef<SliderElement, SliderProps>(
onValueChange = () => {},
onValueCommit = () => {},
inverted = false,
form,
...sliderProps
} = props;
const thumbRefs = React.useRef<SliderContextValue['thumbs']>(new Set());
Expand Down Expand Up @@ -151,6 +154,7 @@ const Slider = React.forwardRef<SliderElement, SliderProps>(
thumbs={thumbRefs.current}
values={values}
orientation={orientation}
form={form}
>
<Collection.Provider scope={props.__scopeSlider}>
<Collection.Slot scope={props.__scopeSlider}>
Expand Down Expand Up @@ -556,7 +560,7 @@ const SliderThumbImpl = React.forwardRef<SliderThumbImplElement, SliderThumbImpl
const [thumb, setThumb] = React.useState<HTMLSpanElement | null>(null);
const composedRefs = useComposedRefs(forwardedRef, (node) => setThumb(node));
// We set this to true by default so that events bubble to forms without JS (SSR)
const isFormControl = thumb ? Boolean(thumb.closest('form')) : true;
const isFormControl = thumb ? context.form || !!thumb.closest('form') : true;
const size = useSize(thumb);
// We cast because index could be `-1` which would return undefined
const value = context.values[index] as number | undefined;
Expand Down Expand Up @@ -618,6 +622,7 @@ const SliderThumbImpl = React.forwardRef<SliderThumbImplElement, SliderThumbImpl
name ??
(context.name ? context.name + (context.values.length > 1 ? '[]' : '') : undefined)
}
form={context.form}
value={value}
/>
)}
Expand Down
4 changes: 3 additions & 1 deletion packages/react/switch/src/Switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,14 @@ const Switch = React.forwardRef<SwitchElement, SwitchProps>(
disabled,
value = 'on',
onCheckedChange,
form,
...switchProps
} = props;
const [button, setButton] = React.useState<HTMLButtonElement | null>(null);
const composedRefs = useComposedRefs(forwardedRef, (node) => setButton(node));
const hasConsumerStoppedPropagationRef = React.useRef(false);
// We set this to true by default so that events bubble to forms without JS (SSR)
const isFormControl = button ? Boolean(button.closest('form')) : true;
const isFormControl = button ? form || !!button.closest('form') : true;
const [checked = false, setChecked] = useControllableState({
prop: checkedProp,
defaultProp: defaultChecked,
Expand Down Expand Up @@ -87,6 +88,7 @@ const Switch = React.forwardRef<SwitchElement, SwitchProps>(
checked={checked}
required={required}
disabled={disabled}
form={form}
// We transform because the input is absolutely positioned but we have
// rendered it **after** the button. This pulls it back to sit on top
// of the button.
Expand Down

0 comments on commit dcd7a35

Please sign in to comment.