Skip to content

Commit

Permalink
fix: use cloneChildren to pass props through fragments
Browse files Browse the repository at this point in the history
  • Loading branch information
adarshpastakia committed Oct 25, 2024
1 parent 4e1e34b commit 7741faa
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 43 deletions.
5 changes: 3 additions & 2 deletions packages/core/src/components/button/ButtonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import { cloneChildren } from "@react-fabric/utilities";
import classNames from "classnames";
import { Children, cloneElement } from "react";
import { cloneElement } from "react";
import {
type AriaProps,
type ChildrenProp,
Expand Down Expand Up @@ -96,7 +97,7 @@ export const ButtonGroup = ({
)}
{...aria}
>
{Children.map(children, (child: AnyObject) =>
{cloneChildren(children, (child: AnyObject) =>
cloneElement(child, {
color,
size,
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/components/button/ToggleButtonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import { EMPTY_ARRAY, isArray } from "@react-fabric/utilities";
import { cloneChildren, EMPTY_ARRAY, isArray } from "@react-fabric/utilities";
import classNames from "classnames";
import { Children, cloneElement, useCallback, useMemo } from "react";
import { cloneElement, useCallback, useMemo } from "react";
import { useControlledValue } from "../../hooks/useControlledValue";
import {
type AriaProps,
Expand Down Expand Up @@ -140,7 +140,7 @@ export const ToggleButtonGroup = ({
)}
{...aria}
>
{Children.map(children, (child: AnyObject) =>
{cloneChildren(children, (child: AnyObject) =>
cloneElement(child, {
color,
size,
Expand Down
39 changes: 18 additions & 21 deletions packages/core/src/components/menu/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,9 @@ import {
useRole,
useTypeahead,
} from "@floating-ui/react";
import { mergeRefs } from "@react-fabric/utilities";
import { cloneChildren, mergeRefs } from "@react-fabric/utilities";
import classNames from "classnames";
import {
Children,
cloneElement,
Fragment,
useCallback,
Expand Down Expand Up @@ -231,25 +230,23 @@ const MenuComponent = ({
// @ts-expect-error ignore
{...{ style: !isNested ? rest.style : floatingStyles }}
>
{Children.map(children, (child: AnyObject, index) => {
if (child) {
labelsRef.current[index] = child.props.label;
return cloneElement(
child,
nodeCheck(child, MenuItem, Menu, MenuComponent)
? {
minimal: !isNested && minimal,
"data-focus": activeIndex === index,
ref: (el: HTMLElement) =>
(elementsRef.current[index] = el),
...getItemProps({
onClick: child.props.onClick,
tabIndex: activeIndex === index ? 0 : -1,
}),
}
: {},
);
}
{cloneChildren(children, (child: AnyObject, index) => {
labelsRef.current[index] = child.props.label;
return cloneElement(
child,
nodeCheck(child, MenuItem, Menu, MenuComponent)
? {
minimal: !isNested && minimal,
"data-focus": activeIndex === index,
ref: (el: HTMLElement) =>
(elementsRef.current[index] = el),
...getItemProps({
onClick: child.props.onClick,
tabIndex: activeIndex === index ? 0 : -1,
}),
}
: {},
);
})}
</div>
</FloatingFocusManager>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,12 @@ export const _ButtonGroup: ButtonGroupStory = {
return (
<Fragment>
<ButtonGroup {...args}>
<Button>First</Button>
<Tooltip content="Tester">
<Button>First</Button>
</Tooltip>
<Button>Second</Button>
<Button>Third</Button>
{args.className === "red" && <Button>Four</Button>}
</ButtonGroup>
</Fragment>
);
Expand Down
6 changes: 4 additions & 2 deletions packages/core/stories/components/menu/Menu.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,10 @@ export const Minimal: MenuStory = {
appendLabel="NEW"
/>
<MenuItem id="newItem" label="New Menu Item" badge={45} />
<MenuItem id="collapsable1" label="Collapsable Item" disabled />
<MenuItem id="collapsable2" label="Collapsable Item" active />
<Fragment>
<MenuItem id="collapsable1" label="Collapsable Item" disabled />
<MenuItem id="collapsable2" label="Collapsable Item" active />
</Fragment>
<MenuItem id="sectionItem" label="Section Item" />
<Menu label="Floating Item">
<MenuItem id="inner1" label="Inner Item" />
Expand Down
5 changes: 2 additions & 3 deletions packages/form/src/input/ArrayInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@
import { DndContext, type DragEndEvent } from "@dnd-kit/core";
import { SortableContext, useSortable } from "@dnd-kit/sortable";
import { Button, CoreIcons, Icon, Tooltip } from "@react-fabric/core";
import { isString } from "@react-fabric/utilities";
import { cloneChildren, isString } from "@react-fabric/utilities";
import classNames from "classnames";
import {
Children,
cloneElement,
useCallback,
useImperativeHandle,
Expand Down Expand Up @@ -255,7 +254,7 @@ export const ArrayInput = ({
index,
name: `${name}.${index}`,
})
: Children.map(children, (child: AnyObject) =>
: cloneChildren(children, (child: AnyObject) =>
cloneElement(
child,
Object.assign(
Expand Down
14 changes: 6 additions & 8 deletions packages/form/src/input/Field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ import {
type ChildrenProp,
type CssProp,
} from "@react-fabric/core/dist/types/types";
import { cloneChildren } from "@react-fabric/utilities";
import classNames from "classnames";
import { Children, cloneElement } from "react";
import { cloneElement } from "react";
import classes from "../internal/Field.module.css";
import { FieldWrapper } from "../internal/FieldWrapper";

Expand Down Expand Up @@ -90,13 +91,10 @@ export const Field = ({
!vertical && "flex flex-nowrap",
)}
>
{Children.map(
children,
(child) =>
child &&
cloneElement(child as AnyObject, {
"data-inner": true,
}),
{cloneChildren(children, (child: AnyObject) =>
cloneElement(child, {
"data-inner": true,
}),
)}
</div>
</FieldWrapper>
Expand Down
7 changes: 4 additions & 3 deletions packages/form/src/input/RadioGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import {
type ChildrenProp,
type CssProp,
} from "@react-fabric/core/dist/types/types";
import { Children, cloneElement } from "react";
import { cloneChildren } from "@react-fabric/utilities";
import { cloneElement } from "react";
import { FieldWrapper } from "../internal/FieldWrapper";
import { type Radio } from "./Radio";

Expand Down Expand Up @@ -84,8 +85,8 @@ export const RadioGroup = ({
return (
<FieldWrapper {...rest}>
<div className={className}>
{Children.map(children, (child) =>
cloneElement(child as AnyObject, {
{cloneChildren(children, (child: AnyObject) =>
cloneElement(child, {
name,
onChange,
width: optionWidth,
Expand Down
42 changes: 42 additions & 0 deletions packages/utilities/src/_cloneElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* React Fabric
* @version: 1.0.0
*
*
* The MIT License (MIT)
* Copyright (c) 2024 Adarsh Pastakia
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

import { type ReactNode, Children, isValidElement } from "react";
import { Fragment } from "react/jsx-runtime";

export const cloneChildren = (
children: ReactNode,
callback: (child: ReactNode, index: number) => ReactNode,
index = 0,
): ReactNode => {
return Children.map(children, (c: ReactNode) => {
if (isValidElement(c)) {
if (c.type === Fragment) {
// just compare to `Fragment`
return cloneChildren(c.props?.children, callback);
}
return callback(c, index++);
}
return c;
});
};
1 change: 1 addition & 0 deletions packages/utilities/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
export * from "./_ascii";
export * from "./_boundingBox";
export { getBoundingBox, getBox } from "./_boundingBox";
export * from "./_cloneElement";
export { default as Countries } from "./_countries";
export type { Country } from "./_countries";
export * from "./_debounce";
Expand Down

0 comments on commit 7741faa

Please sign in to comment.