Skip to content

Commit 972fc07

Browse files
committed
feat(mergeProps): utility to merge component props and global config
1 parent 2880228 commit 972fc07

File tree

4 files changed

+145
-0
lines changed

4 files changed

+145
-0
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
]
3939
},
4040
"dependencies": {
41+
"classnames": "^2.5.1",
42+
"is-plain-object": "^5.0.0",
4143
"react-is": "^18.2.0"
4244
},
4345
"devDependencies": {

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export { default as useEvent } from './hooks/useEvent';
22
export { default as useMergedState } from './hooks/useMergedState';
3+
export { default as mergeProps } from './mergeProps';
34
export { supportNodeRef, supportRef, useComposeRef } from './ref';
45
export { default as get } from './utils/get';
56
export { default as set } from './utils/set';

src/mergeProps.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import classNames from 'classnames';
2+
import { isPlainObject } from 'is-plain-object';
3+
4+
function mergeClassNames<T>(classNamesA: T, classNamesB: T): T {
5+
const result = { ...classNamesA };
6+
Object.keys(classNamesB).forEach(key => {
7+
result[key] = mergeClassName(classNamesA[key], classNamesB[key]);
8+
});
9+
return result;
10+
}
11+
12+
function mergeClassName(classNameA?: any, classNameB?: any): string {
13+
if (typeof classNameA === 'object' || typeof classNameB === 'object') {
14+
return mergeClassNames(classNameA, classNameB);
15+
}
16+
17+
return classNames(classNameA, classNameB);
18+
}
19+
20+
export default function mergeProps<T>(...list: T[]): T {
21+
if (list.length > 2) {
22+
return mergeProps(list[0], mergeProps(...list.slice(1)));
23+
}
24+
const result: T = { ...list[0] };
25+
list[1] &&
26+
Object.keys(list[1]).forEach(key => {
27+
if (key === 'className') {
28+
result[key] = classNames(result[key], list[1][key]);
29+
} else if (key === 'classNames') {
30+
result[key] = mergeClassNames(result[key], list[1][key]);
31+
} else if (isPlainObject(list[1][key])) {
32+
result[key] = mergeProps(result[key], list[1][key]);
33+
} else {
34+
result[key] = list[1][key] ?? result[key];
35+
}
36+
});
37+
return result;
38+
}

tests/mergeProps.test.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import mergeProps from '../src/mergeProps';
2+
3+
test('merge className', () => {
4+
expect(mergeProps({ className: 'foo' }, { className: 'bar' })).toEqual({
5+
className: 'foo bar',
6+
});
7+
});
8+
9+
test('merge classNames', () => {
10+
expect(
11+
mergeProps(
12+
{
13+
classNames: {
14+
body: 'bam',
15+
footer: 'foo',
16+
},
17+
},
18+
{
19+
classNames: {
20+
footer: 'bar',
21+
header: 'boo',
22+
},
23+
},
24+
),
25+
).toEqual({
26+
classNames: {
27+
body: 'bam',
28+
footer: 'foo bar',
29+
header: 'boo',
30+
},
31+
});
32+
});
33+
34+
test('merge style', () => {
35+
expect(
36+
mergeProps(
37+
{
38+
style: {
39+
background: '#000',
40+
color: '#666',
41+
},
42+
},
43+
{
44+
style: {
45+
background: '#fff',
46+
},
47+
},
48+
),
49+
).toEqual({
50+
style: {
51+
background: '#fff',
52+
color: '#666',
53+
},
54+
});
55+
});
56+
57+
test('merge boolean prop', () => {
58+
expect(
59+
mergeProps(
60+
{
61+
disabled: true,
62+
loading: false,
63+
},
64+
{
65+
disabled: false,
66+
},
67+
),
68+
).toEqual({
69+
disabled: false,
70+
loading: false,
71+
});
72+
});
73+
74+
test('merge number prop', () => {
75+
expect(
76+
mergeProps(
77+
{
78+
value: 1,
79+
},
80+
{
81+
value: 2,
82+
},
83+
),
84+
).toEqual({
85+
value: 2,
86+
});
87+
});
88+
89+
test('merge non-plain object prop', () => {
90+
const dateObj = new Date();
91+
const urlObj = new URL('https://example.com/');
92+
expect(
93+
mergeProps(
94+
{
95+
value: dateObj,
96+
},
97+
{
98+
value: urlObj,
99+
},
100+
),
101+
).toEqual({
102+
value: urlObj,
103+
});
104+
});

0 commit comments

Comments
 (0)