npm package: @finlexlabs/stepper
Description: Library Name: finlex-stepper Package Name: @finlexlabs/stepper Repo Name: fx-ng-components
Latest version: 0.0.2


# Finlex Stepper (@finlexlabs/stepper)

Library Name: finlex-stepper
Package Name: @finlexlabs/stepper
Repo Name: fx-ng-components

--------------------------------
Steps to Build & Publish Library
--------------------------------

## Package Renaming

Go to `./src/finlex-stepper/package.json`
Rename package name from `finlex-stepper` to `@finlexlabs/stepper`


## Build

 `npm run build:stepper`

It will build finlex-stepper using ng-packagr.
The build artifacts will be stored in the `dist/` directory.


## Publishing

After building your library with `ng build:stepper`, go to the dist folder `cd dist/finlex-stepper` and run `npm publish`.

---

### NOTE: Finlex Stepper is a modified version of Material Stepper Angular (@angular/stepper module) that is built on top of Angular Stepper CDK so in case you're not familiar with them already, following recoures will be a good start before you understand finlex-form-control and its functionality:

#### Background knowledge:
1. [Read here](https://material.angular.io/components/stepper) an intro to Angular's Material Stepper.
2. [Read here](https://material.angular.io/cdk/stepper) an intro to Angular's Stepper CDK. 
3. [Read here](https://bitbucket.org/finlex-development-team/finlex-angular-packages/src/master/src/components/finlex-form-group/readme.md) for an intro into `FinlexFormGroup`, we use them in stepper to determine and revert if a stepControl has any changes. 

>Mostly all the behaviour of MatStepper is still possible. All the customizations from that behaviour are mentioned below.

---
## (TL;DR) Simple example to use finlex-stepper with form:

#### module.ts
```
import { FinlexStepperModule } from '@finlex/components/finlex-stepper';

@NgModule({
  imports: [
    FinlexStepperModule
]
})
export class ProductsModule { }

```

#### controller.ts
```

// It is not necessary to have a form for stepper
  constructor(
  ){}

// this will set this.stepper value
@ViewChild('stepper', { static: false }) stepper;

const stepCreateProduct: FormGroup = new FinlexFormGroup({
    name: new FinlexFormControl({
        placeholder: 'placeholder text',
        value: this.product.name
    })
})

// It's only required to use function with stepper.next() if we want to make an async call before allowing to change the step.
onActionBeforeNextStep(){
    // do some async action
    this.productService.save(this.stepCreateProduct)
        .subscribe(() => {
            this.stepCreateProduct.commitValue();
            this.stepper.next(); // this will change the step
        })
}
```

#### view.ts
```
    <finlex-stepper #stepper>

        <finlex-step [stepControl]="stepCreateProduct"
                     [stepControlValue]="product">
            <ng-template matStepLabel>{{'STEP_WITH_ASYCN_CALL'}}</ng-template>
            <finlex-form-control fxFlex="40"
                                    [control]="stepCreateProduct.get('name')">
            </finlex-form-control>
            <button mat-button
                    [disabled]="stepCreateProduct.invalid"
                    (click)="onActionBeforeNextStep()">
                {{'NEXT_LABEL' | translate}}
            </button>
        </finlex-step>

        <finlex-step>
            <ng-template matStepLabel>{{'STEP_WITHOUT_FORM'}}</ng-template>
            <button mat-button
                    matStepperPrevious>
                {{'NEXT_LABEL' | translate}}
            </button>
        </finlex-step>

    </finlex-stepper>
```

---


## FinlexStepperModule declarations

* There are no changes to the following directives and they behave so far as it is: [`MatStepperNext`](https://material.angular.io/components/stepper/api#MatStepperNext), [`MatStepperPrevious`](https://material.angular.io/components/stepper/api#MatStepperPrevious), [`MatStepLabel`](https://material.angular.io/components/stepper/api#MatStepLabel). Feel free to use Angular API Doc links to understand them.

* The stepper component called `finlex-stepper` is inspired by [`mat-vertical-stepper`](https://material.angular.io/components/stepper/api#MatStepper) (which also inherits `mat-stepper`).

* The step component called `finlex-step` inspired by [`mat-step`](https://material.angular.io/components/stepper/api#MatStep).
    * The FinlexStep has a new @Input() binding called `disable`. If a step has disable true, the header icon of the step is rendered with a special design (low opacity) and its not possible to click on the step header to open the step.

* The step header called `finlex-step-header` is inspired by [`mat-step-header`](https://material.angular.io/components/stepper/api#MatStepHeader) but this component is not so relavent since its only used for internal renderring of stepper and has no affect in usage.


---

## Customizations

1. The support for [`matStepperIcon`](https://material.angular.io/components/stepper/api#MatStepperIcon) template has been removed since our UI toolkit doesn't require this feature. Instead, we always show step number as the default icon for every step (in any step state).

2. FinlexStepper uses *ngIf directive to prevent the stepper from rendering all steps at once (and hiding them in DOM), which was the default behavior. Instead, the *ngIf ensures that only active step is rendered in DOM and therefore, when we change the step, the lifecycle of other step components is only triggered when that step is active.

3. If a `finlex-step` has `[disable]` binding as true, the step header icon will be styled differently and it will not be possible to transition to this step.

4. FinlexStepper does not use the `[linear]` binding of Material Stepper, instead it performs its own logic for similar behaviour by overriding the `(click)` binding of step header (when user clicks on other steps titles). This click function performs several checks before transition to another step:

    1. If the steps prior to the **clicked** step have `stepControl`, it checks that all the previous `stepControl`'s are in valid state. Otherwise, the transition to the step is denied (nothing happens when clicking on the header). E.G. User is on Step 2 and Step 3 is invalid, so user cannot click on Step 4 directly.

    2. If the **current** step has `stepControl` that is dirty, it checks whether the current step is valid before doing the transition. If not, it shows a dialog to warn the user that their changes will be reset if they continue to change the step directly (without saving). If user continues, the `stepControl` changes are **reset* before performing the transition.

    3. If the **current** step has `stepControl` that is dirty, it checks whether the step has any  **changes*. If yes, it shows a dialog to warn the user that their changes will be reset if they continue to change the step directly (without saving). If user continues, the `stepControl` changes are **reset* before performing the transition.

>To perofrm actions like "reset" and check if a `stepControl` has "changes", the `stepControl` should be of type [`FinlexFormGroup`](https://bitbucket.org/finlex-development-team/finlex-angular-packages/src/master/src/components/finlex-form-group/readme.md). To learn more about how FinlexFormGroup provides support for this, please read  [`here`](https://bitbucket.org/finlex-development-team/finlex-angular-packages/src/master/src/components/finlex-form-group/readme.md).

---

## Future Enhancement (suggestive):
* Remove the `stepControlValue` binding of `FinlexStep` and encapsulate the logic to check for changes of a form to `FinlexFormGroup`: https://finlex.atlassian.net/browse/PX-1139