Skip to content

Commit ea13cd7

Browse files
committed
- added MovingDirection parameter to inital stepEnter eventEmitter call of the first step
- added new tests for the navigation bar and the wizard step - improved and extended README.md
1 parent 95fcd4f commit ea13cd7

File tree

4 files changed

+353
-31
lines changed

4 files changed

+353
-31
lines changed

README.md

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Ng2Wizard
1+
# Overview ng2-wizard
22

33
[![Build Status](https://travis-ci.org/madoar/ng2-wizard.png)](https://travis-ci.org/madoar/ng2-wizard)
44
[![Dependency Status](https://david-dm.org/madoar/ng2-wizard.svg)](https://david-dm.org/madoar/ng2-wizard)
@@ -17,15 +17,16 @@ Run `npm test` to execute the unit tests via [Karma](https://karma-runner.github
1717

1818
## How to use the wizard
1919

20-
To use the this wizard component in an angular 2 project simply add a wizard component as followed to the html template of your component:
20+
To use the this wizard component in an angular 2 project simply add a wizard component to the html template of your component, like this:
2121

22-
```angular2html
22+
```html
2323
<wizard>
2424
<wizard-step title="Title of step 1">
2525
Content of Step 1
2626
<button type="button" nextStep>Next Step</button>
27+
<button type="button" goToStep="2">Go directly to third Step</button>
2728
</wizard-step>
28-
<wizard-step title="Title of step 2">
29+
<wizard-step title="Title of step 2" optionalStep>
2930
Content of Step 2
3031
<button type="button" previousStep>Go to previous step</button>
3132
<button type="button" nextStep>Go to next step</button>
@@ -40,35 +41,63 @@ To use the this wizard component in an angular 2 project simply add a wizard com
4041

4142
### <wizard>
4243
`<wizard>...</wizard>` is the environment, in which you define your wizard.
43-
This contains all steps that belong to your wizard.
44+
This environment must contain all steps that make up your wizard.
4445

4546
### <wizard-step>
4647
`<wizard-step>...</wizard-step>` is the wizard step environment.
47-
Every wizard step must be defined inside its own `<wizard-step></wizard-step>` environment.
48-
If you need to call a function to do some initializing work before entering a wizard step you can add `stepEnter` to the wizard step environment like this:
48+
Every step that your wizard contains must be defined inside its own `<wizard-step></wizard-step>` environment.
49+
A wizard must contain a title, which is shown in the navigation bar of the wizard.
50+
The title of a step can be set by adding a `title` attribute to the step definition.
4951

50-
```angular2html
51-
<wizard-step title="Second Step" (stepenter)="enterSecondStep()"></wizard-step>
52+
If you need to call a function to do some initialisation work before entering a wizard step you can add a `stepEnter` attribute to the wizard step environment like this:
53+
54+
```html
55+
<wizard-step title="Second Step" (stepEnter)="enterSecondStep($event)"></wizard-step>
5256
```
5357

5458
This leads to the calling of the `enterSecondStep` function when the wizard moves to this step.
55-
When the first step of the wizard contains a `stepEnter` function, it's also called after the wizard was initialized.
59+
When the first step of the wizard contains a `stepEnter` function, it not only gets called
60+
when the used moves back from a later step to the first step, but also after the wizard is initialized.
61+
The event emitter will call the given function with a parameter that contains the `MovingDirection` of the user.
62+
If the user went backwards, like from the third step to the second or first step, then `MovingDirection.Backwards` will be passed to the function.
63+
If the user went forwards `MovingDirection.Forwards` will be passed to the function.
64+
65+
Similarly you can add a `stepExit` attribute to the wizard step environment, if you want to call a function every time a wizard step is exited
66+
either by pressing on a component with a `nextStep` or `previousStep` directive, or by a click on the navigation bar.
67+
`stepExit`, like `stepEnter` can call the given function with an argument of type `MovingDirection` that signalises in which direction the step was exited.
5668

57-
Similarly you can add `stepExit` to the wizard step environment if you want to call a function every time a wizard step is exited,
58-
either by pressing on a component with a `nextStep` or `previousStep` directive or by a click on the navigation bar.
69+
### [optionalStep]
70+
If you need to define an optional step, that doesn't need to be done to continue to the next steps, you can define an optional step
71+
by adding the `optionalStep` directive to the step you want to define as optional.
5972

6073
### [nextStep]
61-
By adding `nextStep` to a button or a link you automatically add a `onClick` listener to the button or link, that leads to the next step.
74+
By adding a `nextStep` directive to a button or a link inside a step, you automatically add a `onClick` listener to the button or link, that leads to the next step.
6275
This listener will automatically change the currently selected wizard step to the next wizard step after a click on the component.
6376

64-
If you want to call a function only after pressing on a component with a `nextStep` directive you can add the a function to the component declaration of the component tagged with `nextStep` like this:
77+
If you want to call a function only after pressing on a component with a `nextStep` directive you can add the a function
78+
to the component declaration of the component tagged with `nextStep` like this:
6579

66-
```angular2html
80+
```html
6781
<button (finalize)="finalizeStep()" nextStep>Next Step</button>
6882
```
6983

70-
This leads to a call of the function `finalizeStep` everytime the button is pressed.
84+
This leads to a call of the function `finalizeStep` every time, the button is pressed.
7185

7286
### [previousStep]
73-
By adding `previousStep` to a button or a link you automatically add a `onClick` listener to the button or link, that leads to the previous step.
87+
By adding a `previousStep` directive to a button or a link, you automatically add a `onClick` listener to the button or link, that changes your wizard to the previous step.
7488
This listener will automatically change the currently selected wizard step to the previous wizard step after a click on the component.
89+
90+
### [goToStep]
91+
In addition to the `previousStep` and `nextStep` directives the `goToStep` directive is available to move between steps.
92+
The `goToStep` directive must receive an argument, that tells the directive to which the button should link:
93+
94+
```html
95+
<button goToStep="2" (finalize)="finalizeStep()">Go directly to the third Step</button>
96+
```
97+
98+
In the previous example the button will move the user automatically to the third step, after the user pressed onto it.
99+
This makes it possible to directly jump to all already completed steps and to the first not completed optional or default (not optional) next step,
100+
which will set the current as completed and makes it possible to jump over steps defined as optional steps.
101+
102+
Like the `nextStep` directive the `goToStep` directive provides a `finalize` output, that will be called every time,
103+
the current step was successfully left by clicking on the button containg the `goToStep` directive.

src/components/components/wizard-navigation-bar.component.spec.ts

Lines changed: 215 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
/* tslint:disable:no-unused-variable */
22
import {async, ComponentFixture, TestBed} from "@angular/core/testing";
3-
import {QueryList, ViewChild, Component, ContentChild, OnInit} from "@angular/core";
3+
import {ViewChild, Component} from "@angular/core";
44
import {WizardNavigationBarComponent} from "./wizard-navigation-bar.component";
55
import {WizardComponent} from "./wizard.component";
66
import {WizardStepComponent} from "./wizard-step.component";
77
import {GoToStepDirective} from "../directives/go-to-step.directive";
88
import {By} from "@angular/platform-browser";
9+
import {OptionalStepDirective} from "../directives/optional-step.directive";
910

1011
@Component({
1112
selector: 'test-wizard',
1213
template: `
1314
<wizard>
1415
<wizard-step title='Steptitle 1'>Step 1</wizard-step>
15-
<wizard-step title='Steptitle 2'>Step 2</wizard-step>
16+
<wizard-step title='Steptitle 2' optionalStep>Step 2</wizard-step>
1617
<wizard-step title='Steptitle 3'>Step 3</wizard-step>
1718
</wizard>
1819
`
@@ -28,7 +29,7 @@ describe('WizardNavigationBarComponent', () => {
2829

2930
beforeEach(async(() => {
3031
TestBed.configureTestingModule({
31-
declarations: [ WizardComponent, WizardStepComponent, WizardNavigationBarComponent, WizardTestComponent, GoToStepDirective ]
32+
declarations: [ WizardComponent, WizardStepComponent, WizardNavigationBarComponent, WizardTestComponent, GoToStepDirective, OptionalStepDirective ]
3233
})
3334
.compileComponents();
3435
}));
@@ -43,4 +44,215 @@ describe('WizardNavigationBarComponent', () => {
4344
expect(wizardTest).toBeTruthy();
4445
expect(wizardTestFixture.debugElement.query(By.css('wizard-navigation-bar'))).toBeTruthy();
4546
});
47+
48+
it('should create only one navigation bar', () => {
49+
expect(wizardTest).toBeTruthy();
50+
expect(wizardTestFixture.debugElement.queryAll(By.css('wizard-navigation-bar')).length).toBe(1);
51+
});
52+
53+
it('should show the initial step correctly', () => {
54+
const navBar = wizardTestFixture.debugElement.query(By.css('wizard-navigation-bar'));
55+
56+
// the first step is the current step
57+
expect(navBar.query(By.css('li.current'))).toBeTruthy();
58+
expect(navBar.query(By.css('li.current'))).toBe(navBar.query(By.css('li:first-child')));
59+
expect(navBar.queryAll(By.css('li.current')).length).toBe(1);
60+
61+
// no step is currently marked as done
62+
expect(navBar.query(By.css('li.done'))).toBeNull();
63+
64+
// no step is marked as editing
65+
expect(navBar.query(By.css('li.editing'))).toBeNull();
66+
67+
// only the second step is marked as optional
68+
expect(navBar.query(By.css('li.optional'))).toBeTruthy();
69+
expect(navBar.query(By.css('li.optional'))).toBe(navBar.query(By.css('li:nth-child(2)')));
70+
expect(navBar.queryAll(By.css('li.optional')).length).toBe(1);
71+
72+
// only the third step is marked as default (neither done, current or optional)
73+
expect(navBar.query(By.css('li.default'))).toBeTruthy();
74+
expect(navBar.query(By.css('li.default'))).toBe(navBar.query(By.css('li:nth-child(3)')));
75+
expect(navBar.queryAll(By.css('li.default')).length).toBe(1);
76+
});
77+
78+
it('should show the second step correctly', () => {
79+
const navBar = wizardTestFixture.debugElement.query(By.css('wizard-navigation-bar'));
80+
81+
// go to second step
82+
wizardTest.wizard.goToNextStep();
83+
wizardTestFixture.detectChanges();
84+
85+
// the second step is the current step
86+
expect(navBar.query(By.css('li.current'))).toBeTruthy();
87+
expect(navBar.query(By.css('li.current'))).toBe(navBar.query(By.css('li:nth-child(2)')));
88+
expect(navBar.queryAll(By.css('li.current')).length).toBe(1);
89+
90+
// the first step should be marked as done
91+
expect(navBar.query(By.css('li.done'))).toBeTruthy();
92+
expect(navBar.query(By.css('li.done'))).toBe(navBar.query(By.css('li:nth-child(1)')));
93+
expect(navBar.queryAll(By.css('li.done')).length).toBe(1);
94+
95+
// no step is marked as editing
96+
expect(navBar.query(By.css('li.editing'))).toBeNull();
97+
98+
// no step is marked as optional, because the optional step is the current step
99+
expect(navBar.query(By.css('li.optional'))).toBeNull();
100+
101+
// only the third step is marked as default (neither done, current or optional)
102+
expect(navBar.query(By.css('li.default'))).toBeTruthy();
103+
expect(navBar.query(By.css('li.default'))).toBe(navBar.query(By.css('li:nth-child(3)')));
104+
expect(navBar.queryAll(By.css('li.default')).length).toBe(1);
105+
});
106+
107+
it('should show the third step correctly', () => {
108+
const navBar = wizardTestFixture.debugElement.query(By.css('wizard-navigation-bar'));
109+
110+
// go to second step
111+
wizardTest.wizard.goToNextStep();
112+
// go to third step
113+
wizardTest.wizard.goToNextStep();
114+
wizardTestFixture.detectChanges();
115+
116+
// the third step is the current step
117+
expect(navBar.query(By.css('li.current'))).toBeTruthy();
118+
expect(navBar.query(By.css('li.current'))).toBe(navBar.query(By.css('li:nth-child(3)')));
119+
expect(navBar.queryAll(By.css('li.current')).length).toBe(1);
120+
121+
// the first and second step should be marked as done
122+
expect(navBar.query(By.css('li.done'))).toBeTruthy();
123+
expect(navBar.query(By.css('li.done:first-child'))).toBe(navBar.query(By.css('li:nth-child(1)')));
124+
expect(navBar.query(By.css('li.done:nth-child(2)'))).toBe(navBar.query(By.css('li:nth-child(2)')));
125+
expect(navBar.queryAll(By.css('li.done')).length).toBe(2);
126+
127+
// no step is marked as editing
128+
expect(navBar.query(By.css('li.editing'))).toBeNull();
129+
130+
// no step is marked as optional, because the optional step is a "done" step
131+
expect(navBar.query(By.css('li.optional'))).toBeNull();
132+
133+
// no step is marked as default (neither done, current or optional)
134+
expect(navBar.query(By.css('li.default'))).toBeNull();
135+
});
136+
137+
it('should show the third step correctly, after jump from first to third step', () => {
138+
const navBar = wizardTestFixture.debugElement.query(By.css('wizard-navigation-bar'));
139+
140+
// go to third step and jump over the optional second step
141+
wizardTest.wizard.goToStep(2);
142+
wizardTestFixture.detectChanges();
143+
144+
// the third step is the current step
145+
expect(navBar.query(By.css('li.current'))).toBeTruthy();
146+
expect(navBar.query(By.css('li.current'))).toBe(navBar.query(By.css('li:nth-child(3)')));
147+
expect(navBar.queryAll(By.css('li.current')).length).toBe(1);
148+
149+
// the first step should be marked as done
150+
expect(navBar.query(By.css('li.done'))).toBeTruthy();
151+
expect(navBar.query(By.css('li.done'))).toBe(navBar.query(By.css('li:nth-child(1)')));
152+
expect(navBar.queryAll(By.css('li.done')).length).toBe(1);
153+
154+
// no step is marked as editing
155+
expect(navBar.query(By.css('li.editing'))).toBeNull();
156+
157+
// the second step is marked as optional, because we jumped over it
158+
expect(navBar.query(By.css('li.optional'))).toBeTruthy();
159+
expect(navBar.query(By.css('li.optional'))).toBe(navBar.query(By.css('li:nth-child(2)')));
160+
expect(navBar.queryAll(By.css('li.optional')).length).toBe(1);
161+
162+
// no step is marked as default (neither done, current or optional)
163+
expect(navBar.query(By.css('li.default'))).toBeNull();
164+
});
165+
166+
it('should show the first step correctly, after going back from the second step to the first step', () => {
167+
const navBar = wizardTestFixture.debugElement.query(By.css('wizard-navigation-bar'));
168+
169+
// go to second step
170+
wizardTest.wizard.goToNextStep();
171+
// go back to first step
172+
wizardTest.wizard.goToPreviousStep();
173+
wizardTestFixture.detectChanges();
174+
175+
// no step is the current step
176+
expect(navBar.query(By.css('li.current'))).toBeNull();
177+
178+
// no step should be marked as done
179+
expect(navBar.query(By.css('li.done'))).toBeNull();
180+
181+
// the first step is marked as editing
182+
expect(navBar.query(By.css('li.editing'))).toBeTruthy();
183+
expect(navBar.query(By.css('li.editing'))).toBe(navBar.query(By.css('li:nth-child(1)')));
184+
expect(navBar.queryAll(By.css('li.editing')).length).toBe(1);
185+
186+
// the second step is marked as optional
187+
expect(navBar.query(By.css('li.optional'))).toBeTruthy();
188+
expect(navBar.query(By.css('li.optional'))).toBe(navBar.query(By.css('li:nth-child(2)')));
189+
expect(navBar.queryAll(By.css('li.optional')).length).toBe(1);
190+
191+
// no step is marked as default (neither done, current or optional)
192+
expect(navBar.query(By.css('li.default'))).toBeTruthy();
193+
expect(navBar.query(By.css('li.default'))).toBe(navBar.query(By.css('li:nth-child(3)')));
194+
expect(navBar.queryAll(By.css('li.default')).length).toBe(1);
195+
});
196+
197+
it('should show the first step correctly, after first jumping from the first to the third step and then back from the third step to the first step', () => {
198+
const navBar = wizardTestFixture.debugElement.query(By.css('wizard-navigation-bar'));
199+
200+
// go to third step, by jumping over the optional step
201+
wizardTest.wizard.goToStep(2);
202+
// go back to first step
203+
wizardTest.wizard.goToStep(0);
204+
wizardTestFixture.detectChanges();
205+
206+
// no step is the current step
207+
expect(navBar.query(By.css('li.current'))).toBeNull();
208+
209+
// no step should be marked as done
210+
expect(navBar.query(By.css('li.done'))).toBeNull();
211+
212+
// the first step is marked as editing
213+
expect(navBar.query(By.css('li.editing'))).toBeTruthy();
214+
expect(navBar.query(By.css('li.editing'))).toBe(navBar.query(By.css('li:nth-child(1)')));
215+
expect(navBar.queryAll(By.css('li.editing')).length).toBe(1);
216+
217+
// the second step is marked as optional
218+
expect(navBar.query(By.css('li.optional'))).toBeTruthy();
219+
expect(navBar.query(By.css('li.optional'))).toBe(navBar.query(By.css('li:nth-child(2)')));
220+
expect(navBar.queryAll(By.css('li.optional')).length).toBe(1);
221+
222+
// the third step is marked as default (neither done, current or optional)
223+
expect(navBar.query(By.css('li.default'))).toBeTruthy();
224+
expect(navBar.query(By.css('li.default'))).toBe(navBar.query(By.css('li:nth-child(3)')));
225+
expect(navBar.queryAll(By.css('li.default')).length).toBe(1);
226+
});
227+
228+
it('should show the second step correctly, after first jumping from the first to the third step and then back from the third step to the second step', () => {
229+
const navBar = wizardTestFixture.debugElement.query(By.css('wizard-navigation-bar'));
230+
231+
// go to third step, by jumping over the optional step
232+
wizardTest.wizard.goToStep(2);
233+
// go back to second step
234+
wizardTest.wizard.goToPreviousStep();
235+
wizardTestFixture.detectChanges();
236+
237+
// the second step is the current step
238+
expect(navBar.query(By.css('li.current'))).toBeTruthy();
239+
expect(navBar.query(By.css('li.current'))).toBe(navBar.query(By.css('li:nth-child(2)')));
240+
expect(navBar.queryAll(By.css('li.current')).length).toBe(1);
241+
242+
// the first step should be marked as done
243+
expect(navBar.query(By.css('li.done'))).toBeTruthy();
244+
expect(navBar.query(By.css('li.done'))).toBe(navBar.query(By.css('li:nth-child(1)')));
245+
expect(navBar.queryAll(By.css('li.done')).length).toBe(1);
246+
247+
// no step is marked as editing
248+
expect(navBar.query(By.css('li.editing'))).toBeNull();
249+
250+
// no step is marked as optional, because the optional step is the current step
251+
expect(navBar.query(By.css('li.optional'))).toBeNull();
252+
253+
// the third step is marked as default (neither done, current or optional)
254+
expect(navBar.query(By.css('li.default'))).toBeTruthy();
255+
expect(navBar.query(By.css('li.default'))).toBe(navBar.query(By.css('li:nth-child(3)')));
256+
expect(navBar.queryAll(By.css('li.default')).length).toBe(1);
257+
});
46258
});

0 commit comments

Comments
 (0)