Skip to content

Commit

Permalink
fix: insert new view tab into the tab bar after the active view tab
Browse files Browse the repository at this point in the history
By default, the view tab insertion position was changed to be after the active view tab instead of at the end.
In addition, workbench router allows specifying the insertion index of the view tab into the view tab bar.

fixes: #167
  • Loading branch information
danielwiehl authored and mofogasy committed Aug 23, 2019
1 parent 04fc715 commit 14d76f0
Show file tree
Hide file tree
Showing 13 changed files with 259 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
<option value="self">self</option>
<option value="blank">blank</option>
</select>

<label for="insertionIndex">InsertionIndex</label>
<input id="insertionIndex" [formControl]="form.get(INSERTION_INDEX)" list="insertionIndexHints">
<datalist id="insertionIndexHints">
<option value="start">
<option value="end">
</datalist>
</form>

<button (click)="onExecute()" class="e2e-execute">Execute</button>
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ import { FormArray, FormBuilder, FormGroup } from '@angular/forms';
import { SciParamsEnterComponent } from '@scion/app/common';
import { WbNavigationExtras, WorkbenchRouter } from '@scion/workbench-application.angular';
import { Qualifier } from '@scion/workbench-application-platform.api';
import { coerceNumberProperty } from '@angular/cdk/coercion';

const QUALIFIER = 'qualifier';
const QUERY_PARAMS = 'queryParams';
const MATRIX_PARAMS = 'matrixParams';
const ACTIVATE_IF_PRESENT = 'activateIfPresent';
const CLOSE_IF_PRESENT = 'closeIfPresent';
const TARGET = 'target';
const INSERTION_INDEX = 'insertionIndex';

@Component({
selector: 'app-view-navigation-panel',
Expand All @@ -34,6 +36,7 @@ export class ViewNavigationPanelComponent {
public readonly ACTIVATE_IF_PRESENT = ACTIVATE_IF_PRESENT;
public readonly CLOSE_IF_PRESENT = CLOSE_IF_PRESENT;
public readonly TARGET = TARGET;
public readonly INSERTION_INDEX = INSERTION_INDEX;

public form: FormGroup;

Expand All @@ -45,6 +48,7 @@ export class ViewNavigationPanelComponent {
[ACTIVATE_IF_PRESENT]: formBuilder.control(true),
[CLOSE_IF_PRESENT]: formBuilder.control(false),
[TARGET]: formBuilder.control('blank'),
[INSERTION_INDEX]: formBuilder.control(''),
});
}

Expand All @@ -56,8 +60,19 @@ export class ViewNavigationPanelComponent {
activateIfPresent: this.form.get(ACTIVATE_IF_PRESENT).value,
closeIfPresent: this.form.get(CLOSE_IF_PRESENT).value,
target: this.form.get(TARGET).value,
blankInsertionIndex: coerceInsertionIndex(this.form.get(INSERTION_INDEX).value),
};

this._router.navigate(qualifier, extras);
}
}

function coerceInsertionIndex(value: any): number | 'start' | 'end' | undefined {
if (value === '') {
return undefined;
}
if (value === 'start' || value === 'end' || value === undefined) {
return value;
}
return coerceNumberProperty(value);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</wb-activity>

<ng-template wbViewPartAction *ngIf="showOpenNewViewTabAction">
<button [wbRouterLink]="'/welcome'" class="material-icons e2e-open-new-tab" [wbRouterLinkExtras]="{activateIfPresent: false}">
<button [wbRouterLink]="'/welcome'" class="material-icons e2e-open-new-tab" [wbRouterLinkExtras]="{activateIfPresent: false, blankInsertionIndex: 'end'}">
add
</button>
</ng-template>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
<option value="self">self</option>
<option value="blank">blank</option>
</select>

<label for="insertionIndex">InsertionIndex</label>
<input id="insertionIndex" [formControl]="form.get(INSERTION_INDEX)" list="insertionIndexHints">
<datalist id="insertionIndexHints">
<option value="start">
<option value="end">
</datalist>
</form>

<button (click)="onNavigate()" class="e2e-navigate">Navigate</button>
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ import { FormArray, FormBuilder, FormGroup } from '@angular/forms';
import { SciParamsEnterComponent } from '@scion/app/common';
import { WbNavigationExtras, WorkbenchRouter, WorkbenchView } from '@scion/workbench';
import { Params } from '@angular/router';
import { coerceNumberProperty } from '@angular/cdk/coercion';

const PATH = 'path';
const QUERY_PARAMS = 'queryParams';
const MATRIX_PARAMS = 'matrixParams';
const ACTIVATE_IF_PRESENT = 'activateIfPresent';
const CLOSE_IF_PRESENT = 'closeIfPresent';
const TARGET = 'target';
const INSERTION_INDEX = 'insertionIndex';

@Component({
selector: 'app-view-navigation',
Expand All @@ -34,6 +36,7 @@ export class ViewNavigationComponent {
public readonly ACTIVATE_IF_PRESENT = ACTIVATE_IF_PRESENT;
public readonly CLOSE_IF_PRESENT = CLOSE_IF_PRESENT;
public readonly TARGET = TARGET;
public readonly INSERTION_INDEX = INSERTION_INDEX;

public form: FormGroup;

Expand All @@ -48,6 +51,7 @@ export class ViewNavigationComponent {
[ACTIVATE_IF_PRESENT]: formBuilder.control(true),
[CLOSE_IF_PRESENT]: formBuilder.control(false),
[TARGET]: formBuilder.control('blank'),
[INSERTION_INDEX]: formBuilder.control(''),
});
}

Expand All @@ -59,13 +63,25 @@ export class ViewNavigationComponent {
closeIfPresent: this.form.get(CLOSE_IF_PRESENT).value,
target: this.form.get(TARGET).value,
selfViewRef: this._view.viewRef,
blankInsertionIndex: coerceInsertionIndex(this.form.get(INSERTION_INDEX).value),
};

const commands: any[] = String(this.form.get(PATH).value).split('/');
if (matrixParams && Object.keys(matrixParams).length) {
commands.push(matrixParams);
}

this._router.navigate(commands, extras);
this._router.navigate(commands, extras).then();
}
}

function coerceInsertionIndex(value: any): number | 'start' | 'end' | undefined {
if (value === '') {
return undefined;
}
if (value === 'start' || value === 'end' || value === undefined) {
return value;
}
return coerceNumberProperty(value);
}

9 changes: 9 additions & 0 deletions projects/e2e/workbench/src/page-object/app.po.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@ export class AppPO {
}
return createViewPartBarFinder(viewPartRef).$$('wb-view-tab').count();
}

/**
* Returns the view tab view references in the order as displayed in the view tab bar.
*/
public async getViewTabs(viewPartRef: string): Promise<string[]> {
const viewTabsFinder = createViewPartBarFinder(viewPartRef).$$('wb-view-tab');
return viewTabsFinder.reduce((acc: string[], viewTabFinder: ElementFinder) => {
return viewTabFinder.getAttribute('viewref').then(viewRef => acc.concat(viewRef));
}, []);
}

/**
Expand Down
148 changes: 148 additions & 0 deletions projects/e2e/workbench/src/view-tab-bar.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,153 @@ describe('ViewTabBar', () => {

await expect(appPO.findViewTab('viewpart.1', {cssClass: 'e2e-view-2'}).isActive()).toBeTruthy();
});

it('should insert a new view tab into the tabbar after the active view tab by default', async () => {
await viewNavigationPO.navigateTo();
await expect(appPO.getViewTabCount('viewpart.1')).toEqual(1);
await expect(appPO.getViewTabs('viewpart.1')).toEqual(['view.1']);

// open view.2
await viewNavigationPO.enterPath('view');
await viewNavigationPO.checkActivateIfPresent(false);
await viewNavigationPO.selectTarget('blank');
await viewNavigationPO.navigate();

await expect(appPO.findViewTab('viewpart.1', {viewRef: 'view.2'}).isActive()).toBeTruthy();
await expect(appPO.getViewTabs('viewpart.1')).toEqual(['view.1', 'view.2']);

// open view.3
await viewNavigationPO.activateViewTab();
await viewNavigationPO.enterPath('view');
await viewNavigationPO.checkActivateIfPresent(false);
await viewNavigationPO.selectTarget('blank');
await viewNavigationPO.navigate();

await expect(appPO.findViewTab('viewpart.1', {viewRef: 'view.3'}).isActive()).toBeTruthy();
await expect(appPO.getViewTabs('viewpart.1')).toEqual(['view.1', 'view.3', 'view.2']);

// open view.4
await viewNavigationPO.activateViewTab();
await viewNavigationPO.enterPath('view');
await viewNavigationPO.checkActivateIfPresent(false);
await viewNavigationPO.selectTarget('blank');
await viewNavigationPO.navigate();

await expect(appPO.findViewTab('viewpart.1', {viewRef: 'view.4'}).isActive()).toBeTruthy();
await expect(appPO.getViewTabs('viewpart.1')).toEqual(['view.1', 'view.4', 'view.3', 'view.2']);
});

it('should insert a new view tab into the tabbar at the end', async () => {
await viewNavigationPO.navigateTo();
await expect(appPO.getViewTabCount('viewpart.1')).toEqual(1);
await expect(appPO.getViewTabs('viewpart.1')).toEqual(['view.1']);

// open view.2
await viewNavigationPO.enterPath('view');
await viewNavigationPO.checkActivateIfPresent(false);
await viewNavigationPO.selectTarget('blank');
await viewNavigationPO.setInsertionIndex('end');
await viewNavigationPO.navigate();

await expect(appPO.findViewTab('viewpart.1', {viewRef: 'view.2'}).isActive()).toBeTruthy();
await expect(appPO.getViewTabs('viewpart.1')).toEqual(['view.1', 'view.2']);

// open view.3
await viewNavigationPO.activateViewTab();
await viewNavigationPO.enterPath('view');
await viewNavigationPO.checkActivateIfPresent(false);
await viewNavigationPO.selectTarget('blank');
await viewNavigationPO.setInsertionIndex('end');
await viewNavigationPO.navigate();

await expect(appPO.findViewTab('viewpart.1', {viewRef: 'view.3'}).isActive()).toBeTruthy();
await expect(appPO.getViewTabs('viewpart.1')).toEqual(['view.1', 'view.2', 'view.3']);

// open view.4
await viewNavigationPO.activateViewTab();
await viewNavigationPO.enterPath('view');
await viewNavigationPO.checkActivateIfPresent(false);
await viewNavigationPO.selectTarget('blank');
await viewNavigationPO.setInsertionIndex('end');
await viewNavigationPO.navigate();

await expect(appPO.findViewTab('viewpart.1', {viewRef: 'view.4'}).isActive()).toBeTruthy();
await expect(appPO.getViewTabs('viewpart.1')).toEqual(['view.1', 'view.2', 'view.3', 'view.4']);
});

it('should insert a new view tab into the tabbar at the start', async () => {
await viewNavigationPO.navigateTo();
await expect(appPO.getViewTabCount('viewpart.1')).toEqual(1);
await expect(appPO.getViewTabs('viewpart.1')).toEqual(['view.1']);

// open view.2
await viewNavigationPO.enterPath('view');
await viewNavigationPO.checkActivateIfPresent(false);
await viewNavigationPO.selectTarget('blank');
await viewNavigationPO.setInsertionIndex('start');
await viewNavigationPO.navigate();

await expect(appPO.findViewTab('viewpart.1', {viewRef: 'view.2'}).isActive()).toBeTruthy();
await expect(appPO.getViewTabs('viewpart.1')).toEqual(['view.2', 'view.1']);

// open view.3
await viewNavigationPO.activateViewTab();
await viewNavigationPO.enterPath('view');
await viewNavigationPO.checkActivateIfPresent(false);
await viewNavigationPO.selectTarget('blank');
await viewNavigationPO.setInsertionIndex('start');
await viewNavigationPO.navigate();

await expect(appPO.findViewTab('viewpart.1', {viewRef: 'view.3'}).isActive()).toBeTruthy();
await expect(appPO.getViewTabs('viewpart.1')).toEqual(['view.3', 'view.2', 'view.1']);

// open view.4
await viewNavigationPO.activateViewTab();
await viewNavigationPO.enterPath('view');
await viewNavigationPO.checkActivateIfPresent(false);
await viewNavigationPO.selectTarget('blank');
await viewNavigationPO.setInsertionIndex('start');
await viewNavigationPO.navigate();

await expect(appPO.findViewTab('viewpart.1', {viewRef: 'view.4'}).isActive()).toBeTruthy();
await expect(appPO.getViewTabs('viewpart.1')).toEqual(['view.4', 'view.3', 'view.2', 'view.1']);
});

it('should insert a new view tab into the tabbar at a custom position', async () => {
await viewNavigationPO.navigateTo();
await expect(appPO.getViewTabCount('viewpart.1')).toEqual(1);
await expect(appPO.getViewTabs('viewpart.1')).toEqual(['view.1']);

// open view.2
await viewNavigationPO.enterPath('view');
await viewNavigationPO.checkActivateIfPresent(false);
await viewNavigationPO.selectTarget('blank');
await viewNavigationPO.setInsertionIndex(1);
await viewNavigationPO.navigate();

await expect(appPO.findViewTab('viewpart.1', {viewRef: 'view.2'}).isActive()).toBeTruthy();
await expect(appPO.getViewTabs('viewpart.1')).toEqual(['view.1', 'view.2']);

// open view.3
await viewNavigationPO.activateViewTab();
await viewNavigationPO.enterPath('view');
await viewNavigationPO.checkActivateIfPresent(false);
await viewNavigationPO.selectTarget('blank');
await viewNavigationPO.setInsertionIndex(1);
await viewNavigationPO.navigate();

await expect(appPO.findViewTab('viewpart.1', {viewRef: 'view.3'}).isActive()).toBeTruthy();
await expect(appPO.getViewTabs('viewpart.1')).toEqual(['view.1', 'view.3', 'view.2']);

// open view.4
await viewNavigationPO.activateViewTab();
await viewNavigationPO.enterPath('view');
await viewNavigationPO.checkActivateIfPresent(false);
await viewNavigationPO.selectTarget('blank');
await viewNavigationPO.setInsertionIndex(1);
await viewNavigationPO.navigate();

await expect(appPO.findViewTab('viewpart.1', {viewRef: 'view.4'}).isActive()).toBeTruthy();
await expect(appPO.getViewTabs('viewpart.1')).toEqual(['view.1', 'view.4', 'view.3', 'view.2']);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -210,5 +210,11 @@ export interface ViewIntentMessage extends IntentMessage {
* 'self': opens the view in the current view tab
*/
target?: 'blank' | 'self';
/**
* Specifies the position where to insert the view into the tab bar when using 'blank' view target strategy.
* If not specified, the view is inserted after the active view. Set the index to 'start' or 'end' for inserting
* the view at the beginning or at the end.
*/
blankInsertionIndex?: number | 'start' | 'end' | undefined;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export class ViewIntentHandler implements IntentHandler {
target: intentMessage.payload.target,
selfViewRef: view && view.viewRef,
blankViewPartRef: view && this._workbench.resolveViewPart(view.viewRef),
blankInsertionIndex: intentMessage.payload.blankInsertionIndex,
};

const commands = this.createNavigateCommands(viewCapability, matrixParamObject, envelope.message.qualifier);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export class WorkbenchRouter {
activateIfPresent: extras.activateIfPresent,
closeIfPresent: extras.closeIfPresent,
target: extras.target,
blankInsertionIndex: extras.blankInsertionIndex,
};
Platform.getService(RouterService).navigate(navigateCommand);
}
Expand Down Expand Up @@ -73,4 +74,10 @@ export interface WbNavigationExtras {
matrixParams?: {
[key: string]: any;
};
/**
* Specifies the position where to insert the view into the tab bar when using 'blank' view target strategy.
* If not specified, the view is inserted after the active view. Set the index to 'start' or 'end' for inserting
* the view at the beginning or at the end.
*/
blankInsertionIndex?: number | 'start' | 'end' | undefined;
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export class RouterService implements Service {
activateIfPresent: command.activateIfPresent,
closeIfPresent: command.closeIfPresent,
target: command.target,
blankInsertionIndex: command.blankInsertionIndex,
},
};

Expand Down Expand Up @@ -84,4 +85,10 @@ export interface ViewNavigateCommand {
* 'self': opens the view in the current view tab
*/
target?: 'blank' | 'self';
/**
* Specifies the position where to insert the view into the tab bar when using 'blank' view target strategy.
* If not specified, the view is inserted after the active view. Set the index to 'start' or 'end' for inserting
* the view at the beginning or at the end.
*/
blankInsertionIndex?: number | 'start' | 'end' | undefined;
}
Loading

0 comments on commit 14d76f0

Please sign in to comment.