Skip to content

Commit

Permalink
Component integration / tests
Browse files Browse the repository at this point in the history
  • Loading branch information
atomiks committed Aug 6, 2024
1 parent d788bf7 commit 99bd308
Show file tree
Hide file tree
Showing 28 changed files with 410 additions and 303 deletions.
3 changes: 1 addition & 2 deletions docs/pages/base-ui/api/field-root.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
"render": { "type": { "name": "union", "description": "element<br>&#124;&nbsp;func" } },
"validate": { "type": { "name": "func" } },
"validateDebounceTime": { "type": { "name": "number" }, "default": "0" },
"validateOnChange": { "type": { "name": "bool" }, "default": "false" },
"validateOnMount": { "type": { "name": "bool" }, "default": "false" }
"validateOnChange": { "type": { "name": "bool" }, "default": "false" }
},
"name": "FieldRoot",
"imports": ["import * as Field from '@base_ui/react/Field';\nconst FieldRoot = Field.Root;"],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"componentDescription": "The field's control element.",
"componentDescription": "The field's control element. This is not necessary to use when using a native Base UI input\ncomponent (Checkbox, Switch, NumberField, Slider).",
"propDescriptions": {
"className": {
"description": "Class names applied to the element or a function that returns them based on the component&#39;s state."
Expand Down
3 changes: 0 additions & 3 deletions docs/translations/api-docs/field-root/field-root.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
},
"validateOnChange": {
"description": "Determines if validation should be triggered on the <code>change</code> event, rather than only on commit (blur)."
},
"validateOnMount": {
"description": "Determines if validation should be triggered as soon as the field is mounted."
}
},
"classDescriptions": {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useCheckboxStyleHooks } from '../utils';
import { resolveClassName } from '../../utils/resolveClassName';
import { evaluateRenderProp } from '../../utils/evaluateRenderProp';
import { useRenderPropForkRef } from '../../utils/useRenderPropForkRef';
import { useFieldRootContext } from '../../Field/Root/FieldRootContext';

function defaultRender(props: React.ComponentPropsWithRef<'span'>) {
return <span {...props} />;
Expand All @@ -29,20 +30,24 @@ const CheckboxIndicator = React.forwardRef(function CheckboxIndicator(
const { render: renderProp, className, keepMounted = false, ...otherProps } = props;
const render = renderProp ?? defaultRender;

const { ownerState: fieldOwnerState } = useFieldRootContext();

const ownerState = React.useContext(CheckboxContext);
if (ownerState === null) {
throw new Error('Base UI: Checkbox.Indicator is not placed inside the Checkbox component.');
}

const styleHooks = useCheckboxStyleHooks(ownerState);
const extendedOwnerState = { ...fieldOwnerState, ...ownerState };

const styleHooks = useCheckboxStyleHooks(extendedOwnerState);
const mergedRef = useRenderPropForkRef(render, forwardedRef);

if (!keepMounted && !ownerState.checked && !ownerState.indeterminate) {
return null;
}

const elementProps = {
className: resolveClassName(className, ownerState),
className: resolveClassName(className, extendedOwnerState),
ref: mergedRef,
...styleHooks,
...otherProps,
Expand Down
13 changes: 9 additions & 4 deletions packages/mui-base/src/Checkbox/Root/CheckboxRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import * as React from 'react';
import PropTypes from 'prop-types';
import { CheckboxContext } from './CheckboxContext';
import { useCheckboxRoot } from './useCheckboxRoot';
import type { CheckboxOwnerState, CheckboxRootProps } from './CheckboxRoot.types';
import type { CheckboxRootOwnerState, CheckboxRootProps } from './CheckboxRoot.types';
import { useCheckboxStyleHooks } from '../utils';
import { resolveClassName } from '../../utils/resolveClassName';
import { evaluateRenderProp } from '../../utils/evaluateRenderProp';
import { useRenderPropForkRef } from '../../utils/useRenderPropForkRef';
import { defaultRenderFunctions } from '../../utils/defaultRenderFunctions';
import { useFieldRootContext } from '../../Field/Root/FieldRootContext';

/**
* The foundation for building custom-styled checkboxes.
Expand All @@ -28,10 +29,10 @@ const CheckboxRoot = React.forwardRef(function CheckboxRoot(
name,
onCheckedChange,
defaultChecked,
disabled = false,
readOnly = false,
indeterminate = false,
required = false,
disabled: disabledProp = false,
checked: checkedProp,
render: renderProp,
className,
Expand All @@ -41,15 +42,19 @@ const CheckboxRoot = React.forwardRef(function CheckboxRoot(

const { checked, getInputProps, getButtonProps } = useCheckboxRoot(props);

const ownerState: CheckboxOwnerState = React.useMemo(
const { ownerState: fieldOwnerState, disabled: fieldDisabled } = useFieldRootContext();
const disabled = fieldDisabled ?? disabledProp;

const ownerState: CheckboxRootOwnerState = React.useMemo(
() => ({
...fieldOwnerState,
checked,
disabled,
readOnly,
required,
indeterminate,
}),
[checked, disabled, readOnly, required, indeterminate],
[checked, disabled, readOnly, required, indeterminate, fieldOwnerState],
);

const styleHooks = useCheckboxStyleHooks(ownerState);
Expand Down
9 changes: 5 additions & 4 deletions packages/mui-base/src/Checkbox/Root/CheckboxRoot.types.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import type * as React from 'react';
import type { BaseUIComponentProps } from '../../utils/types';
import type { FieldRootOwnerState } from '../../Field/Root/FieldRoot.types';

export type CheckboxOwnerState = {
export interface CheckboxRootOwnerState extends FieldRootOwnerState {
checked: boolean;
disabled: boolean;
readOnly: boolean;
required: boolean;
indeterminate: boolean;
};
}

export interface CheckboxRootProps
extends Omit<UseCheckboxRootParameters, 'setControlId' | 'descriptionId'>,
Omit<BaseUIComponentProps<'button', CheckboxOwnerState>, 'onChange'> {}
Omit<BaseUIComponentProps<'button', CheckboxRootOwnerState>, 'onChange'> {}

export type CheckboxContextValue = CheckboxOwnerState;
export type CheckboxContextValue = CheckboxRootOwnerState;

export interface UseCheckboxRootParameters {
/**
Expand Down
23 changes: 4 additions & 19 deletions packages/mui-base/src/Field/Control/FieldControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { useComponentRenderer } from '../../utils/useComponentRenderer';
import type {
FieldControlElement,
FieldControlOwnerState,
FieldControlProps,
} from './FieldControl.types';
import type { FieldControlElement, FieldControlProps } from './FieldControl.types';
import { useFieldControl } from './useFieldControl';
import { useFieldRootContext } from '../Root/FieldRootContext';
import { STYLE_HOOK_MAPPING } from '../utils/constants';
import { useEnhancedEffect } from '../../utils/useEnhancedEffect';

/**
* The field's control element.
* The field's control element. This is not necessary to use when using a native Base UI input
* component (Checkbox, Switch, NumberField, Slider).
*
* Demos:
*
Expand All @@ -38,26 +35,14 @@ const FieldControl = React.forwardRef(function FieldControl(
...otherProps
} = props;

const { validityData, setDisabled, touched, dirty, invalid } = useFieldRootContext();

const valid = !invalid && validityData.state.valid;
const { setDisabled, ownerState } = useFieldRootContext(false);

useEnhancedEffect(() => {
setDisabled(disabled);
}, [disabled, setDisabled]);

const { getControlProps } = useFieldControl({ id, name, value: value ?? defaultValue ?? '' });

const ownerState: FieldControlOwnerState = React.useMemo(
() => ({
disabled,
touched,
dirty,
valid,
}),
[dirty, disabled, touched, valid],
);

const { renderElement } = useComponentRenderer({
propGetter: getControlProps,
render: render ?? 'input',
Expand Down
8 changes: 2 additions & 6 deletions packages/mui-base/src/Field/Control/FieldControl.types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import type { BaseUIComponentProps } from '../../utils/types';
import type { FieldRootOwnerState } from '../Root/FieldRoot.types';

export type FieldControlElement = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;

export type FieldControlOwnerState = {
disabled: boolean;
touched: boolean;
dirty: boolean;
valid: boolean;
};
export type FieldControlOwnerState = FieldRootOwnerState;

export interface FieldControlProps extends BaseUIComponentProps<'input', FieldControlOwnerState> {}
16 changes: 2 additions & 14 deletions packages/mui-base/src/Field/Description/FieldDescription.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { useComponentRenderer } from '../../utils/useComponentRenderer';
import type { FieldDescriptionProps, FieldDescriptionOwnerState } from './FieldDescription.types';
import type { FieldDescriptionProps } from './FieldDescription.types';
import { useFieldRootContext } from '../Root/FieldRootContext';
import { useFieldDescription } from './useFieldDescription';
import { STYLE_HOOK_MAPPING } from '../utils/constants';
Expand All @@ -24,22 +24,10 @@ const FieldDescription = React.forwardRef(function FieldDescription(
) {
const { render, id, className, ...otherProps } = props;

const { validityData, touched, dirty, disabled = false, invalid } = useFieldRootContext();

const valid = !invalid && validityData.state.valid;
const { ownerState } = useFieldRootContext(false);

const { getDescriptionProps } = useFieldDescription({ id });

const ownerState: FieldDescriptionOwnerState = React.useMemo(
() => ({
disabled,
touched,
dirty,
valid,
}),
[disabled, touched, dirty, valid],
);

const { renderElement } = useComponentRenderer({
propGetter: getDescriptionProps,
render: render ?? 'p',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import type { BaseUIComponentProps } from '../../utils/types';
import type { FieldRootOwnerState } from '../Root/FieldRoot.types';

export type FieldDescriptionOwnerState = {
disabled: boolean;
touched: boolean;
dirty: boolean;
valid: boolean;
};
export type FieldDescriptionOwnerState = FieldRootOwnerState;

export interface FieldDescriptionProps
extends BaseUIComponentProps<'p', FieldDescriptionOwnerState> {}
15 changes: 2 additions & 13 deletions packages/mui-base/src/Field/Error/FieldError.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { useComponentRenderer } from '../../utils/useComponentRenderer';
import type { FieldErrorOwnerState, FieldErrorProps } from './FieldError.types';
import type { FieldErrorProps } from './FieldError.types';
import { useFieldRootContext } from '../Root/FieldRootContext';
import { useFieldError } from './useFieldError';
import { STYLE_HOOK_MAPPING } from '../utils/constants';
Expand All @@ -24,24 +24,13 @@ const FieldError = React.forwardRef(function FieldError(
) {
const { render, id, className, show, ...otherProps } = props;

const { validityData, touched, dirty, disabled = false, invalid } = useFieldRootContext();
const { validityData, ownerState, invalid } = useFieldRootContext(false);

const valid = !invalid && validityData.state.valid;
const rendered =
invalid || (show ? Boolean(validityData.state[show]) : validityData.state.valid === false);

const { getErrorProps } = useFieldError({ id, rendered });

const ownerState: FieldErrorOwnerState = React.useMemo(
() => ({
disabled,
touched,
dirty,
valid,
}),
[dirty, disabled, touched, valid],
);

const { renderElement } = useComponentRenderer({
propGetter: getErrorProps,
render: render ?? 'span',
Expand Down
8 changes: 2 additions & 6 deletions packages/mui-base/src/Field/Error/FieldError.types.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import type { BaseUIComponentProps } from '../../utils/types';
import type { FieldRootOwnerState } from '../Root/FieldRoot.types';

export type FieldErrorOwnerState = {
disabled: boolean;
touched: boolean;
dirty: boolean;
valid: boolean;
};
export type FieldErrorOwnerState = FieldRootOwnerState;

export interface FieldErrorProps extends BaseUIComponentProps<'span', FieldErrorOwnerState> {
show?: keyof ValidityState;
Expand Down
25 changes: 3 additions & 22 deletions packages/mui-base/src/Field/Label/FieldLabel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { useComponentRenderer } from '../../utils/useComponentRenderer';
import type { FieldLabelOwnerState, FieldLabelProps } from './FieldLabel.types';
import type { FieldLabelProps } from './FieldLabel.types';
import { useFieldRootContext } from '../Root/FieldRootContext';
import { useFieldLabel } from './useFieldLabel';
import { STYLE_HOOK_MAPPING } from '../utils/constants';
Expand All @@ -22,20 +22,11 @@ import { useEnhancedEffect } from '../../utils/useEnhancedEffect';
*/
const FieldLabel = React.forwardRef(function FieldLabel(
props: FieldLabelProps,
forwardedRef: React.ForwardedRef<HTMLLabelElement>,
forwardedRef: React.ForwardedRef<any>,
) {
const { render, className, id: idProp, ...otherProps } = props;

const {
setLabelId,
touched,
dirty,
disabled = false,
invalid,
validityData,
} = useFieldRootContext();

const valid = !invalid && validityData.state.valid;
const { setLabelId, ownerState } = useFieldRootContext(false);

const id = useId(idProp);

Expand All @@ -48,16 +39,6 @@ const FieldLabel = React.forwardRef(function FieldLabel(

const { getLabelProps } = useFieldLabel({ customTag: render != null });

const ownerState: FieldLabelOwnerState = React.useMemo(
() => ({
disabled,
touched,
dirty,
valid,
}),
[dirty, disabled, touched, valid],
);

const { renderElement } = useComponentRenderer({
propGetter: getLabelProps,
render: render ?? 'label',
Expand Down
8 changes: 2 additions & 6 deletions packages/mui-base/src/Field/Label/FieldLabel.types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import type { BaseUIComponentProps } from '../../utils/types';
import type { FieldRootOwnerState } from '../Root/FieldRoot.types';

export type FieldLabelOwnerState = {
disabled: boolean;
touched: boolean;
dirty: boolean;
valid: boolean;
};
export type FieldLabelOwnerState = FieldRootOwnerState;

export interface FieldLabelProps extends BaseUIComponentProps<'div', FieldLabelOwnerState> {}
Loading

0 comments on commit 99bd308

Please sign in to comment.