Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow arbitrary number of prop objects to be passed to mergeProps #776

Merged
merged 8 commits into from
Jul 18, 2020
Merged

Allow arbitrary number of prop objects to be passed to mergeProps #776

merged 8 commits into from
Jul 18, 2020

Conversation

brookslybrand
Copy link
Contributor

Currently mergeProps only allows 2 arguments to be passed in at a time, which results in extra nesting whenever there are more than 2 prop objects that need to be merged, as seen in this example.

So instead of:

let props = mergeProps(mergeProps(buttonProps, hoverProps), focusProps)

You can just do:

let props = mergeProps(buttonProps, hoverProps, focusProps)

@devongovett mentioned on twitter that the team ran into issues with TypeScript handling an arbitrary number.

I was able to find a solution on stackoverflow that helped me solve this, which I cited in the source code.

Please let me know if any more testing needs to be done or if there are any other steps I should take on this PR.

@devongovett
Copy link
Member

Wow, awesome! Thanks for tackling this.

res[key] = classNames(a.UNSAFE_className, b.UNSAFE_className);
export function mergeProps<T extends Props, U extends Props[]>(
a: T,
...rest: U
Copy link
Member

Choose a reason for hiding this comment

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

Does this mean all of the subsequent props objects after the first one must be the same type, or does it allow differences as long as they are all compatible with Props? If so, do we still need to treat a separately or would it make sense to make them all part of the same rest parameter?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They can all be different. I probably should have added a test that showed that, but here's sandbox I have up with an example: https://codesandbox.io/s/mergeprops-irpn6?file=/src/index.ts

At first I did just spread all of the args, but then I separated a out just to start off the result variable a little more easily, but I suppose that's not actually super necessary. Maybe it would be better to switch it to just being ...rest or ...props or something.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alright, I went ahead and got rid of a and just have ...args passed in. I also added another test to show that it will allow you to add other props in the later objects and it will merge them altogether.

let c = {onClick: () => console.log(message3)};
let mergedProps = mergeProps(a, b, c);
mergedProps.onClick();
expect(console.log).toHaveBeenCalledWith(message1);
Copy link
Member

Choose a reason for hiding this comment

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

Would you mind verifying the order they are called in too? We don't want to accidentally change that on people

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@snowystinger changed to toHaveBeenNthCalledWith

Copy link
Member

Choose a reason for hiding this comment

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

Thanks!

it('combines classNames', function () {
let className1 = 'primary';
let className2 = 'hover';
let className3 = 'hover';
Copy link
Member

@snowystinger snowystinger Jul 17, 2020

Choose a reason for hiding this comment

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

this raises an interesting point, should we be deduping classnames?
there's nothing particularly wrong with multiple of the same that I'm aware of, so I'm inclined to leave it alone
edit: quick search of className, i don't think it does it for us

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, that's funny, this was actually a mistake in my test, but you're right, I think just letting the classnames library do what it does is probably the best move.

});

it('combines callbacks', function () {
console.log = jest.fn();
Copy link
Member

@snowystinger snowystinger Jul 17, 2020

Choose a reason for hiding this comment

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

bit of a weird question, this is a global we're assigning a mock function to. I have always cleaned that up in an afterEach, but that may just be me being careful. Do you know if this will get cleaned up after this it?

Copy link
Member

@snowystinger snowystinger Jul 17, 2020

Choose a reason for hiding this comment

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

actually, lets not do this with console.log at all, lets just make a function we can call. That way we don't obscure console messages during this suite unnecessarily

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh yeah, that makes a lot of sense. Alright, cleaned up all of those

Comment on lines 30 to 43
console.log = jest.fn();
let message1 = 'click1';
let message2 = 'click2';
let message3 = 'click3';
let mergedProps = mergeProps(
{onClick: () => console.log(message1)},
{onClick: () => console.log(message2)},
{onClick: () => console.log(message3)}
);
mergedProps.onClick();
expect(console.log).toHaveBeenNthCalledWith(1, message1);
expect(console.log).toHaveBeenNthCalledWith(2, message2);
expect(console.log).toHaveBeenNthCalledWith(3, message3);
expect(console.log).toHaveBeenCalledTimes(3);
Copy link
Member

Choose a reason for hiding this comment

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

    let mockFn = jest.fn();
    let message1 = 'click1';
    let message2 = 'click2';
    let message3 = 'click3';
    let mergedProps = mergeProps(
      {onClick: () => mockFn(message1)},
      {onClick: () => mockFn(message2)},
      {onClick: () => mockFn(message3)}
    );
    mergedProps.onClick();
    expect(mockFn).toHaveBeenNthCalledWith(1, message1);
    expect(mockFn).toHaveBeenNthCalledWith(2, message2);
    expect(mockFn).toHaveBeenNthCalledWith(3, message3);
    expect(mockFn).toHaveBeenCalledTimes(3);

});

it('merges props with different keys', function () {
console.log = jest.fn();
Copy link
Member

Choose a reason for hiding this comment

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

same thing with this test, lets not overwrite console.log

snowystinger
snowystinger previously approved these changes Jul 18, 2020
@devongovett devongovett merged commit 87324e9 into adobe:main Jul 18, 2020
@devongovett
Copy link
Member

Thank you! 😍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants