From 83ed1f667ecc37cdb8f29c9712f197ffc03507dd Mon Sep 17 00:00:00 2001 From: atomiks Date: Wed, 7 Aug 2024 21:49:58 +1000 Subject: [PATCH] James RadioGroup PR Merge Fix form submission data Fix demo Add initial tests Port Composite tests Remove passing of non-DOM props Switch to name prop Fix docs lint Use span indicator Add autoselect Remove unrelated changes use client Refactor touched state Remove orientation Remove elementsRef use client Fix test Remove unused hook Codegen name -> value Update docs Docs Update Docs value: string | number Rename state Update demo Update Radio APIs Add styling Fix test linting use client Update tests Delete tests Update Fix error throw Update types Comment out all Radio tests Comment out Composite tests Add Radio tests back Update APIs Test composite navigation directly Fix form test Integrate with Field Update --- .../system/index.js | 76 ++++ .../system/index.tsx | 76 ++++ .../system/index.tsx.preview | 14 + .../components/radio-group/radio-group.md | 135 +++++++ docs/data/base/pages.ts | 2 +- docs/data/base/pagesApi.js | 12 + docs/pages/base-ui/api/radio-group-root.json | 26 ++ docs/pages/base-ui/api/radio-indicator.json | 19 + docs/pages/base-ui/api/radio-root.json | 24 ++ .../pages/base-ui/api/use-composite-item.json | 8 + .../base-ui/api/use-composite-list-item.json | 8 + .../base-ui/api/use-radio-group-root.json | 8 + docs/pages/base-ui/api/use-radio-root.json | 8 + .../react-radio-group/[docsTab]/index.js | 64 ++++ docs/pages/base-ui/react-radio-group/index.js | 13 + .../radio-group-root/radio-group-root.json | 19 + .../radio-indicator/radio-indicator.json | 10 + .../api-docs/radio-root/radio-root.json | 14 + .../use-composite-item.json | 1 + .../use-composite-list-item.json | 5 + .../use-radio-group-root.json | 1 + .../use-radio-root/use-radio-root.json | 1 + docs/translations/translations.json | 1 + .../src/Composite/Item/CompositeItem.tsx | 70 ++++ .../src/Composite/Item/useCompositeItem.ts | 37 ++ .../src/Composite/List/CompositeList.tsx | 120 ++++++ .../Composite/List/CompositeListContext.ts | 20 + .../Composite/List/useCompositeListItem.ts | 74 ++++ .../src/Composite/Root/CompositeRoot.tsx | 120 ++++++ .../Composite/Root/CompositeRootContext.ts | 18 + .../src/Composite/Root/useCompositeRoot.ts | 210 +++++++++++ packages/mui-base/src/Composite/composite.ts | 349 ++++++++++++++++++ .../Description/FieldDescription.test.tsx | 20 + .../src/Field/Label/FieldLabel.test.tsx | 20 + .../src/Field/Root/FieldRoot.test.tsx | 89 +++++ .../Radio/Indicator/RadioIndicator.test.tsx | 15 + .../src/Radio/Indicator/RadioIndicator.tsx | 64 ++++ .../src/Radio/Root/RadioRoot.test.tsx | 13 + .../mui-base/src/Radio/Root/RadioRoot.tsx | 154 ++++++++ .../src/Radio/Root/RadioRootContext.ts | 18 + .../mui-base/src/Radio/Root/useRadioRoot.tsx | 131 +++++++ packages/mui-base/src/Radio/index.barrel.ts | 2 + packages/mui-base/src/Radio/index.ts | 2 + .../RadioGroup/Root/RadioGroupRoot.test.tsx | 296 +++++++++++++++ .../src/RadioGroup/Root/RadioGroupRoot.tsx | 178 +++++++++ .../RadioGroup/Root/RadioGroupRootContext.ts | 29 ++ .../src/RadioGroup/Root/useRadioGroupRoot.ts | 129 +++++++ .../mui-base/src/RadioGroup/index.barrel.ts | 1 + packages/mui-base/src/RadioGroup/index.ts | 1 + packages/mui-base/src/utils/noop.ts | 1 + 50 files changed, 2725 insertions(+), 1 deletion(-) create mode 100644 docs/data/base/components/radio-group/UnstyledRadioGroupIntroduction/system/index.js create mode 100644 docs/data/base/components/radio-group/UnstyledRadioGroupIntroduction/system/index.tsx create mode 100644 docs/data/base/components/radio-group/UnstyledRadioGroupIntroduction/system/index.tsx.preview create mode 100644 docs/data/base/components/radio-group/radio-group.md create mode 100644 docs/pages/base-ui/api/radio-group-root.json create mode 100644 docs/pages/base-ui/api/radio-indicator.json create mode 100644 docs/pages/base-ui/api/radio-root.json create mode 100644 docs/pages/base-ui/api/use-composite-item.json create mode 100644 docs/pages/base-ui/api/use-composite-list-item.json create mode 100644 docs/pages/base-ui/api/use-radio-group-root.json create mode 100644 docs/pages/base-ui/api/use-radio-root.json create mode 100644 docs/pages/base-ui/react-radio-group/[docsTab]/index.js create mode 100644 docs/pages/base-ui/react-radio-group/index.js create mode 100644 docs/translations/api-docs/radio-group-root/radio-group-root.json create mode 100644 docs/translations/api-docs/radio-indicator/radio-indicator.json create mode 100644 docs/translations/api-docs/radio-root/radio-root.json create mode 100644 docs/translations/api-docs/use-composite-item/use-composite-item.json create mode 100644 docs/translations/api-docs/use-composite-list-item/use-composite-list-item.json create mode 100644 docs/translations/api-docs/use-radio-group-root/use-radio-group-root.json create mode 100644 docs/translations/api-docs/use-radio-root/use-radio-root.json create mode 100644 packages/mui-base/src/Composite/Item/CompositeItem.tsx create mode 100644 packages/mui-base/src/Composite/Item/useCompositeItem.ts create mode 100644 packages/mui-base/src/Composite/List/CompositeList.tsx create mode 100644 packages/mui-base/src/Composite/List/CompositeListContext.ts create mode 100644 packages/mui-base/src/Composite/List/useCompositeListItem.ts create mode 100644 packages/mui-base/src/Composite/Root/CompositeRoot.tsx create mode 100644 packages/mui-base/src/Composite/Root/CompositeRootContext.ts create mode 100644 packages/mui-base/src/Composite/Root/useCompositeRoot.ts create mode 100644 packages/mui-base/src/Composite/composite.ts create mode 100644 packages/mui-base/src/Radio/Indicator/RadioIndicator.test.tsx create mode 100644 packages/mui-base/src/Radio/Indicator/RadioIndicator.tsx create mode 100644 packages/mui-base/src/Radio/Root/RadioRoot.test.tsx create mode 100644 packages/mui-base/src/Radio/Root/RadioRoot.tsx create mode 100644 packages/mui-base/src/Radio/Root/RadioRootContext.ts create mode 100644 packages/mui-base/src/Radio/Root/useRadioRoot.tsx create mode 100644 packages/mui-base/src/Radio/index.barrel.ts create mode 100644 packages/mui-base/src/Radio/index.ts create mode 100644 packages/mui-base/src/RadioGroup/Root/RadioGroupRoot.test.tsx create mode 100644 packages/mui-base/src/RadioGroup/Root/RadioGroupRoot.tsx create mode 100644 packages/mui-base/src/RadioGroup/Root/RadioGroupRootContext.ts create mode 100644 packages/mui-base/src/RadioGroup/Root/useRadioGroupRoot.ts create mode 100644 packages/mui-base/src/RadioGroup/index.barrel.ts create mode 100644 packages/mui-base/src/RadioGroup/index.ts create mode 100644 packages/mui-base/src/utils/noop.ts diff --git a/docs/data/base/components/radio-group/UnstyledRadioGroupIntroduction/system/index.js b/docs/data/base/components/radio-group/UnstyledRadioGroupIntroduction/system/index.js new file mode 100644 index 0000000000..020f334beb --- /dev/null +++ b/docs/data/base/components/radio-group/UnstyledRadioGroupIntroduction/system/index.js @@ -0,0 +1,76 @@ +import * as React from 'react'; +import * as RadioGroup from '@base_ui/react/RadioGroup'; +import * as Radio from '@base_ui/react/Radio'; +import { styled } from '@mui/system'; + +export default function UnstyledRadioGroupIntroduction() { + return ( + + + + Light + + + + Medium + + + + Heavy + + + ); +} + +const grey = { + 100: '#E5EAF2', + 200: '#D8E0E9', + 300: '#CBD4E2', +}; + +const blue = { + 400: '#3399FF', + 600: '#0072E6', + 800: '#004C99', +}; + +const RadioItem = styled(Radio.Root)` + display: flex; + align-items: center; + padding: 8px 16px; + border-radius: 4px; + border: none; + background-color: ${grey[100]}; + color: black; + outline: none; + font-size: 16px; + cursor: default; + + &:hover { + background-color: ${grey[100]}; + } + + &:focus-visible { + outline: 2px solid ${blue[400]}; + outline-offset: 2px; + } + + &[data-radio='checked'] { + background-color: ${blue[600]}; + color: white; + } +`; + +const Indicator = styled(Radio.Indicator)` + border-radius: 50%; + width: 8px; + height: 8px; + margin-right: 8px; + outline: 1px solid black; + + &[data-radio='checked'] { + background-color: white; + border: none; + outline: none; + } +`; diff --git a/docs/data/base/components/radio-group/UnstyledRadioGroupIntroduction/system/index.tsx b/docs/data/base/components/radio-group/UnstyledRadioGroupIntroduction/system/index.tsx new file mode 100644 index 0000000000..020f334beb --- /dev/null +++ b/docs/data/base/components/radio-group/UnstyledRadioGroupIntroduction/system/index.tsx @@ -0,0 +1,76 @@ +import * as React from 'react'; +import * as RadioGroup from '@base_ui/react/RadioGroup'; +import * as Radio from '@base_ui/react/Radio'; +import { styled } from '@mui/system'; + +export default function UnstyledRadioGroupIntroduction() { + return ( + + + + Light + + + + Medium + + + + Heavy + + + ); +} + +const grey = { + 100: '#E5EAF2', + 200: '#D8E0E9', + 300: '#CBD4E2', +}; + +const blue = { + 400: '#3399FF', + 600: '#0072E6', + 800: '#004C99', +}; + +const RadioItem = styled(Radio.Root)` + display: flex; + align-items: center; + padding: 8px 16px; + border-radius: 4px; + border: none; + background-color: ${grey[100]}; + color: black; + outline: none; + font-size: 16px; + cursor: default; + + &:hover { + background-color: ${grey[100]}; + } + + &:focus-visible { + outline: 2px solid ${blue[400]}; + outline-offset: 2px; + } + + &[data-radio='checked'] { + background-color: ${blue[600]}; + color: white; + } +`; + +const Indicator = styled(Radio.Indicator)` + border-radius: 50%; + width: 8px; + height: 8px; + margin-right: 8px; + outline: 1px solid black; + + &[data-radio='checked'] { + background-color: white; + border: none; + outline: none; + } +`; diff --git a/docs/data/base/components/radio-group/UnstyledRadioGroupIntroduction/system/index.tsx.preview b/docs/data/base/components/radio-group/UnstyledRadioGroupIntroduction/system/index.tsx.preview new file mode 100644 index 0000000000..0547decebc --- /dev/null +++ b/docs/data/base/components/radio-group/UnstyledRadioGroupIntroduction/system/index.tsx.preview @@ -0,0 +1,14 @@ + + + + Light + + + + Medium + + + + Heavy + + \ No newline at end of file diff --git a/docs/data/base/components/radio-group/radio-group.md b/docs/data/base/components/radio-group/radio-group.md new file mode 100644 index 0000000000..097667ba8a --- /dev/null +++ b/docs/data/base/components/radio-group/radio-group.md @@ -0,0 +1,135 @@ +--- +productId: base-ui +title: React Radio Group component +components: RadioGroupRoot, RadioRoot, RadioIndicator +githubLabel: 'component: radio' +waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/radio/ +--- + +# Radio Group + +

Radio Groups contain a set of checkable buttons where only one of the buttons can be checked at a time.

+ +{{"component": "@mui/docs/ComponentLinkHeader", "design": false}} + +{{"component": "modules/components/ComponentPageTabs.js"}} + +## Introduction + +{{"demo": "UnstyledRadioGroupIntroduction", "defaultCodeOpen": false, "bg": "gradient"}} + +## Installation + +Base UI components are all available as a single package. + + + +```bash npm +npm install @base_ui/react +``` + +```bash yarn +yarn add @base_ui/react +``` + +```bash pnpm +pnpm add @base_ui/react +``` + + + +Once you have the package installed, import the components. + +```ts +import * as RadioGroup from '@base_ui/react/RadioGroup'; +import * as Radio from '@base_ui/react/Radio'; +``` + +## Anatomy + +Radio Group is composed of a `Root` and `Radio` components: + +- `` is a top-level element that wraps the other components. +- `` renders an individual ` + , + ); + + const [radioA] = screen.getAllByRole('radio'); + const submitButton = screen.getByRole('button'); + + submitButton.click(); + + expect(stringifiedFormData).to.equal('group='); + + await act(() => { + radioA.click(); + }); + + submitButton.click(); + + expect(stringifiedFormData).to.equal('group=a'); + }); + + it('should automatically select radio upon navigation', async () => { + render( + + + + , + ); + + const a = screen.getByTestId('a'); + const b = screen.getByTestId('b'); + + act(() => { + a.focus(); + }); + + expect(a).to.have.attribute('aria-checked', 'false'); + + await user.keyboard('{ArrowDown}'); + + expect(a).to.have.attribute('aria-checked', 'false'); + + expect(b).toHaveFocus(); + expect(b).to.have.attribute('aria-checked', 'true'); + }); + + it('should manage arrow key navigation', async () => { + render( +
+
, + ); + + const a = screen.getByTestId('a'); + const b = screen.getByTestId('b'); + const c = screen.getByTestId('c'); + const after = screen.getByTestId('after'); + + act(() => { + a.focus(); + }); + + expect(a).toHaveFocus(); + + await user.keyboard('{ArrowDown}'); + + expect(b).toHaveFocus(); + + await user.keyboard('{ArrowDown}'); + + expect(c).toHaveFocus(); + + await user.keyboard('{ArrowDown}'); + + expect(a).toHaveFocus(); + + await user.keyboard('{ArrowUp}'); + + expect(c).toHaveFocus(); + + await user.keyboard('{ArrowUp}'); + + expect(b).toHaveFocus(); + + await user.keyboard('{ArrowUp}'); + + expect(a).toHaveFocus(); + + await user.keyboard('{ArrowLeft}'); + + expect(c).toHaveFocus(); + + await user.keyboard('{ArrowRight}'); + + expect(a).toHaveFocus(); + + await user.tab(); + + expect(after).toHaveFocus(); + + await user.tab({ shift: true }); + + expect(a).toHaveFocus(); + + await user.keyboard('{ArrowLeft}'); + + expect(c).toHaveFocus(); + + await user.tab({ shift: true }); + await user.tab(); + + expect(c).toHaveFocus(); + }); +}); diff --git a/packages/mui-base/src/RadioGroup/Root/RadioGroupRoot.tsx b/packages/mui-base/src/RadioGroup/Root/RadioGroupRoot.tsx new file mode 100644 index 0000000000..696b6d2951 --- /dev/null +++ b/packages/mui-base/src/RadioGroup/Root/RadioGroupRoot.tsx @@ -0,0 +1,178 @@ +'use client'; +import * as React from 'react'; +import PropTypes from 'prop-types'; +import type { BaseUIComponentProps } from '../../utils/types'; +import { CompositeRoot } from '../../Composite/Root/CompositeRoot'; +import { useComponentRenderer } from '../../utils/useComponentRenderer'; +import { useEventCallback } from '../../utils/useEventCallback'; +import { useRadioGroupRoot } from './useRadioGroupRoot'; +import { RadioGroupRootContext } from './RadioGroupRootContext'; +import { useFieldRootContext } from '../../Field/Root/FieldRootContext'; + +const RadioGroupRoot = React.forwardRef(function RadioGroupRoot( + props: RadioGroupRoot.Props, + forwardedRef: React.ForwardedRef, +) { + const { + render, + className, + disabled: disabledProp, + readOnly, + required, + onValueChange: onValueChangeProp, + name, + ...otherProps + } = props; + + const { getRootProps, getInputProps, checkedValue, setCheckedValue, touched, setTouched } = + useRadioGroupRoot(props); + + const { ownerState: fieldOwnerState, disabled: fieldDisabled } = useFieldRootContext(); + + const disabled = fieldDisabled || disabledProp; + + const onValueChange = useEventCallback(onValueChangeProp ?? (() => {})); + + const ownerState: RadioGroupRoot.OwnerState = React.useMemo( + () => ({ + ...fieldOwnerState, + disabled: disabled ?? false, + required: required ?? false, + readOnly: readOnly ?? false, + }), + [fieldOwnerState, disabled, readOnly, required], + ); + + const contextValue: RadioGroupRootContext = React.useMemo( + () => ({ + checkedValue, + setCheckedValue, + onValueChange, + disabled, + readOnly, + required, + touched, + setTouched, + }), + [ + checkedValue, + setCheckedValue, + onValueChange, + disabled, + readOnly, + required, + touched, + setTouched, + ], + ); + + const { renderElement } = useComponentRenderer({ + propGetter: getRootProps, + render: render ?? 'div', + ref: forwardedRef, + className, + ownerState, + extraProps: otherProps, + }); + + return ( + + + + + ); +}); + +namespace RadioGroupRoot { + export interface OwnerState { + disabled: boolean | undefined; + readOnly: boolean | undefined; + } + + export interface Props + extends Omit, 'value' | 'defaultValue'> { + /** + * Determines if the radio group is disabled. + * @default false + */ + disabled?: boolean; + /** + * Determines if the radio group is readonly. + * @default false + */ + readOnly?: boolean; + /** + * Determines if the radio group is required. + * @default false + */ + required?: boolean; + /** + * The name of the radio group submitted with the form data. + */ + name?: string; + /** + * The value of the selected radio button. Use when controlled. + */ + value?: string | number; + /** + * The default value of the selected radio button. Use when uncontrolled. + */ + defaultValue?: string | number; + /** + * Callback fired when the value changes. + */ + onValueChange?: (value: string | number, event: React.ChangeEvent) => void; + } +} + +RadioGroupRoot.propTypes /* remove-proptypes */ = { + // ┌────────────────────────────── Warning ──────────────────────────────┐ + // │ These PropTypes are generated from the TypeScript type definitions. │ + // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ + // └─────────────────────────────────────────────────────────────────────┘ + /** + * @ignore + */ + children: PropTypes.node, + /** + * Class names applied to the element or a function that returns them based on the component's state. + */ + className: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + /** + * The default value of the selected radio button. Use when uncontrolled. + */ + defaultValue: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + /** + * Determines if the radio group is disabled. + * @default false + */ + disabled: PropTypes.bool, + /** + * The name of the radio group submitted with the form data. + */ + name: PropTypes.string, + /** + * Callback fired when the value changes. + */ + onValueChange: PropTypes.func, + /** + * Determines if the radio group is readonly. + * @default false + */ + readOnly: PropTypes.bool, + /** + * A function to customize rendering of the component. + */ + render: PropTypes.oneOfType([PropTypes.element, PropTypes.func]), + /** + * Determines if the radio group is required. + * @default false + */ + required: PropTypes.bool, + /** + * The value of the selected radio button. Use when controlled. + */ + value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), +} as any; + +export { RadioGroupRoot }; diff --git a/packages/mui-base/src/RadioGroup/Root/RadioGroupRootContext.ts b/packages/mui-base/src/RadioGroup/Root/RadioGroupRootContext.ts new file mode 100644 index 0000000000..bfefe14deb --- /dev/null +++ b/packages/mui-base/src/RadioGroup/Root/RadioGroupRootContext.ts @@ -0,0 +1,29 @@ +'use client'; +import * as React from 'react'; +import { NOOP } from '../../utils/noop'; + +export interface RadioGroupRootContext { + disabled: boolean | undefined; + readOnly: boolean | undefined; + required: boolean | undefined; + checkedValue: string | number | undefined; + setCheckedValue: React.Dispatch>; + onValueChange: (value: string | number, event: React.ChangeEvent) => void; + touched: boolean; + setTouched: React.Dispatch>; +} + +export const RadioGroupRootContext = React.createContext({ + disabled: undefined, + readOnly: undefined, + required: undefined, + checkedValue: '', + setCheckedValue: NOOP, + onValueChange: NOOP, + touched: false, + setTouched: NOOP, +}); + +export function useRadioGroupRootContext() { + return React.useContext(RadioGroupRootContext); +} diff --git a/packages/mui-base/src/RadioGroup/Root/useRadioGroupRoot.ts b/packages/mui-base/src/RadioGroup/Root/useRadioGroupRoot.ts new file mode 100644 index 0000000000..95f382384d --- /dev/null +++ b/packages/mui-base/src/RadioGroup/Root/useRadioGroupRoot.ts @@ -0,0 +1,129 @@ +import * as React from 'react'; +import { contains } from '@floating-ui/react/utils'; +import { mergeReactProps } from '../../utils/mergeReactProps'; +import { useControlled } from '../../utils/useControlled'; +import { useFieldRootContext } from '../../Field/Root/FieldRootContext'; +import { useEnhancedEffect } from '../../utils/useEnhancedEffect'; +import { useId } from '../../utils/useId'; +import { useFieldControlValidation } from '../../Field/Control/useFieldControlValidation'; + +/** + * + * API: + * + * - [useRadioGroupRoot API](https://mui.com/base-ui/api/use-radio-group-root/) + */ +export function useRadioGroupRoot(params: useRadioGroupRoot.Parameters) { + const { disabled = false, name, defaultValue, readOnly, value: externalValue } = params; + + const { + labelId, + setDisabled, + setControlId, + setTouched: setFieldTouched, + validityData, + setValidityData, + } = useFieldRootContext(); + + const { + getValidationProps, + getInputValidationProps, + inputRef: inputValidationRef, + commitValidation, + } = useFieldControlValidation(); + + useEnhancedEffect(() => { + setDisabled(disabled); + }, [disabled, setDisabled]); + + const id = useId(); + + useEnhancedEffect(() => { + setControlId(id); + return () => { + setControlId(undefined); + }; + }, [id, setControlId]); + + const [checkedValue, setCheckedValue] = useControlled({ + controlled: externalValue, + default: defaultValue, + name: 'RadioGroup', + state: 'value', + }); + + useEnhancedEffect(() => { + if (validityData.initialValue === null && checkedValue !== validityData.initialValue) { + setValidityData((prev) => ({ ...prev, initialValue: checkedValue })); + } + }, [checkedValue, setValidityData, validityData.initialValue]); + + const [touched, setTouched] = React.useState(false); + + const getRootProps = React.useCallback( + (externalProps = {}) => + mergeReactProps<'div'>(getValidationProps(externalProps), { + role: 'radiogroup', + 'aria-disabled': disabled || undefined, + 'aria-readonly': readOnly || undefined, + 'aria-labelledby': labelId, + onBlur(event) { + if (!contains(event.currentTarget, event.relatedTarget)) { + setFieldTouched(true); + commitValidation(checkedValue); + } + }, + onKeyDownCapture(event) { + if (event.key.startsWith('Arrow')) { + setFieldTouched(true); + setTouched(true); + } + }, + }), + [ + checkedValue, + commitValidation, + disabled, + getValidationProps, + labelId, + readOnly, + setFieldTouched, + ], + ); + + const getInputProps = React.useCallback( + (externalProps = {}) => + mergeReactProps(getInputValidationProps(externalProps), { + type: 'hidden', + value: checkedValue ?? '', // avoid uncontrolled -> controlled error + ref: inputValidationRef, + id, + name, + disabled, + readOnly, + }), + [id, checkedValue, disabled, getInputValidationProps, inputValidationRef, name, readOnly], + ); + + return React.useMemo( + () => ({ + getRootProps, + getInputProps, + checkedValue, + setCheckedValue, + touched, + setTouched, + }), + [getRootProps, getInputProps, checkedValue, setCheckedValue, touched], + ); +} + +namespace useRadioGroupRoot { + export interface Parameters { + name?: string; + disabled?: boolean; + readOnly?: boolean; + defaultValue?: string | number; + value?: string | number; + } +} diff --git a/packages/mui-base/src/RadioGroup/index.barrel.ts b/packages/mui-base/src/RadioGroup/index.barrel.ts new file mode 100644 index 0000000000..3d7b2c9369 --- /dev/null +++ b/packages/mui-base/src/RadioGroup/index.barrel.ts @@ -0,0 +1 @@ +export * from './Root/RadioGroupRoot'; diff --git a/packages/mui-base/src/RadioGroup/index.ts b/packages/mui-base/src/RadioGroup/index.ts new file mode 100644 index 0000000000..f78d654570 --- /dev/null +++ b/packages/mui-base/src/RadioGroup/index.ts @@ -0,0 +1 @@ +export { RadioGroupRoot as Root } from './Root/RadioGroupRoot'; diff --git a/packages/mui-base/src/utils/noop.ts b/packages/mui-base/src/utils/noop.ts new file mode 100644 index 0000000000..7da8f11910 --- /dev/null +++ b/packages/mui-base/src/utils/noop.ts @@ -0,0 +1 @@ +export const NOOP = () => {};