Skip to content

Commit

Permalink
Do not require compound subcomponents to have a defined value
Browse files Browse the repository at this point in the history
  • Loading branch information
michaldudak committed Mar 30, 2023
1 parent acf6cf2 commit f4df169
Show file tree
Hide file tree
Showing 17 changed files with 203 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@ const testContext = {
getItemState: dummyGetItemState,
open: false,
registerHighlightChangeHandler: () => () => {},
registerItem: () => {},
registerItem: () => ({ id: '', unregister: () => {} }),
registerSelectionChangeHandler: () => () => {},
totalSubitemCount: 0,
unregisterItem: () => {},
};

describe('<MenuItemUnstyled />', () => {
Expand Down
6 changes: 2 additions & 4 deletions packages/mui-base/src/OptionUnstyled/OptionUnstyled.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@ describe('<OptionUnstyled />', () => {
getItemIndex: () => 0,
getItemState: dummyGetItemState,
registerHighlightChangeHandler: () => () => {},
registerItem: () => () => {},
registerItem: () => ({ id: 0, unregister: () => {} }),
registerSelectionChangeHandler: () => () => {},
totalSubitemCount: 0,
unregisterItem: () => () => {},
}}
>
{node}
Expand All @@ -43,10 +42,9 @@ describe('<OptionUnstyled />', () => {
getItemIndex: () => 0,
getItemState: dummyGetItemState,
registerHighlightChangeHandler: () => () => {},
registerItem: () => () => {},
registerItem: () => ({ id: 0, unregister: () => {} }),
registerSelectionChangeHandler: () => () => {},
totalSubitemCount: 0,
unregisterItem: () => () => {},
}}
>
{node}
Expand Down
3 changes: 1 addition & 2 deletions packages/mui-base/src/TabUnstyled/TabUnstyled.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ describe('<TabUnstyled />', () => {

const testTabsListContext: TabsListProviderValue = {
dispatch: () => {},
registerItem: () => {},
unregisterItem: () => {},
registerItem: () => ({ id: 0, unregister: () => {} }),
getItemIndex: () => 0,
totalSubitemCount: 1,
getItemState() {
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-base/src/TabUnstyled/TabUnstyled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ TabUnstyled.propTypes /* remove-proptypes */ = {
/**
* You can provide your own value. Otherwise, we fall back to the child position index.
*/
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
} as any;

export default TabUnstyled;
2 changes: 1 addition & 1 deletion packages/mui-base/src/TabUnstyled/TabUnstyled.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface TabUnstyledOwnProps
/**
* You can provide your own value. Otherwise, we fall back to the child position index.
*/
value: number | string;
value?: number | string;
/**
* Callback invoked when new value is being set.
*/
Expand Down
4 changes: 1 addition & 3 deletions packages/mui-base/src/useMenu/MenuProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export default function MenuProvider(props: MenuProviderProps) {
registerHighlightChangeHandler,
registerSelectionChangeHandler,
registerItem,
unregisterItem,
totalSubitemCount,
} = value;

Expand All @@ -54,9 +53,8 @@ export default function MenuProvider(props: MenuProviderProps) {
getItemIndex,
registerItem,
totalSubitemCount,
unregisterItem,
}),
[registerItem, getItemIndex, unregisterItem, totalSubitemCount],
[registerItem, getItemIndex, totalSubitemCount],
);

return (
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-base/src/useMenuItem/useMenuItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
UseMenuItemRootSlotProps,
} from './useMenuItem.types';
import { useListItem } from '../useList';
import { useCompoundItem } from '../utils/useCompound';
import { useCompoundItem } from '../utils/useCompoundItem';

/**
*
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-base/src/useOption/useOption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { unstable_useForkRef as useForkRef, unstable_useId as useId } from '@mui
import { SelectOption, UseOptionParameters, UseOptionReturnValue } from './useOption.types';
import { EventHandlers } from '../utils';
import { useListItem } from '../useList';
import { useCompoundItem } from '../utils/useCompound';
import { useCompoundItem } from '../utils/useCompoundItem';

/**
*
Expand Down
4 changes: 1 addition & 3 deletions packages/mui-base/src/useSelect/SelectProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export default function SelectProvider<Value>(props: SelectProviderProps<Value>)
registerHighlightChangeHandler,
registerSelectionChangeHandler,
registerItem,
unregisterItem,
totalSubitemCount,
} = value;

Expand Down Expand Up @@ -56,9 +55,8 @@ export default function SelectProvider<Value>(props: SelectProviderProps<Value>)
getItemIndex,
registerItem,
totalSubitemCount,
unregisterItem,
}),
[registerItem, getItemIndex, unregisterItem, totalSubitemCount],
[registerItem, getItemIndex, totalSubitemCount],
);

return (
Expand Down
24 changes: 15 additions & 9 deletions packages/mui-base/src/useTab/useTab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@ import { unstable_useId as useId, unstable_useForkRef as useForkRef } from '@mui
import { useTabContext } from '../TabsUnstyled';
import { UseTabParameters, UseTabReturnValue, UseTabRootSlotProps } from './useTab.types';
import { EventHandlers } from '../utils';
import { useCompoundItem } from '../utils/useCompound';
import { useCompoundItem } from '../utils/useCompoundItem';
import { useListItem } from '../useList';
import useButton from '../useButton';
import { TabMetadata } from '../useTabs';

function tabValueGenerator(otherTabValues: Set<string | number>) {
return otherTabValues.size;
}

/**
*
* Demos:
Expand All @@ -18,12 +23,20 @@ import { TabMetadata } from '../useTabs';
* - [useTab API](https://mui.com/base/api/use-tab/)
*/
function useTab(parameters: UseTabParameters): UseTabReturnValue {
const { value, ref: externalRef, disabled = false } = parameters;
const { value: valueParam, ref: externalRef, disabled = false } = parameters;

const tabRef = React.useRef<HTMLElement>(null);

const { value: selectedValue, idPrefix = '', selectionFollowsFocus } = useTabContext();

const tabMetadata = React.useMemo(() => ({ disabled, ref: tabRef }), [disabled, tabRef]);

const {
id: value,
index,
totalItemCount: totalTabsCount,
} = useCompoundItem<string | number, TabMetadata>(valueParam, tabMetadata, tabValueGenerator);

const {
getRootProps: getTabProps,
ref: listItemRefHandler,
Expand All @@ -47,13 +60,6 @@ function useTab(parameters: UseTabParameters): UseTabReturnValue {

const tabId = idPrefix + useId();

const tabMetadata = React.useMemo(() => ({ disabled, ref: tabRef }), [disabled, tabRef]);

const { index, totalItemCount: totalTabsCount } = useCompoundItem<string | number, TabMetadata>(
value,
tabMetadata,
);

const getRootProps = <TOther extends EventHandlers>(
otherHandlers: TOther = {} as TOther,
): UseTabRootSlotProps<TOther> => {
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-base/src/useTab/useTab.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export interface UseTabParameters {
* The value of the tab.
* It's used to associate the tab with a tab panel(s) with the same value.
*/
value: number | string;
value?: number | string;
/**
* If `true`, the tab will be disabled.
*/
Expand Down
4 changes: 1 addition & 3 deletions packages/mui-base/src/useTabs/TabsProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ export default function TabsProvider(props: TabsProviderProps) {
registerItem,
selectionFollowsFocus,
totalSubitemCount,
unregisterItem,
value,
} = valueProp;

Expand All @@ -36,9 +35,8 @@ export default function TabsProvider(props: TabsProviderProps) {
getItemIndex,
registerItem,
totalSubitemCount,
unregisterItem,
}),
[registerItem, getItemIndex, unregisterItem, totalSubitemCount],
[registerItem, getItemIndex, totalSubitemCount],
);

const tabsContextValue: TabsContextValue = React.useMemo(
Expand Down
4 changes: 1 addition & 3 deletions packages/mui-base/src/useTabsList/TabsListProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ export default function TabsListProvider(props: TabsListProviderProps) {
registerHighlightChangeHandler,
registerSelectionChangeHandler,
registerItem,
unregisterItem,
totalSubitemCount,
} = value;

Expand Down Expand Up @@ -56,9 +55,8 @@ export default function TabsListProvider(props: TabsListProviderProps) {
getItemIndex,
registerItem,
totalSubitemCount,
unregisterItem,
}),
[registerItem, getItemIndex, unregisterItem, totalSubitemCount],
[registerItem, getItemIndex, totalSubitemCount],
);

return (
Expand Down
39 changes: 38 additions & 1 deletion packages/mui-base/src/utils/useCompound.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as React from 'react';
import { expect } from 'chai';
import { render } from 'test/utils';
import { CompoundComponentContext, useCompoundParent, useCompoundItem } from './useCompound';
import { CompoundComponentContext, useCompoundParent } from './useCompound';
import { useCompoundItem } from './useCompoundItem';

describe('compound components', () => {
describe('useCompoundParent', () => {
Expand Down Expand Up @@ -166,5 +167,41 @@ describe('compound components', () => {
expect(child.innerHTML).to.equal(`${index + 1} of 4`);
});
});

it('gets assigned a generated id if none is provided', () => {
function Parent(props: React.PropsWithChildren<{}>) {
const { children } = props;
const { contextValue } = useCompoundParent<number, null>();

return (
<CompoundComponentContext.Provider value={contextValue}>
<ul>{children}</ul>
</CompoundComponentContext.Provider>
);
}

function idGenerator(existingIds: Set<string>) {
return `item-${existingIds.size}`;
}

function Child() {
const { id } = useCompoundItem<string, null>(undefined, null, idGenerator);

return <li>{id}</li>;
}

const { getAllByRole } = render(
<Parent>
<Child />
<Child />
<Child />
</Parent>,
);

const children = getAllByRole('listitem');
expect(children[0].innerHTML).to.equal('item-0');
expect(children[1].innerHTML).to.equal('item-1');
expect(children[2].innerHTML).to.equal('item-2');
});
});
});
Loading

0 comments on commit f4df169

Please sign in to comment.