From eb2af642ed12c0afc893d0e630ec349c442bf2f6 Mon Sep 17 00:00:00 2001 From: HichemTab Date: Sat, 26 Oct 2024 22:02:34 +0100 Subject: [PATCH 1/4] Implement deep merge for specific props in page responses Add support for deep merging specified properties in page responses by introducing `deepMergeProps`. This ensures that nested objects and arrays are correctly merged instead of being overwritten, enhancing the flexibility and consistency of data handling. --- packages/core/src/response.ts | 26 ++++++++++++++++++++++++++ packages/core/src/types.ts | 1 + 2 files changed, 27 insertions(+) diff --git a/packages/core/src/response.ts b/packages/core/src/response.ts index 4b905b666..38eac20da 100644 --- a/packages/core/src/response.ts +++ b/packages/core/src/response.ts @@ -239,6 +239,7 @@ export class Response { protected mergeProps(pageResponse: Page): void { if (this.requestParams.isPartial() && pageResponse.component === currentPage.get().component) { const propsToMerge = pageResponse.mergeProps || [] + const propsToDeepMerge = pageResponse.deepMergeProps || [] propsToMerge.forEach((prop) => { const incomingProp = pageResponse.props[prop] @@ -253,6 +254,31 @@ export class Response { } }) + propsToDeepMerge.forEach((prop) => { + const incomingProp = pageResponse.props[prop]; + const currentProp = currentPage.get().props[prop]; + + // Deep merge function to handle nested objects and arrays + const deepMerge = (target: any, source: any) => { + if (Array.isArray(source)) { + // Merge arrays by concatenating the existing and incoming elements + return [...(Array.isArray(target) ? target : []), ...source]; + } else if (typeof source === 'object' && source !== null) { + // Merge objects by iterating over keys + return Object.keys(source).reduce((acc, key) => { + acc[key] = deepMerge(target ? target[key] : undefined, source[key]); + return acc; + }, { ...target }); + } + // If the source is neither an array nor an object, return it directly + return source; + }; + + // Assign the deeply merged result back to props + pageResponse.props[prop] = deepMerge(currentProp, incomingProp); + }); + + pageResponse.props = { ...currentPage.get().props, ...pageResponse.props } } } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 8bec60465..02529889e 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -42,6 +42,7 @@ export interface Page { encryptHistory: boolean deferredProps?: Record mergeProps?: string[] + deepMergeProps?: string[] /** @internal */ scrollRegions: Array<{ top: number; left: number }> From 73a552401531fe8332b229d9a6d882a4fdcf9c8b Mon Sep 17 00:00:00 2001 From: HichemTab Date: Sat, 26 Oct 2024 22:18:44 +0100 Subject: [PATCH 2/4] Add deep merge props functionality and tests This commit introduces the deepMergeProps feature in the Vue3 application, allowing for more comprehensive property merging. A corresponding test has been created to ensure the functionality operates as expected. Additionally, server routing is updated to support deepMergeProps. --- packages/vue3/src/app.ts | 1 + tests/app/server.js | 27 +++++++++++++++++++-------- tests/deep-merge-props.spec.ts | 29 +++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 tests/deep-merge-props.spec.ts diff --git a/packages/vue3/src/app.ts b/packages/vue3/src/app.ts index ea40fbb11..ce4ef01f6 100755 --- a/packages/vue3/src/app.ts +++ b/packages/vue3/src/app.ts @@ -135,6 +135,7 @@ export function usePage(): Page { clearHistory: computed(() => page.value?.clearHistory), deferredProps: computed(() => page.value?.deferredProps), mergeProps: computed(() => page.value?.mergeProps), + deepMergeProps: computed(() => page.value?.deepMergeProps), scrollRegions: computed(() => page.value?.scrollRegions), rememberedState: computed(() => page.value?.rememberedState), encryptHistory: computed(() => page.value?.encryptHistory), diff --git a/tests/app/server.js b/tests/app/server.js index 213ccca6c..a84e3b3e6 100644 --- a/tests/app/server.js +++ b/tests/app/server.js @@ -237,14 +237,25 @@ app.get('/when-visible', (req, res) => { }) app.get('/merge-props', (req, res) => { - inertia.render(req, res, { - component: 'MergeProps', - props: { - bar: new Array(5).fill(1), - foo: new Array(5).fill(1), - }, - ...(req.headers['x-inertia-reset'] ? {} : { mergeProps: ['foo'] }), - }) + inertia.render(req, res, { + component: 'MergeProps', + props: { + bar: new Array(5).fill(1), + foo: new Array(5).fill(1), + }, + ...(req.headers['x-inertia-reset'] ? {} : { mergeProps: ['foo'] }), + }) +}) + +app.get('/deep-merge-props', (req, res) => { + inertia.render(req, res, { + component: 'DeepMergeProps', + props: { + bar: new Array(5).fill(1), + foo: new Array(5).fill(1), + }, + ...(req.headers['x-inertia-reset'] ? {} : { deepMergeProps: ['foo'] }), + }) }) app.get('/deferred-props/page-1', (req, res) => { diff --git a/tests/deep-merge-props.spec.ts b/tests/deep-merge-props.spec.ts new file mode 100644 index 000000000..40217ca39 --- /dev/null +++ b/tests/deep-merge-props.spec.ts @@ -0,0 +1,29 @@ +import { expect, test } from '@playwright/test' +import { clickAndWaitForResponse } from './support' + +test('can deep merge props', async ({ page }) => { + await page.goto('/deep-merge-props') + + await expect(page.getByText('bar count is 5')).toBeVisible() + await expect(page.getByText('foo count is 5')).toBeVisible() + + await clickAndWaitForResponse(page, 'Reload', null, 'button') + + await expect(page.getByText('bar count is 5')).toBeVisible() + await expect(page.getByText('foo count is 10')).toBeVisible() + + await clickAndWaitForResponse(page, 'Reload', null, 'button') + + await expect(page.getByText('bar count is 5')).toBeVisible() + await expect(page.getByText('foo count is 15')).toBeVisible() + + await clickAndWaitForResponse(page, 'Get Fresh', null, 'button') + + await expect(page.getByText('bar count is 5')).toBeVisible() + await expect(page.getByText('foo count is 5')).toBeVisible() + + await clickAndWaitForResponse(page, 'Reload', null, 'button') + + await expect(page.getByText('bar count is 5')).toBeVisible() + await expect(page.getByText('foo count is 10')).toBeVisible() +}) From abab1369a8cb56557a6b9e6f7efc91d013f43adf Mon Sep 17 00:00:00 2001 From: HichemTab Date: Sat, 26 Oct 2024 23:09:24 +0100 Subject: [PATCH 3/4] Rename component in deep-merge-props route Changed the component from 'DeepMergeProps' to 'MergeProps' in the /deep-merge-props route handler. This aligns the route's component usage with the updated naming conventions in the project. --- tests/app/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/app/server.js b/tests/app/server.js index a84e3b3e6..2a532d356 100644 --- a/tests/app/server.js +++ b/tests/app/server.js @@ -249,7 +249,7 @@ app.get('/merge-props', (req, res) => { app.get('/deep-merge-props', (req, res) => { inertia.render(req, res, { - component: 'DeepMergeProps', + component: 'MergeProps', props: { bar: new Array(5).fill(1), foo: new Array(5).fill(1), From 916172317e76e662f59f38add27e0ca49f6e461f Mon Sep 17 00:00:00 2001 From: HichemTab Date: Sat, 26 Oct 2024 23:28:02 +0100 Subject: [PATCH 4/4] Rename component in deep-merge-props route Changed the component from 'DeepMergeProps' to 'MergeProps' in the /deep-merge-props route handler. This aligns the route's component usage with the updated naming conventions in the project. --- packages/core/src/response.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/response.ts b/packages/core/src/response.ts index 38eac20da..2df6afbfb 100644 --- a/packages/core/src/response.ts +++ b/packages/core/src/response.ts @@ -274,7 +274,7 @@ export class Response { return source; }; - // Assign the deeply merged result back to props + // Assign the deeply merged result back to props. pageResponse.props[prop] = deepMerge(currentProp, incomingProp); });