Skip to content

Commit

Permalink
[core] Change to * imports and support shorthand render prop
Browse files Browse the repository at this point in the history
  • Loading branch information
atomiks committed Apr 11, 2024
1 parent fcb2a57 commit c9ee2f5
Show file tree
Hide file tree
Showing 30 changed files with 220 additions and 213 deletions.
50 changes: 25 additions & 25 deletions packages/mui-base/src/Checkbox/Checkbox.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,29 @@ import * as React from 'react';
import { expect } from 'chai';
import { spy } from 'sinon';
import { createRenderer, act } from '@mui/internal-test-utils';
import { Checkbox } from '.';
import * as Checkbox from '@base_ui/react/Checkbox';
import { describeConformance } from '../../test/describeConformance';

const isJSDOM = /jsdom/.test(window.navigator.userAgent);

describe('<Checkbox />', () => {
describe('<Checkbox.Root />', () => {
const { render } = createRenderer();

describeConformance(<Checkbox />, () => ({
describeConformance(<Checkbox.Root />, () => ({
inheritComponent: 'button',
refInstanceof: window.HTMLButtonElement,
render,
}));

describe('extra props', () => {
it('can override the built-in attributes', () => {
const { container } = render(<Checkbox data-state="checked" role="switch" />);
const { container } = render(<Checkbox.Root data-state="checked" role="switch" />);
expect(container.firstElementChild as HTMLElement).to.have.attribute('role', 'switch');
});
});

it('should change its state when clicked', () => {
const { getAllByRole, container } = render(<Checkbox />);
const { getAllByRole, container } = render(<Checkbox.Root />);
const [checkbox] = getAllByRole('checkbox');
const input = container.querySelector('input[type=checkbox]') as HTMLInputElement;

Expand Down Expand Up @@ -52,7 +52,7 @@ describe('<Checkbox />', () => {
return (
<div>
<button onClick={() => setChecked((c) => !c)}>Toggle</button>
<Checkbox checked={checked} />;
<Checkbox.Root checked={checked} />;
</div>
);
}
Expand All @@ -77,7 +77,7 @@ describe('<Checkbox />', () => {

it('should call onChange when clicked', () => {
const handleChange = spy();
const { getAllByRole, container } = render(<Checkbox onChange={handleChange} />);
const { getAllByRole, container } = render(<Checkbox.Root onChange={handleChange} />);
const [checkbox] = getAllByRole('checkbox');
const input = container.querySelector('input[type=checkbox]') as HTMLInputElement;

Expand All @@ -91,17 +91,17 @@ describe('<Checkbox />', () => {

describe('prop: disabled', () => {
it('should have the `aria-disabled` attribute', () => {
const { getAllByRole } = render(<Checkbox disabled />);
const { getAllByRole } = render(<Checkbox.Root disabled />);
expect(getAllByRole('checkbox')[0]).to.have.attribute('aria-disabled', 'true');
});

it('should not have the aria attribute when `disabled` is not set', () => {
const { getAllByRole } = render(<Checkbox />);
const { getAllByRole } = render(<Checkbox.Root />);
expect(getAllByRole('checkbox')[0]).not.to.have.attribute('aria-disabled');
});

it('should not change its state when clicked', () => {
const { getAllByRole } = render(<Checkbox disabled />);
const { getAllByRole } = render(<Checkbox.Root disabled />);
const [checkbox] = getAllByRole('checkbox');

expect(checkbox).to.have.attribute('aria-checked', 'false');
Expand All @@ -116,17 +116,17 @@ describe('<Checkbox />', () => {

describe('prop: readOnly', () => {
it('should have the `aria-readonly` attribute', () => {
const { getAllByRole } = render(<Checkbox readOnly />);
const { getAllByRole } = render(<Checkbox.Root readOnly />);
expect(getAllByRole('checkbox')[0]).to.have.attribute('aria-readonly', 'true');
});

it('should not have the aria attribute when `readOnly` is not set', () => {
const { getAllByRole } = render(<Checkbox />);
const { getAllByRole } = render(<Checkbox.Root />);
expect(getAllByRole('checkbox')[0]).not.to.have.attribute('aria-readonly');
});

it('should not change its state when clicked', () => {
const { getAllByRole } = render(<Checkbox readOnly />);
const { getAllByRole } = render(<Checkbox.Root readOnly />);
const [checkbox] = getAllByRole('checkbox');

expect(checkbox).to.have.attribute('aria-checked', 'false');
Expand All @@ -141,12 +141,12 @@ describe('<Checkbox />', () => {

describe('prop: indeterminate', () => {
it('should set the `aria-checked` attribute as "mixed"', () => {
const { getAllByRole } = render(<Checkbox indeterminate />);
const { getAllByRole } = render(<Checkbox.Root indeterminate />);
expect(getAllByRole('checkbox')[0]).to.have.attribute('aria-checked', 'mixed');
});

it('should not change its state when clicked', () => {
const { getAllByRole } = render(<Checkbox indeterminate />);
const { getAllByRole } = render(<Checkbox.Root indeterminate />);
const [checkbox] = getAllByRole('checkbox');

expect(checkbox).to.have.attribute('aria-checked', 'mixed');
Expand All @@ -159,23 +159,23 @@ describe('<Checkbox />', () => {
});

it('should not set the `data-indeterminate` attribute', () => {
const { getAllByRole } = render(<Checkbox indeterminate />);
const { getAllByRole } = render(<Checkbox.Root indeterminate />);
expect(getAllByRole('checkbox')[0]).to.not.have.attribute('data-indeterminate', 'true');
});

it('should not have the aria attribute when `indeterminate` is not set', () => {
const { getAllByRole } = render(<Checkbox />);
const { getAllByRole } = render(<Checkbox.Root />);
expect(getAllByRole('checkbox')[0]).not.to.have.attribute('aria-checked', 'mixed');
});

it('should not be overridden by `checked` prop', () => {
const { getAllByRole } = render(<Checkbox indeterminate checked />);
const { getAllByRole } = render(<Checkbox.Root indeterminate checked />);
expect(getAllByRole('checkbox')[0]).to.have.attribute('aria-checked', 'mixed');
});
});

it('should update its state if the underlying input is toggled', () => {
const { getAllByRole, container } = render(<Checkbox />);
const { getAllByRole, container } = render(<Checkbox.Root />);
const [checkbox] = getAllByRole('checkbox');
const input = container.querySelector('input[type=checkbox]') as HTMLInputElement;

Expand All @@ -188,9 +188,9 @@ describe('<Checkbox />', () => {

it('should place the style hooks on the root and the indicator', () => {
const { getAllByRole } = render(
<Checkbox defaultChecked disabled readOnly required>
<Checkbox.Root defaultChecked disabled readOnly required>
<Checkbox.Indicator />
</Checkbox>,
</Checkbox.Root>,
);

const [checkbox] = getAllByRole('checkbox');
Expand All @@ -208,7 +208,7 @@ describe('<Checkbox />', () => {
});

it('should set the name attribute on the input', () => {
const { container } = render(<Checkbox name="checkbox-name" />);
const { container } = render(<Checkbox.Root name="checkbox-name" />);
const input = container.querySelector('input[type="checkbox"]')! as HTMLInputElement;

expect(input).to.have.attribute('name', 'checkbox-name');
Expand All @@ -223,7 +223,7 @@ describe('<Checkbox />', () => {

const { getByTestId, getAllByRole } = render(
<label data-testid="label">
<Checkbox />
<Checkbox.Root />
Toggle
</label>,
);
Expand Down Expand Up @@ -251,7 +251,7 @@ describe('<Checkbox />', () => {
<label htmlFor="test-checkbox" data-testid="label">
Toggle
</label>
<Checkbox id="test-checkbox" />
<Checkbox.Root id="test-checkbox" />
</div>,
);

Expand Down Expand Up @@ -284,7 +284,7 @@ describe('<Checkbox />', () => {
stringifiedFormData = new URLSearchParams(formData as any).toString();
}}
>
<Checkbox name="test-checkbox" />
<Checkbox.Root name="test-checkbox" />
<button type="submit">Submit</button>
</form>,
);
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-base/src/Checkbox/Checkbox.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export type CheckboxOwnerState = {
indeterminate: boolean;
};

export interface CheckboxProps
export interface CheckboxRootProps
extends UseCheckboxParameters,
Omit<BaseUIComponentProps<'button', CheckboxOwnerState>, 'onChange'> {}

Expand Down
26 changes: 13 additions & 13 deletions packages/mui-base/src/Checkbox/CheckboxIndicator.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react';
import { expect } from 'chai';
import { createRenderer } from '@mui/internal-test-utils';
import { describeConformance } from '../../test/describeConformance';
import { Checkbox } from '.';
import * as Checkbox from '@base_ui/react/Checkbox';
import { CheckboxContext } from './CheckboxContext';

const testContext = {
Expand All @@ -29,29 +29,29 @@ describe('<Checkbox.Indicator />', () => {

it('should not render indicator by default', () => {
const { container } = render(
<Checkbox>
<Checkbox.Root>
<Checkbox.Indicator />
</Checkbox>,
</Checkbox.Root>,
);
const indicator = container.querySelector('span');
expect(indicator).to.equal(null);
});

it('should render indicator when checked', () => {
const { container } = render(
<Checkbox checked>
<Checkbox.Root checked>
<Checkbox.Indicator />
</Checkbox>,
</Checkbox.Root>,
);
const indicator = container.querySelector('span');
expect(indicator).not.to.equal(null);
});

it('should spread extra props', () => {
const { container } = render(
<Checkbox defaultChecked>
<Checkbox.Root defaultChecked>
<Checkbox.Indicator data-extra-prop="Lorem ipsum" />
</Checkbox>,
</Checkbox.Root>,
);
const indicator = container.querySelector('span');
expect(indicator).to.have.attribute('data-extra-prop', 'Lorem ipsum');
Expand All @@ -60,29 +60,29 @@ describe('<Checkbox.Indicator />', () => {
describe('keepMounted prop', () => {
it('should keep indicator mounted when unchecked', () => {
const { container } = render(
<Checkbox>
<Checkbox.Root>
<Checkbox.Indicator keepMounted />
</Checkbox>,
</Checkbox.Root>,
);
const indicator = container.querySelector('span');
expect(indicator).not.to.equal(null);
});

it('should keep indicator mounted when checked', () => {
const { container } = render(
<Checkbox checked>
<Checkbox.Root checked>
<Checkbox.Indicator keepMounted />
</Checkbox>,
</Checkbox.Root>,
);
const indicator = container.querySelector('span');
expect(indicator).not.to.equal(null);
});

it('should keep indicator mounted when indeterminate', () => {
const { container } = render(
<Checkbox indeterminate>
<Checkbox.Root indeterminate>
<Checkbox.Indicator keepMounted />
</Checkbox>,
</Checkbox.Root>,
);
const indicator = container.querySelector('span');
expect(indicator).not.to.equal(null);
Expand Down
3 changes: 2 additions & 1 deletion packages/mui-base/src/Checkbox/CheckboxIndicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { CheckboxIndicatorProps } from './Checkbox.types';
import { CheckboxContext } from './CheckboxContext';
import { resolveClassName } from '../utils/resolveClassName';
import { useCheckboxStyleHooks } from './utils';
import { evaluateRenderProp } from '../utils/evaluateRenderProp';

function defaultRender(props: React.ComponentPropsWithRef<'span'>) {
return <span {...props} />;
Expand Down Expand Up @@ -45,7 +46,7 @@ const CheckboxIndicator = React.forwardRef(function CheckboxIndicator(
...otherProps,
};

return render(elementProps, ownerState);
return evaluateRenderProp(render, elementProps, ownerState);
});

CheckboxIndicator.propTypes /* remove-proptypes */ = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import type { CheckboxOwnerState, CheckboxProps } from './Checkbox.types';
import type { CheckboxOwnerState, CheckboxRootProps } from './Checkbox.types';
import { resolveClassName } from '../utils/resolveClassName';
import { CheckboxContext } from './CheckboxContext';
import { useCheckbox } from '../useCheckbox';
import { useCheckboxStyleHooks } from './utils';
import { evaluateRenderProp } from '../utils/evaluateRenderProp';

function defaultRender(props: React.ComponentPropsWithRef<'button'>) {
return <button type="button" {...props} />;
Expand All @@ -21,8 +22,8 @@ function defaultRender(props: React.ComponentPropsWithRef<'button'>) {
*
* - [Checkbox API](https://mui.com/base-ui/react-checkbox/components-api/#checkbox)
*/
const Checkbox = React.forwardRef(function Checkbox(
props: CheckboxProps,
const CheckboxRoot = React.forwardRef(function CheckboxRoot(
props: CheckboxRootProps,
forwardedRef: React.ForwardedRef<HTMLButtonElement>,
) {
const {
Expand Down Expand Up @@ -55,23 +56,23 @@ const Checkbox = React.forwardRef(function Checkbox(

const styleHooks = useCheckboxStyleHooks(ownerState);

const buttonProps = {
const buttonProps = getButtonProps({
className: resolveClassName(className, ownerState),
ref: forwardedRef,
...styleHooks,
...otherProps,
};
});

return (
<CheckboxContext.Provider value={ownerState}>
{render(getButtonProps(buttonProps), ownerState)}
{evaluateRenderProp(render, buttonProps, ownerState)}
{!checked && props.name && <input type="hidden" name={props.name} value="off" />}
<input {...getInputProps()} />
</CheckboxContext.Provider>
);
});

Checkbox.propTypes /* remove-proptypes */ = {
CheckboxRoot.propTypes /* remove-proptypes */ = {
// ┌────────────────────────────── Warning ──────────────────────────────┐
// │ These PropTypes are generated from the TypeScript type definitions. │
// │ To update them, edit the TypeScript types and run `pnpm proptypes`. │
Expand Down Expand Up @@ -140,4 +141,4 @@ Checkbox.propTypes /* remove-proptypes */ = {
required: PropTypes.bool,
} as any;

export { Checkbox };
export { CheckboxRoot };
9 changes: 2 additions & 7 deletions packages/mui-base/src/Checkbox/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
'use client';
import { Checkbox as CheckboxRoot } from './Checkbox';
import { CheckboxIndicator } from './CheckboxIndicator';
import { combineComponentExports } from '../utils/combineComponentExports';
export { CheckboxRoot as Root } from './CheckboxRoot';
export { CheckboxIndicator as Indicator } from './CheckboxIndicator';

export * from './Checkbox.types';

export const Checkbox = combineComponentExports(CheckboxRoot, {
Indicator: CheckboxIndicator,
});
11 changes: 6 additions & 5 deletions packages/mui-base/src/NumberField/NumberField.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,29 @@ import { expect } from 'chai';
import { spy } from 'sinon';
import { act, createRenderer, screen } from '@mui/internal-test-utils';
import { fireEvent } from '@testing-library/react';
import { NumberField as NumberFieldBase, type NumberFieldProps } from '@base_ui/react/NumberField';
import * as NumberFieldBase from '@base_ui/react/NumberField';
import type { NumberFieldRootProps } from '@base_ui/react/NumberField';
import { describeConformance } from '../../test/describeConformance';

describe('<NumberField />', () => {
const { render } = createRenderer();

describeConformance(<NumberFieldBase />, () => ({
describeConformance(<NumberFieldBase.Root />, () => ({
inheritComponent: 'div',
refInstanceof: window.HTMLDivElement,
render,
}));

function NumberField(props: NumberFieldProps) {
function NumberField(props: NumberFieldRootProps) {
return (
<NumberFieldBase {...props}>
<NumberFieldBase.Root {...props}>
<NumberFieldBase.Group>
<NumberFieldBase.Input />
<NumberFieldBase.Increment />
<NumberFieldBase.Decrement />
<NumberFieldBase.ScrubArea />
</NumberFieldBase.Group>
</NumberFieldBase>
</NumberFieldBase.Root>
);
}

Expand Down
Loading

0 comments on commit c9ee2f5

Please sign in to comment.