mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
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:
committed by
Jeremy Elbourn
parent
2433b82067
commit
c812268ac3
@@ -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();
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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})))
|
||||
]),
|
||||
|
||||
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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
@@ -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');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user