Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API change] ConfigurableNavigationMode proposal #211

Merged
merged 14 commits into from
May 16, 2019
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 3 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,12 @@ export {SelectedStepDirective} from './lib/directives/selected-step.directive';
export {WizardCompletionStepDirective} from './lib/directives/wizard-completion-step.directive';
export {WizardStepDirective} from './lib/directives/wizard-step.directive';
export {WizardStepTitleDirective} from './lib/directives/wizard-step-title.directive';
export {NavigationModeDirective} from './lib/directives/navigation-mode.directive';

// export the navigation classes
export {FreeNavigationMode} from './lib/navigation/free-navigation-mode';
export {NavigationMode} from './lib/navigation/navigation-mode.interface';
export {SemiStrictNavigationMode} from './lib/navigation/semi-strict-navigation-mode';
export {StrictNavigationMode} from './lib/navigation/strict-navigation-mode';
export {ConfigurableNavigationMode} from './lib/navigation/configurable-navigation-mode';
export {BaseNavigationMode} from './lib/navigation/base-navigation-mode.interface';
export {NavigationModeInput} from './lib/navigation/navigation-mode-input.interface';
export {NavigationModeFactory} from './lib/navigation/navigation-mode-factory.interface';
export {BaseNavigationModeFactory} from './lib/navigation/base-navigation-mode-factory.provider';

// export the utility functions
export {MovingDirection} from './lib/util/moving-direction.enum';
Expand All @@ -36,4 +32,4 @@ export {WizardCompletionStep} from './lib/util/wizard-completion-step.interface'
export {WizardStep} from './lib/util/wizard-step.interface';

// export the module
export {ArchwizardModule, ArchwizardModuleConfig} from './lib/archwizard.module';
export {ArchwizardModule} from './lib/archwizard.module';
29 changes: 7 additions & 22 deletions src/lib/archwizard.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,9 @@ import {WizardCompletionStepDirective} from './directives/wizard-completion-step
import {WizardStepSymbolDirective} from './directives/wizard-step-symbol.directive';
import {WizardStepTitleDirective} from './directives/wizard-step-title.directive';
import {WizardStepDirective} from './directives/wizard-step.directive';
import {NAVIGATION_MODE_FACTORY, NavigationModeFactory} from './navigation/navigation-mode-factory.interface';
import {BaseNavigationModeFactory} from './navigation/base-navigation-mode-factory.provider';
import {NavigationModeDirective} from './directives/navigation-mode.directive';


/**
* Configuration object for the `angular-archwizard` module.
*
* Allows to customize global settings.
*/
export interface ArchwizardModuleConfig {

/**
* Custom factory of [[NavigationMode]] instances.
*
* You may need a custom factory in order to support custom navigation modes.
* By default, [[BaseNavigationModeFactory]] is used.
*/
navigationModeFactory?: NavigationModeFactory;
}

earshinov marked this conversation as resolved.
Show resolved Hide resolved
/**
* The module defining all the content inside `angular-archwizard`
*
Expand All @@ -56,7 +39,8 @@ export interface ArchwizardModuleConfig {
WizardStepDirective,
WizardCompletionStepDirective,
SelectedStepDirective,
ResetWizardDirective
ResetWizardDirective,
NavigationModeDirective,
],
imports: [
CommonModule
Expand All @@ -76,16 +60,17 @@ export interface ArchwizardModuleConfig {
WizardStepDirective,
WizardCompletionStepDirective,
SelectedStepDirective,
ResetWizardDirective
ResetWizardDirective,
NavigationModeDirective,
]
})
export class ArchwizardModule {
/* istanbul ignore next */
public static forRoot(config?: ArchwizardModuleConfig): ModuleWithProviders {
public static forRoot(): ModuleWithProviders {
return {
ngModule: ArchwizardModule,
providers: [
{ provide: NAVIGATION_MODE_FACTORY, useValue: config && config.navigationModeFactory || new BaseNavigationModeFactory() },
// Nothing here yet
]
};
}
Expand Down
17 changes: 9 additions & 8 deletions src/lib/components/wizard.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import {AfterViewInit, ChangeDetectorRef, Component, ViewChild} from '@angular/c
import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {ArchwizardModule} from '../archwizard.module';
import {FreeNavigationMode} from '../navigation/free-navigation-mode';
import {StrictNavigationMode} from '../navigation/strict-navigation-mode';
import {WizardComponent} from './wizard.component';
import {WizardStep} from '../util/wizard-step.interface';

@Component({
selector: 'aw-test-wizard',
template: `
<aw-wizard [navigationMode]="navigationMode" [disableNavigationBar]="disableNavigationBar" [defaultStepIndex]="defaultStepIndex">
<aw-wizard
[disableNavigationBar]="disableNavigationBar" [defaultStepIndex]="defaultStepIndex"
[awNavigationMode] [navigateForward]="navigateForward" [navigateBackward]="navigateBackward">
<aw-wizard-step stepTitle='Steptitle 1' *ngIf="showStep1">
Step 1
</aw-wizard-step>
Expand All @@ -24,7 +24,8 @@ import {WizardStep} from '../util/wizard-step.interface';
`
})
class WizardTestComponent implements AfterViewInit {
public navigationMode = 'strict';
public navigateForward = 'deny';
public navigateBackward = 'deny';

public disableNavigationBar = false;

Expand Down Expand Up @@ -69,7 +70,6 @@ describe('WizardComponent', () => {
it('should create', () => {
expect(wizardTest).toBeTruthy();
expect(wizard).toBeTruthy();
expect(wizard.navigationMode).toBeTruthy();
});

it('should contain navigation bar at the correct position in default navBarLocation mode', () => {
Expand Down Expand Up @@ -180,12 +180,13 @@ describe('WizardComponent', () => {
});

it('should change the navigation mode correctly during runtime', () => {
expect(wizard.navigation instanceof StrictNavigationMode).toBe(true);
const oldNavigation = wizard.navigation;

wizardTest.navigationMode = 'free';
wizardTest.navigateForward = 'allow';
wizardTest.navigateBackward = 'allow';
wizardTestFixture.detectChanges();

expect(wizard.navigation instanceof FreeNavigationMode).toBe(true);
expect(wizard.navigation).not.toBe(oldNavigation);
});

it('should change disableNavigationBar correctly during runtime', () => {
Expand Down
63 changes: 8 additions & 55 deletions src/lib/components/wizard.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,12 @@ import {
HostBinding,
Input,
QueryList,
SimpleChanges,
Inject,
Optional,
EventEmitter,
OnChanges,
} from '@angular/core';
import {NavigationMode} from '../navigation/navigation-mode.interface';
import {NavigationModeInput} from '../navigation/navigation-mode-input.interface';
import {NavigationModeFactory, NAVIGATION_MODE_FACTORY} from '../navigation/navigation-mode-factory.interface';
import {BaseNavigationModeFactory} from '../navigation/base-navigation-mode-factory.provider';
import {WizardStep} from '../util/wizard-step.interface';
import {MovingDirection} from '../util/moving-direction.enum';
import {ConfigurableNavigationMode} from '../navigation/configurable-navigation-mode';

/**
* The `aw-wizard` component defines the root component of a wizard.
Expand Down Expand Up @@ -57,7 +51,7 @@ import {MovingDirection} from '../util/moving-direction.enum';
selector: 'aw-wizard',
templateUrl: 'wizard.component.html',
})
export class WizardComponent implements AfterContentInit, OnChanges {
export class WizardComponent implements AfterContentInit {
/**
* A QueryList containing all [[WizardStep]]s inside this wizard
*/
Expand Down Expand Up @@ -85,23 +79,6 @@ export class WizardComponent implements AfterContentInit, OnChanges {
@Input()
public navBarDirection = 'left-to-right';

/**
* The navigation mode used for transitioning between different steps.
*
* The input value can be either a navigation mode name or a function.
*
* A set of supported mode names is determined by the configured navigation mode factory.
* The default navigation mode factory recognizes `strict`, `semi-strict` and `free`.
*
* If the value is a function, the function will be called during the initialization of the wizard
* component and must return an instance of [[NavigationMode]] to be used in the component.
*
* If the input is not configured or set to a falsy value, a default mode will be chosen by the navigation mode factory.
* For the default navigation mode factory, the default mode is `strict`.
*/
@Input()
public navigationMode: NavigationModeInput;

/**
* The initially selected step, represented by its index
* Beware: This initial default is only used if no wizard step has been enhanced with the `selected` directive
Expand Down Expand Up @@ -136,7 +113,7 @@ export class WizardComponent implements AfterContentInit, OnChanges {
*
* For outside access, use the [[navigation]] getter.
*/
private _navigation: NavigationMode;
private _navigation: NavigationMode = new ConfigurableNavigationMode();

/**
* An array representation of all wizard steps belonging to this model
Expand All @@ -156,16 +133,8 @@ export class WizardComponent implements AfterContentInit, OnChanges {

/**
* Constructor
*
* @param model The model for this wizard component
* @param navigationModeFactory Navigation mode factory for this wizard component
*/
constructor(
// Using @Optional() in order not to break applications which import ArchwizardModule without calling forRoot().
@Optional() @Inject(NAVIGATION_MODE_FACTORY) private navigationModeFactory: NavigationModeFactory) {
if (!this.navigationModeFactory) {
this.navigationModeFactory = new BaseNavigationModeFactory();
}
*/
constructor() {
}

/**
Expand All @@ -190,21 +159,6 @@ export class WizardComponent implements AfterContentInit, OnChanges {
return this.navBarLocation === 'left' || this.navBarLocation === 'right';
}

/**
* Updates the model after certain input values have changed
*
* @param changes The detected changes
*/
public ngOnChanges(changes: SimpleChanges) {
for (const propName of Object.keys(changes)) {
const change = changes[propName];

if (!change.firstChange && propName === 'navigationMode') {
this.updateNavigationMode(change.currentValue);
}
}
}

/**
* Initialization work
*/
Expand All @@ -216,7 +170,6 @@ export class WizardComponent implements AfterContentInit, OnChanges {

// initialize the model
this.updateWizardSteps(this.wizardStepsQueryList.toArray());
this.updateNavigationMode(this.navigationMode);

// finally reset the whole wizard componennt
this.reset();
Expand Down Expand Up @@ -277,10 +230,10 @@ export class WizardComponent implements AfterContentInit, OnChanges {
/**
* Updates the navigation mode for this wizard component
*
* @param navigationMode The updated navigation mode
* @param navigation The updated navigation mode
*/
public updateNavigationMode(navigationMode: NavigationModeInput) {
this._navigation = this.navigationModeFactory.create(this, navigationMode);
public set navigation(navigation: NavigationMode) {
this._navigation = navigation;
}

/**
Expand Down
3 changes: 1 addition & 2 deletions src/lib/directives/go-to-step.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import {Component, ViewChild} from '@angular/core';
import {async, ComponentFixture, fakeAsync, TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {ArchwizardModule} from '../archwizard.module';
import {NavigationMode} from '../navigation/navigation-mode.interface';
import {GoToStepDirective} from './go-to-step.directive';
import { WizardComponent } from '../components/wizard.component';
import {WizardComponent} from '../components/wizard.component';

@Component({
selector: 'aw-test-wizard',
Expand Down
98 changes: 98 additions & 0 deletions src/lib/directives/navigation-mode.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import {Directive, Input, OnChanges, SimpleChanges} from '@angular/core';

import {NavigationMode} from '../navigation/navigation-mode.interface';
import {ConfigurableNavigationMode} from '../navigation/configurable-navigation-mode';
import {WizardComponent} from '../components/wizard.component';


/**
* The [[awNavigationMode]] directive can be used to customize wizard'd navigation mode.
*
* There are several usage options:
*
* ### Option 1. Customize the default navigation mode with [[navigateBackward]] and/or [[navigateForward]] inputs.
*
* ```html
* <aw-wizard [awNavigationMode] navigateBackward="deny" navigateForward="allow">...</aw-wizard>
* ```
*
* ### Option 2. Pass in a custom navigation mode
*
* ```typescript
* import { BaseNavigationMode } from 'angular-archwizard'
*
* class CustomNavigationMode extends BaseNavigationMode {
*
* // ...
* }
* ```
*
* ```typescript
* @Component({
* // ...
* })
* class MyComponent {
*
* navigationMode = new CustomNavigationMode();
* }
* ```
*
* ```html
* <aw-wizard [awNavigationMode]="navigationMode">...</aw-wizard>
* ```
*
* ### Additional Notes
*
* - Specifying a custom navigation mode takes priority over [[navigateBackward]] and [[navigateForward]] inputs
*
* - Omitting the [[awNavigationMode]] directive or, equally, specifying just [[awNavigationMode]] without
* any inputs or parameters causes the wizard to use the default "strict" navigation mode equivalent to
*
* ```html
* <aw-wizard [awNavigationMode] navigateBackward="deny" navigateForward="allow">...</aw-wizard>
* ````
*/
@Directive({
selector: '[awNavigationMode]',
})
export class NavigationModeDirective implements OnChanges {
madoar marked this conversation as resolved.
Show resolved Hide resolved

/**
* Custom navigation mode instance (optional).
*/
@Input()
public awNavigationMode: NavigationMode|null;

/**
* A parameter for the default navigation mode. Controls whether wizard steps before the current step are navigable:
*
* - `navigateBackward="deny"` -- the steps are not navigable
* - `navigateBackward="allow"` -- the steps are navigable
*/
@Input()
public navigateBackward: 'allow'|'deny'|null;

/**
* A parameter for the default navigation mode. Controls whether wizard steps after the current step are navigable:
*
* - `navigateForward="deny"` -- the steps are not navigable
* - `navigateForward="allow"` -- the steps are navigable
*/
@Input()
public navigateForward: 'allow'|'deny'|null;

constructor(private wizard: WizardComponent) { }

public ngOnChanges(changes: SimpleChanges): void {
this.wizard.navigation = this.getNavigationMode();
}

private getNavigationMode(): NavigationMode {
if (this.awNavigationMode) {
return this.awNavigationMode;
}
return new ConfigurableNavigationMode(this.navigateBackward, this.navigateForward);
}

}