Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ export interface TriggerRef {
// New version will not wrap popup with `rc-trigger-popup-content` when multiple children

export interface TriggerProps {
children: React.ReactElement<any>;
children:
| React.ReactElement<any>
| ((info: { open: boolean }) => React.ReactElement<any>);
action?: ActionType | ActionType[];
showAction?: ActionType[];
hideAction?: ActionType[];
Expand Down Expand Up @@ -261,9 +263,6 @@ export function generateTrigger(
}
});

// ========================== Children ==========================
const child = React.Children.only(children);
const originChildProps = child?.props || {};
const cloneProps: Pick<
React.HTMLAttributes<HTMLElement>,
| 'onClick'
Expand Down Expand Up @@ -311,6 +310,17 @@ export function generateTrigger(
// Render still use props as first priority
const mergedOpen = popupVisible ?? internalOpen;

// ========================== Children ==========================
const child = React.useMemo(() => {
const nextChild =
typeof children === 'function'
? children({ open: mergedOpen })
: children;
return React.Children.only(nextChild);
}, [children, mergedOpen]);

const originChildProps = child?.props || {};

// We use effect sync here in case `popupVisible` back to `undefined`
const setMergedOpen = useEvent((nextOpen: boolean) => {
if (openUncontrolled) {
Expand Down
16 changes: 16 additions & 0 deletions tests/basic.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,22 @@ describe('Trigger.Basic', () => {
});
});

describe('children renderProps', () => {
it('should get current open', () => {
const { container } = render(
<Trigger
popupVisible={true}
popup={<span>Hello!</span>}
>
{({ open }) => <button>{String(open)}</button>}
</Trigger>,
);

const button = container.querySelector('button');
expect(button.textContent).toBe('true');
});
});
Comment on lines +371 to +385
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The added test case is a good start for verifying the render prop functionality. To make the tests more comprehensive, I suggest covering both controlled and uncontrolled component scenarios, as well as state transitions from open to close and vice-versa. This will ensure the open state is correctly propagated to the child in all situations.

  describe('children renderProps', () => {
    it('should get current open state and update for controlled component', () => {
      const { container, rerender } = render(
        <Trigger popupVisible={false} popup={<span>Hello!</span>}>
          {({ open }) => <button>{String(open)}</button>}
        </Trigger>,
      );

      const button = container.querySelector('button');
      expect(button.textContent).toBe('false');

      rerender(
        <Trigger popupVisible={true} popup={<span>Hello!</span>}>
          {({ open }) => <button>{String(open)}</button>}
        </Trigger>,
      );

      expect(button.textContent).toBe('true');
    });

    it('should update with action for uncontrolled component', () => {
      const { container } = render(
        <Trigger action={['click']} popup={<span>Hello!</span>}>
          {({ open }) => <button>{String(open)}</button>}
        </Trigger>,
      );

      const button = container.querySelector('button');
      expect(button.textContent).toBe('false');

      fireEvent.click(button);
      act(() => jest.runAllTimers());

      expect(button.textContent).toBe('true');

      fireEvent.click(button);
      act(() => jest.runAllTimers());

      expect(button.textContent).toBe('false');
    });
  });


describe('destroyPopupOnHide', () => {
it('defaults to false', () => {
const { container } = render(
Expand Down
Loading