Skip to content

Commit

Permalink
feat(navrequest): add support for nav request that replaces session h…
Browse files Browse the repository at this point in the history
…istory

COMUI-1328
  • Loading branch information
thomas-c-dillon committed Nov 17, 2022
1 parent efc7467 commit 6836462
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 40 deletions.
16 changes: 8 additions & 8 deletions apps/ifc-example-client/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ document.getElementById('path').innerHTML = window.location.hash;

window.onhashchange = function() {
document.getElementById('path').innerHTML = window.location.hash;
document.getElementById('urlFromClientPath').innerHTML = iframeClient.urlFromClientPath(
window.location.hash
);
document.getElementById(
'urlFromClientPath'
).innerHTML = iframeClient.urlFromClientPath(window.location.hash);
};

/**** SET UP THE IFRAME CLIENT LIBRARY ****/
Expand All @@ -24,7 +24,7 @@ let iframeClient = new Client({
hostOrigin: `http://${window.location.hostname}:3000`
});

iframeClient.registerCustomElements();
iframeClient.registerCustomElements();

// Add a listener that will handled config data passed from the host to the
// client at startup.
Expand All @@ -38,9 +38,9 @@ iframeClient.addListener('environmentalData', envData => {
console.log(
`Got locale from host. Current date formatted for ${envData.locale} is: ${localizedDate}`
);
document.getElementById('urlFromClientPath').innerHTML = iframeClient.urlFromClientPath(
window.location.hash
);
document.getElementById(
'urlFromClientPath'
).innerHTML = iframeClient.urlFromClientPath(window.location.hash);
displayEnvData(envData);
});

Expand Down Expand Up @@ -98,7 +98,7 @@ function transformLinks() {
});
document.querySelectorAll('a.host-transform-link').forEach(link => {
link.href = iframeClient.urlFromHostPath(link.getAttribute('href'));
})
});
}

function displayEnvData(envData) {
Expand Down
9 changes: 8 additions & 1 deletion packages/iframe-coordinator-cli/src/views/IframeEmbed.vue
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,14 @@ export default {
handleNav(event) {
// TODO: detect and handle external URLs properly
const requestedUrl = new URL(event.detail.url);
window.location.hash = requestedUrl.hash;
if (location.hash === requestedUrl.hash) {
// The requested navigation is for the current location, do nothing
} else if (event.detail.history === 'replace') {
window.location.replace(requestedUrl.hash);
} else {
window.location.hash = requestedUrl.hash;
}
},
handleKeyEvent(event) {
this.$notify({
Expand Down
12 changes: 6 additions & 6 deletions packages/iframe-coordinator/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ export class Client {
if (event.data.protocol === API_PROTOCOL) {
throw new Error(
`
I recieved an invalid message from the host application. This is probably due
I received an invalid message from the host application. This is probably due
to a major version mismatch between client and host iframe-coordinator libraries.
`.trim() +
'\n' +
Expand Down Expand Up @@ -261,7 +261,7 @@ export class Client {
this._handleEnvironmentData(message);
return;
default:
// Only emit events which are specifically handeled
// Only emit events which are specifically handled
}
}

Expand Down Expand Up @@ -314,11 +314,11 @@ export class Client {
* @deprecated Use the new {@urlFromClientPath} method instead
*/
public asHostUrl(clientRouteLegacy: string): string {
const trimedClientRoute = stripLeadingSlashAndHashTag(clientRouteLegacy);
const trimmedClientRoute = stripLeadingSlashAndHashTag(clientRouteLegacy);
return joinRoutes(
this.environmentData.hostRootUrl,
this._assignedRoute || '',
trimedClientRoute
trimmedClientRoute
);
}

Expand Down Expand Up @@ -386,10 +386,10 @@ bad input into one of the iframe-coordinator client methods.
}

/**
* Accessor for the general-purpose pub-sub bus betwen client and host applications.
* Accessor for the general-purpose pub-sub bus between client and host applications.
* The content of messages on this bus are not defined by this API beyond a basic
* data wrapper of topic and payload. This is for application-specific messages
* agreed upon as a shared API betwen host and client.
* agreed upon as a shared API between host and client.
*/
public get messaging(): EventEmitter<Publication> {
return this._publishExposedEmitter;
Expand Down
2 changes: 1 addition & 1 deletion packages/iframe-coordinator/src/messages/ClientToHost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from './Publication';

/**
* All avaiable message types that can be sent
* All available message types that can be sent
* from the client content to the host application.
*/
export type ClientToHost =
Expand Down
4 changes: 2 additions & 2 deletions packages/iframe-coordinator/src/messages/Lifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,13 @@ export { startedDecoder, envDecoder };
* Handles new environmental data events. These should be configured
* before {@link Client.start} is called, as the host application
* will send the environment data immediately after the `Client.start`
* message is recieved.
* message is received.
*/
export type EnvDataHandler = (envData: EnvData) => void;

/**
* Helpful properties for working with lifecycle stages and
* their coresponding labeled messages.
* their corresponding labeled messages.
*/
export class Lifecycle {
/**
Expand Down
13 changes: 11 additions & 2 deletions packages/iframe-coordinator/src/messages/NavRequest.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { constant, Decoder, object, string } from 'decoders';
import { constant, Decoder, either, object, optional, string } from 'decoders';
import { labeledDecoder, LabeledMsg } from './LabeledMsg';

/**
Expand All @@ -7,6 +7,12 @@ import { labeledDecoder, LabeledMsg } from './LabeledMsg';
export interface NavRequest {
/** The URL the client wants the host application to navigate to */
url: string;
/** How the client wants the host to update the browser's session history.
* Push is the default behavior (adds a new session history entry).
* Replace alters the current entry so that using the back button
* after navigation will skip the location you are navigating from.
*/
history?: 'push' | 'replace';
}

/**
Expand All @@ -24,7 +30,10 @@ export interface LabeledNavRequest
const decoder: Decoder<LabeledNavRequest> = labeledDecoder(
constant<'navRequest'>('navRequest'),
object({
url: string
url: string,
history: optional(
either(constant<'push'>('push'), constant<'replace'>('replace'))
)
})
);

Expand Down
4 changes: 2 additions & 2 deletions packages/iframe-coordinator/src/messages/Publication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import {
import { labeledDecoder, LabeledMsg } from './LabeledMsg';

/**
* A pub-sub message for general-puropse messaging between hosts and clients.
* A pub-sub message for general-purpose messaging between hosts and clients.
*/
export interface Publication {
/**
* The topic to publish on. The host application must be subscribed to the topic
* in order to recieve the message.
* in order to receive the message.
*/
topic: string;
/** Data to publish */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ describe('ClientToHost', () => {
});
});

describe('validating navrequest type', () => {
describe('validating navRequest type', () => {
describe('when valid url is provided', () => {
const testMessage = {
msgType: 'navRequest',
Expand All @@ -258,6 +258,10 @@ describe('ClientToHost', () => {

const expectedMessage = {
...testMessage,
msg: {
...testMessage.msg,
history: undefined
},
protocol: 'iframe-coordinator',
version: 'unknown',
direction: undefined
Expand All @@ -273,7 +277,7 @@ describe('ClientToHost', () => {
});
});

describe('when invalid data is provided', () => {
describe('when invalid property is provided', () => {
const testMessage = {
msgType: 'navRequest',
msg: {
Expand All @@ -287,5 +291,47 @@ describe('ClientToHost', () => {
}).toThrow();
});
});

describe('when invalid history option is provided', () => {
const testMessage = {
msgType: 'navRequest',
msg: {
url: 'navRequest.url',
history: 'fail'
}
};

it('should return throw an exception', () => {
expect(() => {
validate(testMessage);
}).toThrow();
});
});

describe('when valid history option is provided', () => {
const testMessage = {
msgType: 'navRequest',
msg: {
url: 'navRequest.url',
history: 'push'
}
};

const expectedMessage = {
...testMessage,
protocol: 'iframe-coordinator',
version: 'unknown',
direction: undefined
} as LabeledNavRequest;

let testResult: ClientToHost;
beforeEach(() => {
testResult = validate(testMessage);
});

it('should return the validated message', () => {
expect(testResult).toEqual(expectedMessage);
});
});
});
});
32 changes: 16 additions & 16 deletions packages/iframe-coordinator/src/specs/client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ describe('client', () => {
});
});

describe('when an initial data environment is recieved', () => {
let recievedEnvData: EnvData;
describe('when an initial data environment is received', () => {
let receivedEnvData: EnvData;

const testEnvironmentData: SetupData = {
locale: 'nl-NL',
Expand All @@ -66,7 +66,7 @@ describe('client', () => {
};
beforeEach(() => {
client.addListener('environmentalData', (env: EnvData) => {
recievedEnvData = env;
receivedEnvData = env;
});
client.start();

Expand All @@ -81,7 +81,7 @@ describe('client', () => {

it('should delegate', () => {
const { assignedRoute, ...restEnvData } = testEnvironmentData;
expect(recievedEnvData).toEqual(restEnvData);
expect(receivedEnvData).toEqual(restEnvData);
});

describe('generate full URL from client path', () => {
Expand Down Expand Up @@ -221,7 +221,7 @@ describe('client', () => {
});
}).toThrowMatching(err => {
return err.message.startsWith(
'I recieved an invalid message from the host application'
'I received an invalid message from the host application'
);
});
expect(subscriptionCalled).toBe(false);
Expand All @@ -239,7 +239,7 @@ describe('client', () => {
});
}).toThrowMatching(err => {
return err.message.startsWith(
'I recieved an invalid message from the host application'
'I received an invalid message from the host application'
);
});
expect(subscriptionCalled).toBe(false);
Expand Down Expand Up @@ -277,14 +277,14 @@ describe('client', () => {
});
});

describe('when recieving an valid window message from the host application missing the direction field', () => {
describe('when receiving an valid window message from the host application missing the direction field', () => {
let publishCalls = 0;
let recievedPayload: string;
let receivedPayload: string;
beforeEach(() => {
client.start();
client.messaging.addListener('test.topic', (data: Publication) => {
publishCalls++;
recievedPayload = data.payload;
receivedPayload = data.payload;
});
mockFrameWindow.trigger('message', {
origin: 'origin',
Expand All @@ -300,18 +300,18 @@ describe('client', () => {

it('should raise a publish event for the topic', () => {
expect(publishCalls).toBe(1);
expect(recievedPayload).toBe('test data');
expect(receivedPayload).toBe('test data');
});
});

describe('when recieving an valid window message from the host application', () => {
describe('when receiving an valid window message from the host application', () => {
let publishCalls = 0;
let recievedPayload: string;
let receivedPayload: string;
beforeEach(() => {
client.start();
client.messaging.addListener('test.topic', (data: Publication) => {
publishCalls++;
recievedPayload = data.payload;
receivedPayload = data.payload;
});
mockFrameWindow.trigger('message', {
origin: 'origin',
Expand All @@ -328,7 +328,7 @@ describe('client', () => {

it('should raise a publish event for the topic', () => {
expect(publishCalls).toBe(1);
expect(recievedPayload).toBe('test data');
expect(receivedPayload).toBe('test data');
});
});

Expand Down Expand Up @@ -415,7 +415,7 @@ describe('client', () => {
expect(mockFrameWindow.parent.postMessage).toHaveBeenCalledWith(
applyClientProtocol({
msgType: 'navRequest',
msg: { url: 'http://www.example.com/' }
msg: { url: 'http://www.example.com/', history: undefined }
}),
'https://example.com'
);
Expand All @@ -442,7 +442,7 @@ describe('client', () => {
expect(mockFrameWindow.parent.postMessage).toHaveBeenCalledWith(
applyClientProtocol({
msgType: 'navRequest',
msg: { url: 'http://www.example.com/' }
msg: { url: 'http://www.example.com/', history: undefined }
}),
'https://example.com'
);
Expand Down

0 comments on commit 6836462

Please sign in to comment.