Skip to content

Commit 9cd660c

Browse files
authored
fix(theme): merge optional params (#256)
Allow the option to merge optional partial params on a partial `Theme`. fix #253
1 parent fc6640d commit 9cd660c

File tree

3 files changed

+97
-10
lines changed

3 files changed

+97
-10
lines changed

src/lib/themes/theme.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,5 +224,5 @@ export function mergeWithDefaultAnnotationRect(config?: Partial<RectAnnotationSt
224224
}
225225

226226
export function mergeWithDefaultTheme(theme: PartialTheme, defaultTheme: Theme = LIGHT_THEME): Theme {
227-
return mergePartial(defaultTheme, theme);
227+
return mergePartial(defaultTheme, theme, { mergeOptionalPartialValues: true });
228228
}

src/lib/utils/commons.test.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,5 +134,80 @@ describe('commons utilities', () => {
134134
mergePartial(base, partial);
135135
expect(base).toEqual(baseClone);
136136
});
137+
138+
describe('MergeOptions', () => {
139+
describe('mergeOptionalPartialValues', () => {
140+
interface OptionalTestType {
141+
value1: string;
142+
value2?: number;
143+
value3: string;
144+
value4?: OptionalTestType;
145+
}
146+
const defaultBase: OptionalTestType = {
147+
value1: 'foo',
148+
value3: 'bar',
149+
value4: {
150+
value1: 'foo',
151+
value3: 'bar',
152+
},
153+
};
154+
const partial1: RecursivePartial<OptionalTestType> = { value1: 'baz', value2: 10 };
155+
const partial2: RecursivePartial<OptionalTestType> = { value1: 'baz', value4: { value2: 10 } };
156+
157+
describe('mergeOptionalPartialValues is true', () => {
158+
test('should merge optional parameters', () => {
159+
const merged = mergePartial(defaultBase, partial1, { mergeOptionalPartialValues: true });
160+
expect(merged).toEqual({
161+
value1: 'baz',
162+
value2: 10,
163+
value3: 'bar',
164+
value4: {
165+
value1: 'foo',
166+
value3: 'bar',
167+
},
168+
});
169+
});
170+
171+
test('should merge nested optional parameters', () => {
172+
const merged = mergePartial(defaultBase, partial2, { mergeOptionalPartialValues: true });
173+
expect(merged).toEqual({
174+
value1: 'baz',
175+
value3: 'bar',
176+
value4: {
177+
value1: 'foo',
178+
value2: 10,
179+
value3: 'bar',
180+
},
181+
});
182+
});
183+
});
184+
185+
describe('mergeOptionalPartialValues is false', () => {
186+
test('should NOT merge optional parameters', () => {
187+
const merged = mergePartial(defaultBase, partial1, { mergeOptionalPartialValues: false });
188+
expect(merged).toEqual({
189+
value1: 'baz',
190+
value3: 'bar',
191+
value4: {
192+
value1: 'foo',
193+
value3: 'bar',
194+
},
195+
});
196+
});
197+
198+
test('should NOT merge nested optional parameters', () => {
199+
const merged = mergePartial(defaultBase, partial2, { mergeOptionalPartialValues: false });
200+
expect(merged).toEqual({
201+
value1: 'baz',
202+
value3: 'bar',
203+
value4: {
204+
value1: 'foo',
205+
value3: 'bar',
206+
},
207+
});
208+
});
209+
});
210+
});
211+
});
137212
});
138213
});

src/lib/utils/commons.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ export type RecursivePartial<T> = {
4242
: RecursivePartial<T[P]>
4343
};
4444

45+
export interface MergeOptions {
46+
mergeOptionalPartialValues?: boolean;
47+
}
48+
4549
/**
4650
* Merges values of a partial structure with a base structure.
4751
*
@@ -50,18 +54,26 @@ export type RecursivePartial<T> = {
5054
*
5155
* @returns new base structure with updated partial values
5256
*/
53-
export function mergePartial<T>(base: T, partial?: RecursivePartial<T>): T {
57+
export function mergePartial<T>(base: T, partial?: RecursivePartial<T>, options: MergeOptions = {}): T {
5458
if (Array.isArray(base)) {
5559
return partial ? (partial as T) : base; // No nested array merging
5660
} else if (typeof base === 'object') {
57-
return Object.keys(base).reduce(
58-
(newBase, key) => {
59-
// @ts-ignore
60-
newBase[key] = mergePartial(base[key], partial && partial[key]);
61-
return newBase;
62-
},
63-
{ ...base },
64-
);
61+
const baseClone = { ...base };
62+
63+
if (partial && options.mergeOptionalPartialValues) {
64+
Object.keys(partial).forEach((key) => {
65+
if (!(key in baseClone)) {
66+
// @ts-ignore
67+
baseClone[key] = partial[key];
68+
}
69+
});
70+
}
71+
72+
return Object.keys(base).reduce((newBase, key) => {
73+
// @ts-ignore
74+
newBase[key] = mergePartial(base[key], partial && partial[key], options);
75+
return newBase;
76+
}, baseClone);
6577
}
6678

6779
return partial !== undefined ? (partial as T) : base;

0 commit comments

Comments
 (0)