From df72d02a90084711f223f59bfe762c53b32274c0 Mon Sep 17 00:00:00 2001 From: Alex Kholodniak Date: Sat, 22 Jun 2024 19:04:26 -0500 Subject: [PATCH] Add data-sg-replace functionality --- superglue/lib/action_creators/requests.ts | 4 +- superglue/lib/utils/ujs.ts | 13 ++++- superglue/spec/lib/utils/ujs.spec.js | 66 +++++++++++++++++++---- 3 files changed, 71 insertions(+), 12 deletions(-) diff --git a/superglue/lib/action_creators/requests.ts b/superglue/lib/action_creators/requests.ts index 13d8ad4a..15791293 100644 --- a/superglue/lib/action_creators/requests.ts +++ b/superglue/lib/action_creators/requests.ts @@ -152,6 +152,7 @@ export function visit( placeholderKey, beforeSave = (prevPage, receivedPage) => receivedPage, revisit = false, + suggestedAction = 'push', } = {} ) { path = withoutBusters(path) @@ -207,7 +208,8 @@ export function visit( fetchArgs, } - meta.suggestedAction = 'push' + meta.suggestedAction = suggestedAction + if (!rsp.redirected && fetchArgs[1].method != 'GET') { meta.suggestedAction = 'replace' } diff --git a/superglue/lib/utils/ujs.ts b/superglue/lib/utils/ujs.ts index 93cfd2c1..7dadf69d 100644 --- a/superglue/lib/utils/ujs.ts +++ b/superglue/lib/utils/ujs.ts @@ -48,8 +48,11 @@ export class HandlerBuilder { const hasRemote = !!node.getAttribute( this.attributePrefix + '-remote' ) + const hasReplace = !!node.getAttribute( + this.attributePrefix + '-replace' + ) - return hasVisit || hasRemote + return hasVisit || hasRemote || hasReplace } handleSubmit(event) { @@ -92,6 +95,7 @@ export class HandlerBuilder { visitOrRemote(linkOrForm, url, opts: any = {}) { let target + let suggestedAction = 'push' if (linkOrForm.getAttribute(this.attributePrefix + '-visit')) { target = this.visit @@ -107,10 +111,15 @@ export class HandlerBuilder { target = this.remote } + if (linkOrForm.getAttribute(this.attributePrefix + '-replace')) { + target = this.visit + suggestedAction = 'replace' + } + if (!target) { return } else { - return target(url, opts) + return target(url, { ...opts, suggestedAction }) } } diff --git a/superglue/spec/lib/utils/ujs.spec.js b/superglue/spec/lib/utils/ujs.spec.js index 7e76bb45..319e8fa9 100644 --- a/superglue/spec/lib/utils/ujs.spec.js +++ b/superglue/spec/lib/utils/ujs.spec.js @@ -68,6 +68,25 @@ describe('ujs', () => { } } + function createFakeReplaceEvent() { + return { + preventDefault: () => {}, + target: { + nodeName: 'A', + parentNode: 'DIV', + href: '/foo', + getAttribute: (attr) => { + if(attr === 'href') { + return '/foo' + } + if(attr === 'data-replace') { + return true + } + } + } + } + } + describe('onClick', () => { it('calls visit on a valid link', () => { const ujsAttributePrefix = 'data' @@ -89,7 +108,7 @@ describe('ujs', () => { const {onClick} = builder.handlers() onClick(createFakeEvent()) - expect(visit).toHaveBeenCalledWith('/foo', {method: 'GET'}) + expect(visit).toHaveBeenCalledWith('/foo', {method: 'GET', suggestedAction: 'push'}) }) it('calls visit with a placeholder when props_at is present on a valid link', () => { @@ -120,7 +139,11 @@ describe('ujs', () => { const {onClick} = builder.handlers() onClick(createFakeVisitGraftEvent()) - expect(visit).toHaveBeenCalledWith('/foo?props_at=data.hello', {method: 'GET', placeholderKey: '/current'}) + expect(visit).toHaveBeenCalledWith('/foo?props_at=data.hello', { + method: 'GET', + placeholderKey: '/current', + suggestedAction: 'push', + }) }) it('calls remote if a link is enabled with remote', () => { @@ -143,10 +166,33 @@ describe('ujs', () => { const {onClick} = builder.handlers() onClick(createFakeRemoteEvent()) - expect(remote).toHaveBeenCalledWith('/foo', {method: 'GET'}) + expect(remote).toHaveBeenCalledWith('/foo', {method: 'GET', suggestedAction: 'push'}) + }) + + it('calls visit with replace action if link has data-replace attribute', () => { + const ujsAttributePrefix = 'data' + const visit = jest.fn() + const navigatorRef = { + current: { + navigateTo: () => {} + } + } + const store = {} + + const builder = new HandlerBuilder({ + ujsAttributePrefix, + store, + visit, + navigatorRef + }) + + const {onClick} = builder.handlers() + onClick(createFakeReplaceEvent()) + + expect(visit).toHaveBeenCalledWith('/foo', {method: 'GET', suggestedAction: 'replace'}) }) - it('does not call visit on an link does not have the visit attribute data-visit', () => { + it('does not call visit on a link that does not have the visit attribute data-visit', () => { const store = {} const ujsAttributePrefix = 'data' const visit = jest.fn() @@ -176,7 +222,7 @@ describe('ujs', () => { expect(visit).not.toHaveBeenCalledWith('/foo', {}) }) - it('does not call visit on an non-standard link', () => { + it('does not call visit on a non-standard link', () => { const store = {} const ujsAttributePrefix = 'data' const visit = jest.fn() @@ -227,7 +273,7 @@ describe('ujs', () => { fakeEvent = createFakeEvent() onClick(fakeEvent) - expect(visit).toHaveBeenCalledWith('/foo', {method: 'GET'}) + expect(visit).toHaveBeenCalledWith('/foo', {method: 'GET', suggestedAction: 'push'}) }) }) @@ -274,7 +320,7 @@ describe('ujs', () => { } } - it('succssfully posts a form with a visit attribute', () => { + it('successfully posts a form with a visit attribute', () => { const store = {} const ujsAttributePrefix = 'data' const visit = jest.fn() @@ -303,11 +349,12 @@ describe('ujs', () => { headers: { "content-type": null, }, + suggestedAction: 'push', body: {some: 'Body'} }) }) - it('succssfully posts a form with a remote attribut', () => { + it('successfully posts a form with a remote attribute', () => { const store = {} const ujsAttributePrefix = 'data' const remote = jest.fn() @@ -336,11 +383,12 @@ describe('ujs', () => { headers: { "content-type": null, }, + suggestedAction: 'push', body: {some: 'Body'} }) }) - it('does not posts a form without a visit attribute', () => { + it('does not post a form without a visit attribute', () => { const store = {} const ujsAttributePrefix = 'data' const visit = jest.fn()