') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); fix(overlay): proper backdrop stacking with multiple overlays by crisbeto · Pull Request #2276 · angular/components · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/lib/core/overlay/overlay-ref.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -101,13 +101,13 @@ export class OverlayRef implements PortalHost {
this._backdropElement.classList.add('cdk-overlay-backdrop');
this._backdropElement.classList.add(this._state.backdropClass);

this._pane.parentElement.appendChild(this._backdropElement);
// Insert the backdrop before the pane in the DOM order,
// in order to handle stacked overlays properly.
this._pane.parentElement.insertBefore(this._backdropElement, this._pane);

// Forward backdrop clicks such that the consumer of the overlay can perform whatever
// action desired when such a click occurs (usually closing the overlay).
this._backdropElement.addEventListener('click', () => {
this._backdropClick.next(null);
});
this._backdropElement.addEventListener('click', () => this._backdropClick.next(null));

// Add class to fade-in the backdrop after one frame.
requestAnimationFrame(() => {
Expand Down
16 changes: 16 additions & 0 deletions src/lib/core/overlay/overlay.spec.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -235,6 +235,22 @@ describe('Overlay', () => {
expect(backdrop.style.pointerEvents).toBe('none');
});

it('should insert the backdrop before the overlay pane in the DOM order', () => {
let overlayRef = overlay.create(config);
overlayRef.attach(componentPortal);

viewContainerFixture.detectChanges();

let backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop');
let pane = overlayContainerElement.querySelector('.cdk-overlay-pane');
let children = Array.prototype.slice.call(overlayContainerElement.children);

expect(children.indexOf(backdrop)).toBeGreaterThan(-1);
expect(children.indexOf(pane)).toBeGreaterThan(-1);
expect(children.indexOf(backdrop))
.toBeLessThan(children.indexOf(pane), 'Expected backdrop to be before the pane in the DOM');
});

});
});

Expand Down
3 changes: 1 addition & 2 deletions src/lib/core/style/_variables.scss
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,12 +24,11 @@ $z-index-drawer: 100 !default;
// stacking context for all overlays.
$cdk-z-index-overlay-container: 1000;
$cdk-z-index-overlay: 1000;
$cdk-z-index-overlay-backdrop: 1;
$cdk-z-index-overlay-backdrop: 1000;

// Background color for all of the backdrops
$cdk-overlay-dark-backdrop-background: rgba(0, 0, 0, 0.6);


// Global constants
$pi: 3.14159265;

Expand Down
4 changes: 2 additions & 2 deletions src/lib/menu/menu.spec.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -101,7 +101,7 @@ describe('MdMenu', () => {
fixture.componentInstance.trigger.openMenu();
fixture.detectChanges();

const overlayPane = overlayContainerElement.children[0];
const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane');
expect(overlayPane.getAttribute('dir')).toEqual('rtl');
});

Expand DownExpand Up@@ -248,7 +248,7 @@ describe('MdMenu', () => {
});

function getOverlayPane(): HTMLElement {
let pane = overlayContainerElement.children[0] as HTMLElement;
let pane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
pane.style.position = 'absolute';
return pane;
}
Expand Down
42 changes: 27 additions & 15 deletions src/lib/select/select.spec.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -104,7 +104,7 @@ describe('MdSelect', () => {
fixture.whenStable().then(() => {
trigger.click();
fixture.detectChanges();
const pane = overlayContainerElement.children[0] as HTMLElement;
const pane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
expect(pane.style.minWidth).toBe('200px');
});
}));
Expand DownExpand Up@@ -561,7 +561,7 @@ describe('MdSelect', () => {
* @param index The index of the option.
*/
function checkTriggerAlignedWithOption(index: number): void {
const overlayPane = overlayContainerElement.children[0] as HTMLElement;
const overlayPane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;

// We need to set the position to absolute, because the top/left positioning won't work
// since the component CSS isn't included in the tests.
Expand DownExpand Up@@ -599,7 +599,8 @@ describe('MdSelect', () => {
trigger.click();
fixture.detectChanges();

const overlayPane = overlayContainerElement.children[0] as HTMLElement;
const overlayPane =
overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
const scrollContainer = overlayPane.querySelector('.md-select-panel');

// The panel should be scrolled to 0 because centering the option is not possible.
Expand All@@ -616,7 +617,8 @@ describe('MdSelect', () => {
trigger.click();
fixture.detectChanges();

const overlayPane = overlayContainerElement.children[0] as HTMLElement;
const overlayPane =
overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
const scrollContainer = overlayPane.querySelector('.md-select-panel');

// The panel should be scrolled to 0 because centering the option is not possible.
Expand All@@ -633,7 +635,8 @@ describe('MdSelect', () => {
trigger.click();
fixture.detectChanges();

const overlayPane = overlayContainerElement.children[0] as HTMLElement;
const overlayPane =
overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
const scrollContainer = overlayPane.querySelector('.md-select-panel');

// The selected option should be scrolled to the center of the panel.
Expand All@@ -654,7 +657,8 @@ describe('MdSelect', () => {
trigger.click();
fixture.detectChanges();

const overlayPane = overlayContainerElement.children[0] as HTMLElement;
const overlayPane =
overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
const scrollContainer = overlayPane.querySelector('.md-select-panel');

// The selected option should be scrolled to the max scroll position.
Expand DownExpand Up@@ -687,7 +691,8 @@ describe('MdSelect', () => {
trigger.click();
fixture.detectChanges();

const overlayPane = overlayContainerElement.children[0] as HTMLElement;
const overlayPane =
overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
const scrollContainer = overlayPane.querySelector('.md-select-panel');

// Scroll should adjust by the difference between the top space available (85px + 8px
Expand All@@ -711,7 +716,8 @@ describe('MdSelect', () => {
trigger.click();
fixture.detectChanges();

const overlayPane = overlayContainerElement.children[0] as HTMLElement;
const overlayPane =
overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
const scrollContainer = overlayPane.querySelector('.md-select-panel');

// Scroll should adjust by the difference between the bottom space available
Expand All@@ -736,7 +742,8 @@ describe('MdSelect', () => {
trigger.click();
fixture.detectChanges();

const overlayPane = overlayContainerElement.children[0] as HTMLElement;
const overlayPane =
overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;

// We need to set the position to absolute, because the top/left positioning won't work
// since the component CSS isn't included in the tests.
Expand DownExpand Up@@ -768,7 +775,8 @@ describe('MdSelect', () => {
trigger.click();
fixture.detectChanges();

const overlayPane = overlayContainerElement.children[0] as HTMLElement;
const overlayPane =
overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;

// We need to set the position to absolute, because the top/left positioning won't work
// since the component CSS isn't included in the tests.
Expand DownExpand Up@@ -857,7 +865,8 @@ describe('MdSelect', () => {
fixture.detectChanges();

// CSS styles aren't in the tests, so position must be absolute to reflect top/left
const overlayPane = overlayContainerElement.children[0] as HTMLElement;
const overlayPane =
overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
overlayPane.style.position = 'absolute';

const triggerBottom = trigger.getBoundingClientRect().bottom;
Expand All@@ -884,7 +893,8 @@ describe('MdSelect', () => {
fixture.detectChanges();

// CSS styles aren't in the tests, so position must be absolute to reflect top/left
const overlayPane = overlayContainerElement.children[0] as HTMLElement;
const overlayPane =
overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
overlayPane.style.position = 'absolute';

const triggerTop = trigger.getBoundingClientRect().top;
Expand All@@ -906,7 +916,8 @@ describe('MdSelect', () => {
trigger.click();
fixture.detectChanges();

const overlayPane = overlayContainerElement.children[0] as HTMLElement;
const overlayPane =
overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;

// We need to set the position to absolute, because the top/left positioning won't work
// since the component CSS isn't included in the tests.
Expand All@@ -929,7 +940,8 @@ describe('MdSelect', () => {
trigger.click();
fixture.detectChanges();

const overlayPane = overlayContainerElement.children[0] as HTMLElement;
const overlayPane =
overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;

// We need to set the position to absolute, because the top/left positioning won't work
// since the component CSS isn't included in the tests.
Expand DownExpand Up@@ -1170,7 +1182,7 @@ describe('MdSelect', () => {
trigger.click();
fixture.detectChanges();

const pane = overlayContainerElement.children[0] as HTMLElement;
const pane = overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement;
expect(pane.style.minWidth).toEqual('300px');

expect(fixture.componentInstance.select.panelOpen).toBe(true);
Expand Down