diff --git a/src/components-examples/material/stepper/index.ts b/src/components-examples/material/stepper/index.ts
index 4cf6781d0871..3c8d2c61aa36 100644
--- a/src/components-examples/material/stepper/index.ts
+++ b/src/components-examples/material/stepper/index.ts
@@ -17,6 +17,7 @@ import {StepperHarnessExample} from './stepper-harness/stepper-harness-example';
import {StepperIntlExample} from './stepper-intl/stepper-intl-example';
import {StepperLazyContentExample} from './stepper-lazy-content/stepper-lazy-content-example';
import {StepperResponsiveExample} from './stepper-responsive/stepper-responsive-example';
+import {StepperHeaderPositionExample} from './stepper-header-position/stepper-header-position-example';
export {
StepperEditableExample,
@@ -30,6 +31,7 @@ export {
StepperVerticalExample,
StepperLazyContentExample,
StepperResponsiveExample,
+ StepperHeaderPositionExample,
};
const EXAMPLES = [
@@ -44,6 +46,7 @@ const EXAMPLES = [
StepperVerticalExample,
StepperLazyContentExample,
StepperResponsiveExample,
+ StepperHeaderPositionExample,
];
@NgModule({
diff --git a/src/components-examples/material/stepper/stepper-header-position/stepper-header-position-example.css b/src/components-examples/material/stepper/stepper-header-position/stepper-header-position-example.css
new file mode 100644
index 000000000000..7432308753e6
--- /dev/null
+++ b/src/components-examples/material/stepper/stepper-header-position/stepper-header-position-example.css
@@ -0,0 +1 @@
+/** No CSS for this example */
diff --git a/src/components-examples/material/stepper/stepper-header-position/stepper-header-position-example.html b/src/components-examples/material/stepper/stepper-header-position/stepper-header-position-example.html
new file mode 100644
index 000000000000..7f092843f2f7
--- /dev/null
+++ b/src/components-examples/material/stepper/stepper-header-position/stepper-header-position-example.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+ Done
+ You are now done.
+
+
+
+
+
+
diff --git a/src/components-examples/material/stepper/stepper-header-position/stepper-header-position-example.ts b/src/components-examples/material/stepper/stepper-header-position/stepper-header-position-example.ts
new file mode 100644
index 000000000000..62b57ed53cb3
--- /dev/null
+++ b/src/components-examples/material/stepper/stepper-header-position/stepper-header-position-example.ts
@@ -0,0 +1,26 @@
+import {Component, OnInit} from '@angular/core';
+import {FormBuilder, FormGroup, Validators} from '@angular/forms';
+
+/**
+ * @title Stepper header position
+ */
+@Component({
+ selector: 'stepper-header-position-example',
+ templateUrl: 'stepper-header-position-example.html',
+ styleUrls: ['stepper-header-position-example.css'],
+})
+export class StepperHeaderPositionExample implements OnInit {
+ firstFormGroup: FormGroup;
+ secondFormGroup: FormGroup;
+
+ constructor(private _formBuilder: FormBuilder) {}
+
+ ngOnInit() {
+ this.firstFormGroup = this._formBuilder.group({
+ firstCtrl: ['', Validators.required],
+ });
+ this.secondFormGroup = this._formBuilder.group({
+ secondCtrl: ['', Validators.required],
+ });
+ }
+}
diff --git a/src/material/stepper/stepper.html b/src/material/stepper/stepper.html
index b074d0c2e6e2..dadc6b4f2860 100644
--- a/src/material/stepper/stepper.html
+++ b/src/material/stepper/stepper.html
@@ -1,6 +1,6 @@
-
+
-
+
diff --git a/src/material/stepper/stepper.md b/src/material/stepper/stepper.md
index 229fb7044498..d2e486892697 100644
--- a/src/material/stepper/stepper.md
+++ b/src/material/stepper/stepper.md
@@ -33,6 +33,13 @@ This behaviour is controlled by `labelPosition` property.
"file": "stepper-label-position-bottom-example.html",
"region": "label-position"}) -->
+#### Header position
+If you're using a horizontal stepper, you can control where the stepper's content is positioned
+using the `headerPosition` input. By default it's on top of the content, but it can also be placed
+under it.
+
+
+
### Stepper buttons
There are two button directives to support navigation between different steps:
`matStepperPrevious` and `matStepperNext`.
diff --git a/src/material/stepper/stepper.scss b/src/material/stepper/stepper.scss
index 1d73e0a0178a..6d0ba7c07f20 100644
--- a/src/material/stepper/stepper.scss
+++ b/src/material/stepper/stepper.scss
@@ -16,6 +16,10 @@
.mat-stepper-label-position-bottom & {
align-items: flex-start;
}
+
+ .mat-stepper-header-position-bottom & {
+ order: 1;
+ }
}
.mat-stepper-horizontal-line {
@@ -116,6 +120,11 @@
}
}
+.mat-horizontal-stepper-wrapper {
+ display: flex;
+ flex-direction: column;
+}
+
.mat-horizontal-stepper-content {
outline: 0;
@@ -132,6 +141,10 @@
overflow: hidden;
padding: 0 stepper-variables.$side-gap stepper-variables.$side-gap stepper-variables.$side-gap;
+
+ .mat-stepper-header-position-bottom & {
+ padding: stepper-variables.$side-gap stepper-variables.$side-gap 0 stepper-variables.$side-gap;
+ }
}
.mat-vertical-content-container {
diff --git a/src/material/stepper/stepper.spec.ts b/src/material/stepper/stepper.spec.ts
index b627a1e79f5a..cf36fd75ebee 100644
--- a/src/material/stepper/stepper.spec.ts
+++ b/src/material/stepper/stepper.spec.ts
@@ -1238,6 +1238,19 @@ describe('MatStepper', () => {
expect(interactedSteps).toEqual([0, 1, 2]);
subscription.unsubscribe();
});
+
+ it('should set a class on the host if the header is positioned at the bottom', () => {
+ const fixture = createComponent(SimpleMatHorizontalStepperApp);
+ fixture.detectChanges();
+ const stepperHost = fixture.nativeElement.querySelector('.mat-stepper-horizontal');
+
+ expect(stepperHost.classList).not.toContain('mat-stepper-header-position-bottom');
+
+ fixture.componentInstance.headerPosition = 'bottom';
+ fixture.detectChanges();
+
+ expect(stepperHost.classList).toContain('mat-stepper-header-position-bottom');
+ });
});
describe('linear stepper with valid step', () => {
@@ -1815,7 +1828,10 @@ class MatHorizontalStepperWithErrorsApp implements OnInit {
@Component({
template: `
-
+
Step 1
Content 1
@@ -1847,6 +1863,7 @@ class SimpleMatHorizontalStepperApp {
disableRipple = false;
stepperTheme: ThemePalette;
secondStepTheme: ThemePalette;
+ headerPosition: string;
}
@Component({
diff --git a/src/material/stepper/stepper.ts b/src/material/stepper/stepper.ts
index 55e8b6d155cb..078986f25280 100644
--- a/src/material/stepper/stepper.ts
+++ b/src/material/stepper/stepper.ts
@@ -131,6 +131,7 @@ export class MatStep extends CdkStep implements ErrorStateMatcher, AfterContentI
'orientation === "horizontal" && labelPosition == "end"',
'[class.mat-stepper-label-position-bottom]':
'orientation === "horizontal" && labelPosition == "bottom"',
+ '[class.mat-stepper-header-position-bottom]': 'headerPosition === "bottom"',
'[attr.aria-orientation]': 'orientation',
'role': 'tablist',
},
@@ -171,6 +172,13 @@ export class MatStepper extends CdkStepper implements AfterContentInit {
@Input()
labelPosition: 'bottom' | 'end' = 'end';
+ /**
+ * Position of the stepper's header.
+ * Only applies in the `horizontal` orientation.
+ */
+ @Input()
+ headerPosition: 'top' | 'bottom' = 'top';
+
/** Consumer-specified template-refs to be used to override the header icons. */
_iconOverrides: Record> = {};
diff --git a/tools/public_api_guard/material/stepper.md b/tools/public_api_guard/material/stepper.md
index 9bd7ba21bae9..3144750cb0c2 100644
--- a/tools/public_api_guard/material/stepper.md
+++ b/tools/public_api_guard/material/stepper.md
@@ -132,6 +132,7 @@ export class MatStepper extends CdkStepper implements AfterContentInit {
readonly _animationDone: Subject;
color: ThemePalette;
disableRipple: boolean;
+ headerPosition: 'top' | 'bottom';
_iconOverrides: Record>;
_icons: QueryList;
labelPosition: 'bottom' | 'end';
@@ -143,7 +144,7 @@ export class MatStepper extends CdkStepper implements AfterContentInit {
readonly steps: QueryList;
_steps: QueryList;
// (undocumented)
- static ɵcmp: i0.ɵɵComponentDeclaration;
+ static ɵcmp: i0.ɵɵComponentDeclaration;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration;
}