Skip to content

Commit

Permalink
feat(core): ✨ add replace history via data-attribute
Browse files Browse the repository at this point in the history
Closes #460
  • Loading branch information
thierrymichel committed Nov 19, 2019
1 parent 6d7ce9c commit 272a43f
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 92 deletions.
30 changes: 30 additions & 0 deletions packages/core/__tests__/core/core.go.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,33 @@ it('use self transition on same url [popstate]', async () => {
true
);
});

it('add history', async () => {
barba.history.add = jest.fn();

await barba.go('http://localhost/foo');

expect(barba.history.add).toHaveBeenCalledWith(
'http://localhost/foo',
'barba',
undefined
);
});

it('manage direction', async () => {
barba.page = jest.fn();

await barba.go('http://localhost/foo', 'popstate', {
state: {
index: 1,
},
stopPropagation() {},
preventDefault() {},
} as PopStateEvent);

expect(barba.page).toHaveBeenCalledWith(
'http://localhost/foo',
'forward',
false
);
});
10 changes: 0 additions & 10 deletions packages/core/__tests__/core/core.page.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,6 @@ it('do page', async () => {
expect(document.title).toBe('New page');
});

// it('do page [popstate]', async () => {
// barba.transitions.doPage = jest.fn();
// barba.history.add = jest.fn();

// barba.transitions.store.add('transition', { leave() {}, enter() {} });
// await barba.page(nextUrl, 'popstate', false);

// expect(barba.history.add).toHaveBeenCalledTimes(1);
// });

it('do page [has cache]', async () => {
barba.history.add = jest.fn();
barba.cache.set(sameUrl, Promise.resolve(sameHtml), 'init');
Expand Down
45 changes: 0 additions & 45 deletions packages/core/__tests__/core/core.state.test.ts

This file was deleted.

73 changes: 59 additions & 14 deletions packages/core/__tests__/utils/history.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ const second = {
},
url: 'url2',
};
const tmp = {
...second,
ns: 'tmp',
};
const triggerPush = document.createElement('a');
const triggerReplace = document.createElement('a');

triggerReplace.dataset.barbaHistory = 'replace';

const h = {
b: (global as any).window.history.back = jest.fn(),
ps: (global as any).window.history.pushState = jest.fn(),
rs: (global as any).window.history.replaceState = jest.fn(),
};

afterEach(() => {
history.clear();
Expand All @@ -28,33 +42,40 @@ it('has no history ', () => {
});

it('init state and has current', () => {
(global as any).window.history.replaceState = jest.fn();
history.init(first.url, first.ns);

expect(history.current).toEqual(first);
expect(history.previous).toBeNull();
expect((global as any).window.history.replaceState).toHaveBeenCalledTimes(1);
expect(h.rs).toHaveBeenCalledTimes(1);
});

it('adds state and has previous', () => {
history.init(first.url, first.ns);
history.add(second.url, second.ns);
history.add(second.url, 'barba');

expect(history.current).toEqual(second);
expect(history.current).toEqual(tmp);
expect(history.previous).toEqual(first);
});

it('pushes history', () => {
(global as any).window.history.pushState = jest.fn();
history.add(first.url, first.ns);
history.add(second.url, second.ns, null, false);
history.add(first.url, triggerPush);
history.add(second.url, triggerReplace);
history.add(second.url, 'popstate');

expect((global as any).window.history.pushState).toHaveBeenCalledTimes(1);
expect(h.ps).toHaveBeenCalledTimes(1);
});

it('replaces history', () => {
history.add(first.url, triggerReplace);
history.add(second.url, triggerPush);
history.add(second.url, 'popstate');

expect(h.rs).toHaveBeenCalledTimes(1);
});

it('removes state', () => {
history.init(first.url, first.ns);
history.add(second.url, second.ns);
history.add(second.url, 'barba');
history.remove();

expect(history.current).toEqual(first);
Expand All @@ -63,17 +84,17 @@ it('removes state', () => {

it('gets state', () => {
history.init(first.url, first.ns);
history.add(second.url, second.ns);
history.add(second.url, 'barba');
const state1 = history.get(0);
const state2 = history.get(1);

expect(state1).toEqual(first);
expect(state2).toEqual(second);
expect(state2).toEqual(tmp);
});

it('gets directions', () => {
history.init(first.url, first.ns);
history.add(second.url, second.ns);
history.add(second.url, 'barba');

const back = history.getDirection(0);
const forward = history.getDirection(2);
Expand All @@ -84,10 +105,34 @@ it('gets directions', () => {

it('cancels', () => {
history.remove = jest.fn();
(global as any).window.history.back = jest.fn();

history.cancel();

expect(history.remove).toHaveBeenCalledTimes(1);
expect((global as any).window.history.back).toHaveBeenCalledTimes(1);
expect(h.b).toHaveBeenCalledTimes(1);
});

it('manage history with "unknown" state', async () => {
history.add(second.url, 'barba');

expect(h.rs).toHaveBeenCalledTimes(0);
expect(h.ps).toHaveBeenCalledTimes(1);
});

it('manage history with previous state', async () => {
history.add(second.url, 'popstate');

expect(h.ps).toHaveBeenCalledTimes(0);
expect(h.rs).toHaveBeenCalledTimes(0);
});

it('manage history with data-barba-history="replace"', async () => {
const link = document.createElement('a');

link.dataset.barbaHistory = 'replace';

history.add(second.url, link);

expect(h.ps).toHaveBeenCalledTimes(0);
expect(h.rs).toHaveBeenCalledTimes(1);
});
21 changes: 7 additions & 14 deletions packages/core/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,24 +304,17 @@ export class Core {
return;
}

// Manage history / popstate direction
if (trigger === 'popstate') {
this.history.add(href, trigger, e);

// Manage popstate direction
if (trigger === 'popstate' && e) {
const { state } = e as PopStateEvent;

// So we will add to barba.history but, as this is already "popstate"
// we do not push to browser history (last param => false).
if (state === null) {
// If no state, probably coming from "anchored" link
// or manually modified history…
this.history.add(href, 'tmp', null, false);
} else {
// If previous state exists,
// we update the trigger with the direction.
/* istanbul ignore else */
if (state !== null) {
// We update the trigger with the direction.
trigger = this.history.getDirection(state.index as number);
this.history.add(href, state.ns, state.index, false);
}
} else {
this.history.add(href, 'tmp');
}

if (e) {
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/defs/history.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* @module typings/history
*/

export type HistoryAction = 'none' | 'push' | 'replace';
1 change: 1 addition & 0 deletions packages/core/src/defs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './cache';
export * from './dom';
export * from './global';
export * from './ignore';
export * from './history';
export * from './hooks';
export * from './prevent';
export * from './request';
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/defs/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type SchemaAttributeValues =
| 'wrapper'
| 'container'
| 'prevent'
| 'history'
| 'namespace';

/**
Expand All @@ -19,13 +20,15 @@ export type SchemaAttributeValues =
* @param wrapper data-prefix="__wrapper__"
* @param container data-prefix="__container__"
* @param prevent data-prefix-__prevent__
* @param history data-prefix-__history__
* @param namespace data-prefix-__namespace__
*/
export interface ISchemaAttribute {
prefix?: string;
wrapper?: string;
container?: string;
prevent?: string;
history?: string;
namespace?: string;
}

Expand Down
1 change: 1 addition & 0 deletions packages/core/src/modules/Transitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ export class Transitions {
}
} else {
let leaveResult: any = false;

try {
// Leave
await this._doAsyncHook('beforeLeave', data, t);
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/schemas/attribute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ISchemaAttribute } from '../defs';
*/
export const schemaAttribute: ISchemaAttribute = {
container: 'container',
history: 'history',
namespace: 'namespace',
prefix: 'data-barba',
prevent: 'prevent',
Expand Down
56 changes: 47 additions & 9 deletions packages/core/src/utils/history.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Trigger } from '../defs';
import { HistoryAction, LinkEvent, Trigger } from '../defs';
// Schemas
import { schemaAttribute } from '../schemas/attribute';

/**
* @barba/core/utils/history
Expand Down Expand Up @@ -63,12 +65,24 @@ export class History {
*/
public add(
url: string,
ns: string,
i: number = null,
push: boolean = true
trigger: Trigger,
e?: LinkEvent | PopStateEvent
): void {
const index = i || this.size;
const state: IHistoryItem = {
// If no state, it will be updated later.
const state = e ? (e as PopStateEvent).state : null;
const ns = state ? state.ns : 'tmp';
const index = state ? state.index : this.size;

// By default (popstate), we will add to barba.history but
// we do not push to browser history.
let action: HistoryAction = 'none';

// No popstate means modifying the history.
if (trigger !== 'popstate') {
action = this._getAction(trigger);
}

const data: IHistoryItem = {
index,
ns,
scroll: {
Expand All @@ -78,10 +92,16 @@ export class History {
url,
};

this._state.push(state);
this._state.push(data);

if (push) {
window.history && window.history.pushState(state, '', state.url);
switch (action) {
case 'push':
window.history && window.history.pushState(data, '', data.url);
break;
case 'replace':
window.history && window.history.replaceState(data, '', data.url);
break;
default:
}
}

Expand Down Expand Up @@ -164,6 +184,24 @@ export class History {
get size(): number {
return this._state.length;
}

/**
* Get the hostiry action: push vs replace
*/
private _getAction(trigger: Trigger): HistoryAction {
let action: HistoryAction = 'push';

// Manage `data-barba-history` attribute
// to get the right action (push vs replace).
const el = trigger as HTMLAnchorElement;
const attr = `${schemaAttribute.prefix}-${schemaAttribute.history}`;

if (el.hasAttribute && el.hasAttribute(attr)) {
action = el.getAttribute(attr) as HistoryAction;
}

return action;
}
}

const history = new History();
Expand Down

0 comments on commit 272a43f

Please sign in to comment.