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

Refactor the hierarchy of NavigationMode classes. #166

Merged
merged 22 commits into from Mar 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6663ff6
Refactor the hierarchy of NavigationMode classes.
earshinov Oct 20, 2018
d8840aa
Cleanup: Restore missing access modifier
earshinov Oct 21, 2018
92ffc2d
Document arguments for goToNextStep and goToPreviousStep
earshinov Oct 21, 2018
31334b6
Cleanup: Split a long line
earshinov Oct 21, 2018
9164886
Allow checkReset to indicate check failure by returning false
earshinov Oct 21, 2018
c16fb9c
Split `abstract class NavigationMode` into `interface NavigatinMode` …
earshinov Oct 22, 2018
6834e74
Provide an ability to specify WizardComponent's navigationMode input …
earshinov Oct 22, 2018
d567ad4
Merge branch 'develop' into 'refactor-nav-modes'
earshinov Mar 17, 2019
e259bcd
Merge branch 'develop' into 'refactor-nav-modes'
earshinov Mar 23, 2019
13b59d2
Extract NavigationModeFactory interface and BaseNavigationModeFactory…
earshinov Mar 23, 2019
93aa8f6
In wizard.component.ts, set default navigationMode to '' rather than …
earshinov Mar 23, 2019
feecb85
Remove the option to return a navigation node name from a function pa…
earshinov Mar 23, 2019
01b3dd7
Add comments for ArchwizardModuleConfig and ArchwizardModuleConfig.na…
earshinov Mar 23, 2019
7d9804a
Add missing doc-comment for a WizardComponent's constructor parameter
earshinov Mar 23, 2019
aeccff6
Add missing documentation for WizardComponent.updateNavigationMode
earshinov Mar 23, 2019
72bae52
Added missing `implements` and access modifiers in `BaseNavigationMod…
earshinov Mar 23, 2019
74c0050
Rename `navigationMode` parameter to `navigationModeInput` for consis…
earshinov Mar 23, 2019
589880a
Improve doc comments for WizardComponent.navigationInput field and Na…
earshinov Mar 23, 2019
72bf91b
Removed the default value for WizardComponent.navigationMode field. …
earshinov Mar 23, 2019
54a6147
Enable "member-access" check in tslint.json. Add missing access modi…
earshinov Mar 23, 2019
016b542
In ArchwizardModuleConfig, take an instance of NavigationModeFactory …
earshinov Mar 23, 2019
b4a5601
Move implementation-specific comments from NavigationMode interface t…
earshinov Mar 23, 2019
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
7 changes: 5 additions & 2 deletions src/index.ts
Expand Up @@ -21,8 +21,11 @@ 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 {BaseNavigationMode} from './lib/navigation/base-navigation-mode.interface';
export {WizardState} from './lib/navigation/wizard-state.model';
export {navigationModeFactory} from './lib/navigation/navigation-mode.provider';
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 @@ -34,4 +37,4 @@ export {WizardCompletionStep} from './lib/util/wizard-completion-step.interface'
export {WizardStep} from './lib/util/wizard-step.interface';

// export the module
export {ArchwizardModule} from './lib/archwizard.module';
export {ArchwizardModule, ArchwizardModuleConfig} from './lib/archwizard.module';
28 changes: 26 additions & 2 deletions src/lib/archwizard.module.ts
Expand Up @@ -15,6 +15,25 @@ 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';


/**
* Configuration object for the `angular-archwizard` module.
*
* Allows to customize global settings.
*/
export interface ArchwizardModuleConfig {
madoar marked this conversation as resolved.
Show resolved Hide resolved

/**
* 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;
}

/**
* The module defining all the content inside `angular-archwizard`
Expand Down Expand Up @@ -62,7 +81,12 @@ import {WizardStepDirective} from './directives/wizard-step.directive';
})
export class ArchwizardModule {
/* istanbul ignore next */
static forRoot(): ModuleWithProviders {
return {ngModule: ArchwizardModule, providers: []};
public static forRoot(config?: ArchwizardModuleConfig): ModuleWithProviders {
return {
ngModule: ArchwizardModule,
providers: [
{ provide: NAVIGATION_MODE_FACTORY, useValue: config && config.navigationModeFactory || new BaseNavigationModeFactory() },
]
};
}
}
5 changes: 2 additions & 3 deletions src/lib/components/wizard-completion-step.component.spec.ts
Expand Up @@ -5,7 +5,6 @@ import {ArchwizardModule} from '../archwizard.module';
import {NavigationMode} from '../navigation/navigation-mode.interface';
import {WizardState} from '../navigation/wizard-state.model';
import {MovingDirection} from '../util/moving-direction.enum';
import {WizardCompletionStepComponent} from './wizard-completion-step.component';

@Component({
selector: 'aw-test-wizard',
Expand All @@ -29,11 +28,11 @@ class WizardTestComponent {

public eventLog: Array<string> = [];

enterInto(direction: MovingDirection, destination: number): void {
public enterInto(direction: MovingDirection, destination: number): void {
this.eventLog.push(`enter ${MovingDirection[direction]} ${destination}`);
}

exitFrom(direction: MovingDirection, source: number): void {
public exitFrom(direction: MovingDirection, source: number): void {
this.eventLog.push(`exit ${MovingDirection[direction]} ${source}`);
}
}
Expand Down
5 changes: 2 additions & 3 deletions src/lib/components/wizard-step.component.spec.ts
Expand Up @@ -5,7 +5,6 @@ import {ArchwizardModule} from '../archwizard.module';
import {NavigationMode} from '../navigation/navigation-mode.interface';
import {WizardState} from '../navigation/wizard-state.model';
import {MovingDirection} from '../util/moving-direction.enum';
import {WizardStepComponent} from './wizard-step.component';

@Component({
selector: 'aw-test-wizard',
Expand All @@ -29,11 +28,11 @@ class WizardTestComponent {

public eventLog: Array<string> = [];

enterInto(direction: MovingDirection, destination: number): void {
public enterInto(direction: MovingDirection, destination: number): void {
this.eventLog.push(`enter ${MovingDirection[direction]} ${destination}`);
}

exitFrom(direction: MovingDirection, source: number): void {
public exitFrom(direction: MovingDirection, source: number): void {
this.eventLog.push(`exit ${MovingDirection[direction]} ${source}`);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/wizard.component.spec.ts
Expand Up @@ -40,7 +40,7 @@ class WizardTestComponent implements AfterViewInit {
constructor(private _changeDetectionRef: ChangeDetectorRef) {
}

ngAfterViewInit(): void {
public ngAfterViewInit(): void {
// Force another change detection in order to fix an occuring ExpressionChangedAfterItHasBeenCheckedError
this._changeDetectionRef.detectChanges();
}
Expand Down
49 changes: 41 additions & 8 deletions src/lib/components/wizard.component.ts
Expand Up @@ -7,9 +7,14 @@ import {
OnChanges,
QueryList,
SimpleChanges,
ViewEncapsulation
ViewEncapsulation,
Inject,
Optional
} 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 {WizardState} from '../navigation/wizard-state.model';
import {WizardStep} from '../util/wizard-step.interface';

Expand Down Expand Up @@ -85,10 +90,20 @@ export class WizardComponent implements OnChanges, AfterContentInit {

/**
* The navigation mode used for transitioning between different steps.
* The navigation mode can be either `strict`, `semi-strict` or `free`
*
* 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 = 'strict';
public navigationMode: NavigationModeInput;

/**
* The initially selected step, represented by its index
Expand All @@ -106,8 +121,15 @@ export class WizardComponent implements OnChanges, AfterContentInit {
* Constructor
*
* @param model The model for this wizard component
* @param navigationModeFactory Navigation mode factory for this wizard component
*/
constructor(public model: WizardState) {
constructor(
public model: WizardState,
// Using @Optional() in order not to break applications which import ArchwizardModule without calling forRoot().
@Optional() @Inject(NAVIGATION_MODE_FACTORY) private navigationModeFactory: NavigationModeFactory) {
madoar marked this conversation as resolved.
Show resolved Hide resolved
if (!this.navigationModeFactory) {
this.navigationModeFactory = new BaseNavigationModeFactory();
}
}

/**
Expand Down Expand Up @@ -144,7 +166,7 @@ export class WizardComponent implements OnChanges, AfterContentInit {
*
* @param changes The detected changes
*/
ngOnChanges(changes: SimpleChanges) {
public ngOnChanges(changes: SimpleChanges) {
for (const propName of Object.keys(changes)) {
const change = changes[propName];

Expand All @@ -157,7 +179,7 @@ export class WizardComponent implements OnChanges, AfterContentInit {
this.model.disableNavigationBar = change.currentValue;
break;
case 'navigationMode':
this.model.updateNavigationMode(change.currentValue);
this.updateNavigationMode(change.currentValue);
break;
/* istanbul ignore next */
default:
Expand All @@ -169,7 +191,7 @@ export class WizardComponent implements OnChanges, AfterContentInit {
/**
* Initialization work
*/
ngAfterContentInit(): void {
public ngAfterContentInit(): void {
// add a subscriber to the wizard steps QueryList to listen to changes in the DOM
this.wizardSteps.changes.subscribe(changedWizardSteps => {
this.model.updateWizardSteps(changedWizardSteps.toArray());
Expand All @@ -179,9 +201,20 @@ export class WizardComponent implements OnChanges, AfterContentInit {
this.model.disableNavigationBar = this.disableNavigationBar;
this.model.defaultStepIndex = this.defaultStepIndex;
this.model.updateWizardSteps(this.wizardSteps.toArray());
this.model.updateNavigationMode(this.navigationMode);
this.updateNavigationMode(this.navigationMode);

// finally reset the whole wizard state
this.navigation.reset();
}

/**
* Updates the navigation mode for this wizard component.
*
* Initially the wizard component uses the navigation mode specified in the [[navigationMode]] input
* or the default navigation mode if the [[navigationMode]] input is not defined.
* Use this method to select a different navigation mode after the wizard component is initialized.
*/
public updateNavigationMode(navigationModeInput: NavigationModeInput) {
madoar marked this conversation as resolved.
Show resolved Hide resolved
this.model.updateNavigationMode(this.navigationModeFactory.create(this, navigationModeInput));
}
}
4 changes: 2 additions & 2 deletions src/lib/directives/enable-back-links.directive.spec.ts
Expand Up @@ -31,11 +31,11 @@ class WizardTestComponent {

public completionStepExit: (direction: MovingDirection, source: number) => void = this.exitFrom;

enterInto(direction: MovingDirection, destination: number): void {
public enterInto(direction: MovingDirection, destination: number): void {
this.eventLog.push(`enter ${MovingDirection[direction]} ${destination}`);
}

exitFrom(direction: MovingDirection, source: number): void {
public exitFrom(direction: MovingDirection, source: number): void {
this.eventLog.push(`exit ${MovingDirection[direction]} ${source}`);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/directives/enable-back-links.directive.ts
Expand Up @@ -45,7 +45,7 @@ export class EnableBackLinksDirective implements OnInit {
/**
* Initialization work
*/
ngOnInit(): void {
public ngOnInit(): void {
this.completionStep.canExit = true;
this.completionStep.stepExit = this.stepExit;
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/directives/go-to-step.directive.spec.ts
Expand Up @@ -47,7 +47,7 @@ class WizardTestComponent {

public eventLog: Array<string> = [];

finalizeStep(stepIndex: number): void {
public finalizeStep(stepIndex: number): void {
this.eventLog.push(`finalize ${stepIndex}`);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/directives/next-step.directive.spec.ts
Expand Up @@ -31,7 +31,7 @@ import {NextStepDirective} from './next-step.directive';
class WizardTestComponent {
public eventLog: Array<string> = [];

finalizeStep(stepIndex: number): void {
public finalizeStep(stepIndex: number): void {
this.eventLog.push(`finalize ${stepIndex}`);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/directives/optional-step.directive.ts
Expand Up @@ -38,7 +38,7 @@ export class OptionalStepDirective implements OnInit {
/**
* Initialization work
*/
ngOnInit(): void {
public ngOnInit(): void {
this.wizardStep.optional = true;
}
}
2 changes: 1 addition & 1 deletion src/lib/directives/previous-step.directive.spec.ts
Expand Up @@ -31,7 +31,7 @@ import {PreviousStepDirective} from './previous-step.directive';
class WizardTestComponent {
public eventLog: Array<string> = [];

finalizeStep(stepIndex: number): void {
public finalizeStep(stepIndex: number): void {
this.eventLog.push(`finalize ${stepIndex}`);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/directives/selected-step.directive.ts
Expand Up @@ -29,7 +29,7 @@ export class SelectedStepDirective implements OnInit {
/**
* Initialization work
*/
ngOnInit(): void {
public ngOnInit(): void {
this.wizardStep.defaultSelected = true;
}
}
4 changes: 2 additions & 2 deletions src/lib/directives/wizard-completion-step.directive.spec.ts
Expand Up @@ -29,11 +29,11 @@ class WizardTestComponent {

public eventLog: Array<string> = [];

enterInto(direction: MovingDirection, destination: number): void {
public enterInto(direction: MovingDirection, destination: number): void {
this.eventLog.push(`enter ${MovingDirection[direction]} ${destination}`);
}

exitFrom(direction: MovingDirection, source: number): void {
public exitFrom(direction: MovingDirection, source: number): void {
this.eventLog.push(`exit ${MovingDirection[direction]} ${source}`);
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/lib/directives/wizard-step.directive.spec.ts
Expand Up @@ -29,11 +29,11 @@ class WizardTestComponent {

public eventLog: Array<string> = [];

enterInto(direction: MovingDirection, destination: number): void {
public enterInto(direction: MovingDirection, destination: number): void {
this.eventLog.push(`enter ${MovingDirection[direction]} ${destination}`);
}

exitFrom(direction: MovingDirection, source: number): void {
public exitFrom(direction: MovingDirection, source: number): void {
this.eventLog.push(`exit ${MovingDirection[direction]} ${source}`);
}
}
Expand Down
72 changes: 72 additions & 0 deletions src/lib/navigation/base-navigation-mode-factory.provider.ts
@@ -0,0 +1,72 @@
import {FreeNavigationMode} from './free-navigation-mode';
import {NavigationMode} from './navigation-mode.interface';
import {SemiStrictNavigationMode} from './semi-strict-navigation-mode';
import {StrictNavigationMode} from './strict-navigation-mode';
import {WizardComponent} from '../components/wizard.component';
import {NavigationModeInput} from './navigation-mode-input.interface';
import {NavigationModeFactory} from './navigation-mode-factory.interface';

/**
* A factory used to create [[NavigationMode]] instances
madoar marked this conversation as resolved.
Show resolved Hide resolved
*/
export class BaseNavigationModeFactory implements NavigationModeFactory {
madoar marked this conversation as resolved.
Show resolved Hide resolved

/**
* @inheritDoc
*/
public create(wizard: WizardComponent, navigationModeInput: NavigationModeInput): NavigationMode {
let navigationModeName: string;
if (typeof navigationModeInput === 'function') {
// input is a function
return navigationModeInput(wizard);
} else {
// input is a name
navigationModeName = navigationModeInput;
}
// create NavigationMode by name
return this.createByName(wizard, navigationModeName);
}

/**
* Create a [[NavigationMode]] for the given wizard instance by a navigation mode name
*
* @param wizard The wizard componenent where the created [[NavigationMode]] will be used
* @param navigationModeInput The name of a built-in navigation mode or a custom navigation mode
* @returns The created [[NavigationMode]]
*/
protected createByName(wizard: WizardComponent, navigationModeInput: string): NavigationMode {
switch (navigationModeInput) {
case 'free':
return new FreeNavigationMode(wizard.model);
case 'semi-strict':
return new SemiStrictNavigationMode(wizard.model);
case 'strict':
return new StrictNavigationMode(wizard.model);
default:
return !navigationModeInput ? this.createDefault(wizard) : this.createUnknown(wizard, navigationModeInput);
}
}

/**
* Create a [[NavigationMode]] for the given wizard instance which does not have a configured navigation mode
*
* @param wizard The wizard componenent where the created [[NavigationMode]] will be used
* @returns The created [[NavigationMode]]
*/
protected createDefault(wizard: WizardComponent): NavigationMode {
return new StrictNavigationMode(wizard.model);
}

/**
* Create a [[NavigationMode]] for the given wizard instance by a not recognized navigation mode name
*
* The base implementation always throws an Error.
*
* @param wizard The wizard componenent where the created [[NavigationMode]] will be used
* @param navigationModeInput The name of a custom navigation mode
* @returns The created [[NavigationMode]]
*/
protected createUnknown(wizard: WizardComponent, navigationModeInput: string): NavigationMode {
throw new Error(`Unknown navigation mode name: ${navigationModeInput}`);
}
}