feat(menu): align with 2018 material design spec (#12331)

Aligns the menu component with the latest version of the Material design spec. Includes making `overlapTrigger` false by default.
This commit is contained in:
Kristiyan Kostadinov
2018-08-31 02:28:20 +02:00
committed by Jeremy Elbourn
parent 2433b82067
commit c812268ac3
8 changed files with 49 additions and 52 deletions
+1 -1
View File
@@ -156,7 +156,7 @@ describe('menu', () => {
await expectAlignedWith(page.menu(), '#trigger');
});
it('should align overlay end to origin end when x-position is "before"', async() => {
it('should align overlay end to origin end when x-position is "before"', async () => {
page.beforeTrigger().click();
const trigger = await page.beforeTrigger().getLocation();
+6 -6
View File
@@ -137,7 +137,7 @@
<div class="demo-menu">
<div class="demo-menu-section">
<p>overlapTrigger: false</p>
<p>overlapTrigger: true</p>
<mat-toolbar>
<button mat-icon-button [mat-menu-trigger-for]="menuOverlay">
@@ -145,7 +145,7 @@
</button>
</mat-toolbar>
<mat-menu [overlapTrigger]="false" #menuOverlay="matMenu">
<mat-menu [overlapTrigger]="true" #menuOverlay="matMenu">
<button mat-menu-item *ngFor="let item of items" [disabled]="item.disabled">
{{ item.text }}
</button>
@@ -153,7 +153,7 @@
</div>
<div class="demo-menu-section">
<p>
Position x: before, overlapTrigger: false
Position x: before, overlapTrigger: true
</p>
<mat-toolbar class="demo-end-icon">
<button mat-icon-button [mat-menu-trigger-for]="posXMenuOverlay">
@@ -161,7 +161,7 @@
</button>
</mat-toolbar>
<mat-menu xPosition="before" [overlapTrigger]="false" #posXMenuOverlay="matMenu">
<mat-menu xPosition="before" [overlapTrigger]="true" #posXMenuOverlay="matMenu">
<button mat-menu-item *ngFor="let item of iconItems" [disabled]="item.disabled">
<mat-icon>{{ item.icon }}</mat-icon>
{{ item.text }}
@@ -170,7 +170,7 @@
</div>
<div class="demo-menu-section">
<p>
Position y: above, overlapTrigger: false
Position y: above, overlapTrigger: true
</p>
<mat-toolbar>
<button mat-icon-button [mat-menu-trigger-for]="posYMenuOverlay">
@@ -178,7 +178,7 @@
</button>
</mat-toolbar>
<mat-menu yPosition="above" [overlapTrigger]="false" #posYMenuOverlay="matMenu">
<mat-menu yPosition="above" [overlapTrigger]="true" #posYMenuOverlay="matMenu">
<button mat-menu-item *ngFor="let item of items" [disabled]="item.disabled">
{{ item.text }}
</button>
+14 -4
View File
@@ -5,7 +5,7 @@
<button [matMenuTriggerFor]="menu" id="trigger">TRIGGER</button>
<button [matMenuTriggerFor]="menu" id="trigger-two">TRIGGER 2</button>
<mat-menu #menu="matMenu" yPosition="below" class="custom">
<mat-menu #menu="matMenu" yPosition="below" class="custom" overlapTrigger>
<button mat-menu-item (click)="selected='one'">One</button>
<button mat-menu-item (click)="selected='two'">Two</button>
<button mat-menu-item (click)="selected='three'" disabled>Three</button>
@@ -15,20 +15,30 @@
<button [matMenuTriggerFor]="beforeMenu" id="before-t">
BEFORE
</button>
<mat-menu xPosition="before" yPosition="below" class="before" #beforeMenu="matMenu">
<mat-menu
xPosition="before"
yPosition="below"
class="before"
#beforeMenu="matMenu"
overlapTrigger>
<button mat-menu-item>Item</button>
</mat-menu>
<div class="bottom-row">
<button [matMenuTriggerFor]="aboveMenu" id="above-t">ABOVE</button>
<mat-menu yPosition="above" class="above" #aboveMenu="matMenu">
<mat-menu yPosition="above" class="above" #aboveMenu="matMenu" overlapTrigger>
<button mat-menu-item>Item</button>
</mat-menu>
<button [matMenuTriggerFor]="combined" id="combined-t">
BOTH
</button>
<mat-menu xPosition="before" yPosition="above" class="combined" #combined="matMenu">
<mat-menu
xPosition="before"
yPosition="above"
class="combined"
#combined="matMenu"
overlapTrigger>
<button mat-menu-item>Item</button>
</mat-menu>
+3 -3
View File
@@ -40,9 +40,9 @@
@mixin mat-menu-typography($config) {
.mat-menu-item {
font: {
family: mat-font-family($config, subheading-2);
size: mat-font-size($config, subheading-2);
weight: mat-font-weight($config, subheading-2);
family: mat-font-family($config, body-1);
size: mat-font-size($config, body-1);
weight: mat-font-weight($config, body-1);
}
}
}
+5 -14
View File
@@ -14,7 +14,6 @@ import{
transition,
query,
group,
sequence,
AnimationTriggerMetadata,
} from '@angular/animations';
@@ -38,21 +37,13 @@ export const matMenuAnimations: {
transformMenu: trigger('transformMenu', [
state('void', style({
opacity: 0,
// This starts off from 0.01, instead of 0, because there's an issue in the Angular animations
// as of 4.2, which causes the animation to be skipped if it starts from 0.
transform: 'scale(0.01, 0.01)'
transform: 'scale(0.8)'
})),
transition('void => enter', sequence([
query('.mat-menu-content', style({opacity: 0})),
animate('100ms linear', style({opacity: 1, transform: 'scale(1, 0.5)'})),
group([
query('.mat-menu-content', animate('400ms cubic-bezier(0.55, 0, 0.55, 0.2)',
style({opacity: 1})
)),
animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({transform: 'scale(1, 1)'})),
])
transition('void => enter', group([
query('.mat-menu-content', animate('100ms linear', style({opacity: 1}))),
animate('120ms cubic-bezier(0, 0, 0.2, 1)', style({transform: 'scale(1)'})),
])),
transition('* => void', animate('150ms 50ms linear', style({opacity: 0})))
transition('* => void', animate('100ms 25ms linear', style({opacity: 0})))
]),
+2 -2
View File
@@ -69,7 +69,7 @@ export const MAT_MENU_DEFAULT_OPTIONS =
/** @docs-private */
export function MAT_MENU_DEFAULT_OPTIONS_FACTORY(): MatMenuDefaultOptions {
return {
overlapTrigger: true,
overlapTrigger: false,
xPosition: 'after',
yPosition: 'below',
backdropClass: 'cdk-overlay-transparent-backdrop',
@@ -79,7 +79,7 @@ export function MAT_MENU_DEFAULT_OPTIONS_FACTORY(): MatMenuDefaultOptions {
* Start elevation for the menu panel.
* @docs-private
*/
const MAT_MENU_BASE_ELEVATION = 2;
const MAT_MENU_BASE_ELEVATION = 4;
@Component({
+2 -2
View File
@@ -5,11 +5,11 @@
@import '../../cdk/a11y/a11y';
$mat-menu-vertical-padding: 8px !default;
$mat-menu-border-radius: 2px !default;
$mat-menu-border-radius: 4px !default;
$mat-menu-submenu-indicator-size: 10px !default;
.mat-menu-panel {
@include mat-menu-base(2);
@include mat-menu-base(4);
max-height: calc(100vh - #{$mat-menu-item-height});
border-radius: $mat-menu-border-radius;
outline: 0;
+16 -20
View File
@@ -625,7 +625,7 @@ describe('MatMenu', () => {
let panel = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement;
expect(Math.floor(panel.getBoundingClientRect().bottom))
.toBe(Math.floor(trigger.getBoundingClientRect().bottom), 'Expected menu to open above');
.toBe(Math.floor(trigger.getBoundingClientRect().top), 'Expected menu to open above');
fixture.componentInstance.trigger.closeMenu();
fixture.detectChanges();
@@ -638,7 +638,7 @@ describe('MatMenu', () => {
panel = overlayContainerElement.querySelector('.mat-menu-panel') as HTMLElement;
expect(Math.floor(panel.getBoundingClientRect().top))
.toBe(Math.floor(trigger.getBoundingClientRect().top), 'Expected menu to open below');
.toBe(Math.floor(trigger.getBoundingClientRect().bottom), 'Expected menu to open below');
});
});
@@ -671,7 +671,7 @@ describe('MatMenu', () => {
// The y-position of the overlay should be unaffected, as it can already fit vertically
expect(Math.floor(overlayRect.top))
.toBe(Math.floor(triggerRect.top),
.toBe(Math.floor(triggerRect.bottom),
`Expected menu top position to be unchanged if it can fit in the viewport.`);
});
@@ -691,11 +691,8 @@ describe('MatMenu', () => {
const triggerRect = trigger.getBoundingClientRect();
const overlayRect = overlayPane.getBoundingClientRect();
// In "above" position, the bottom edges of the overlay and the origin are aligned.
// To find the overlay top, subtract the menu height from the origin's bottom edge.
const expectedTop = triggerRect.bottom - overlayRect.height;
expect(Math.floor(overlayRect.top))
.toBe(Math.floor(expectedTop),
expect(Math.floor(overlayRect.bottom))
.toBe(Math.floor(triggerRect.top),
`Expected menu to open in "above" position if "below" position wouldn't fit.`);
// The x-position of the overlay should be unaffected, as it can already fit horizontally
@@ -722,14 +719,13 @@ describe('MatMenu', () => {
const overlayRect = overlayPane.getBoundingClientRect();
const expectedLeft = triggerRect.right - overlayRect.width;
const expectedTop = triggerRect.bottom - overlayRect.height;
expect(Math.floor(overlayRect.left))
.toBe(Math.floor(expectedLeft),
`Expected menu to open in "before" position if "after" position wouldn't fit.`);
expect(Math.floor(overlayRect.top))
.toBe(Math.floor(expectedTop),
expect(Math.floor(overlayRect.bottom))
.toBe(Math.floor(triggerRect.top),
`Expected menu to open in "above" position if "below" position wouldn't fit.`);
});
@@ -753,7 +749,7 @@ describe('MatMenu', () => {
// As designated "above" position won't fit on screen, the menu should fall back
// to "below" mode, where the top edges of the overlay and trigger are aligned.
expect(Math.floor(overlayRect.top))
.toBe(Math.floor(triggerRect.top),
.toBe(Math.floor(triggerRect.bottom),
`Expected menu to open in "below" position if "above" position wouldn't fit.`);
});
@@ -1416,11 +1412,11 @@ describe('MatMenu', () => {
const menus = overlay.querySelectorAll('.mat-menu-panel');
expect(menus[0].classList)
.toContain('mat-elevation-z2', 'Expected root menu to have base elevation.');
.toContain('mat-elevation-z4', 'Expected root menu to have base elevation.');
expect(menus[1].classList)
.toContain('mat-elevation-z3', 'Expected first sub-menu to have base elevation + 1.');
.toContain('mat-elevation-z5', 'Expected first sub-menu to have base elevation + 1.');
expect(menus[2].classList)
.toContain('mat-elevation-z4', 'Expected second sub-menu to have base elevation + 2.');
.toContain('mat-elevation-z6', 'Expected second sub-menu to have base elevation + 2.');
});
it('should update the elevation when the same menu is opened at a different depth',
@@ -1438,7 +1434,7 @@ describe('MatMenu', () => {
let lastMenu = overlay.querySelectorAll('.mat-menu-panel')[2];
expect(lastMenu.classList)
.toContain('mat-elevation-z4', 'Expected menu to have the base elevation plus two.');
.toContain('mat-elevation-z6', 'Expected menu to have the base elevation plus two.');
(overlay.querySelector('.cdk-overlay-backdrop')! as HTMLElement).click();
fixture.detectChanges();
@@ -1454,9 +1450,9 @@ describe('MatMenu', () => {
lastMenu = overlay.querySelector('.mat-menu-panel') as HTMLElement;
expect(lastMenu.classList)
.not.toContain('mat-elevation-z4', 'Expected menu not to maintain old elevation.');
.not.toContain('mat-elevation-z6', 'Expected menu not to maintain old elevation.');
expect(lastMenu.classList)
.toContain('mat-elevation-z2', 'Expected menu to have the proper updated elevation.');
.toContain('mat-elevation-z4', 'Expected menu to have the proper updated elevation.');
}));
it('should not increase the elevation if the user specified a custom one', () => {
@@ -1679,7 +1675,7 @@ describe('MatMenu default overrides', () => {
declarations: [SimpleMenu, FakeIcon],
providers: [{
provide: MAT_MENU_DEFAULT_OPTIONS,
useValue: {overlapTrigger: false, xPosition: 'before', yPosition: 'above'},
useValue: {overlapTrigger: true, xPosition: 'before', yPosition: 'above'},
}],
}).compileComponents();
}));
@@ -1689,7 +1685,7 @@ describe('MatMenu default overrides', () => {
fixture.detectChanges();
const menu = fixture.componentInstance.menu;
expect(menu.overlapTrigger).toBe(false);
expect(menu.overlapTrigger).toBe(true);
expect(menu.xPosition).toBe('before');
expect(menu.yPosition).toBe('above');
});