This repository was archived by the owner on Sep 5, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
fix(mdPanel): Propagation, CSS targeting, and dynamic position updating #8983
Closed
bradrich
wants to merge
1
commit into
angular:master
from
bradrich:fix/panel-propagation-css-position
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -108,6 +108,9 @@ angular | |
| * - `attachTo` - `{(string|!angular.JQLite|!Element)=}`: The element to | ||
| * attach the panel to. Defaults to appending to the root element of the | ||
| * application. | ||
| * - `propagateContainerEvents` - `{boolean=}`: Whether pointer or touch | ||
| * events should be allowed to propagate 'go through' the container, aka the | ||
| * wrapper, of the panel. Defaults to false. | ||
| * - `panelClass` - `{string=}`: A css class to apply to the panel element. | ||
| * This class should define any borders, box-shadow, etc. for the panel. | ||
| * - `zIndex` - `{number=}`: The z-index to place the panel at. | ||
|
|
@@ -308,6 +311,8 @@ angular | |
| * Adds a class to the panel. DO NOT use this to hide/show the panel. | ||
| * | ||
| * @param {string} newClass Class to be added. | ||
| * @param {boolean} toElement Whether or not to add the class to the panel | ||
| * element instead of the container. | ||
| */ | ||
|
|
||
| /** | ||
|
|
@@ -317,6 +322,8 @@ angular | |
| * Removes a class from the panel. DO NOT use this to hide/show the panel. | ||
| * | ||
| * @param {string} oldClass Class to be removed. | ||
| * @param {boolean} fromElement Whether or not to remove the class from the | ||
| * panel element instead of the container. | ||
| */ | ||
|
|
||
| /** | ||
|
|
@@ -326,6 +333,17 @@ angular | |
| * Toggles a class on the panel. DO NOT use this to hide/show the panel. | ||
| * | ||
| * @param {string} toggleClass Class to be toggled. | ||
| * @param {boolean} onElement Whether or not to remove the class from the panel | ||
| * element instead of the container. | ||
| */ | ||
|
|
||
| /** | ||
| * @ngdoc method | ||
| * @name MdPanelRef#updatePosition | ||
| * @description | ||
| * Updates the position configuration of a panel. Use this to update the | ||
| * position of a panel that is open, without having to close and re-open the | ||
| * panel. | ||
| */ | ||
|
|
||
| /** | ||
|
|
@@ -628,6 +646,7 @@ function MdPanelService($rootElement, $rootScope, $injector, $window) { | |
| focusOnOpen: true, | ||
| fullscreen: false, | ||
| hasBackdrop: false, | ||
| propagateContainerEvents: false, | ||
| transformTemplate: angular.bind(this, this._wrapTemplate), | ||
| trapFocus: false, | ||
| zIndex: defaultZIndex | ||
|
|
@@ -1057,14 +1076,18 @@ MdPanelRef.prototype.hide = function() { | |
| * Add a class to the panel. DO NOT use this to hide/show the panel. | ||
| * | ||
| * @param {string} newClass Class to be added. | ||
| * @param {boolean} toElement Whether or not to add the class to the panel | ||
| * element instead of the container. | ||
| */ | ||
| MdPanelRef.prototype.addClass = function(newClass) { | ||
| MdPanelRef.prototype.addClass = function(newClass, toElement) { | ||
| if (!this._panelContainer) { | ||
| throw new Error('Panel does not exist yet. Call open() or attach().'); | ||
| } | ||
|
|
||
| if (!this._panelContainer.hasClass(newClass)) { | ||
| if (!toElement && !this._panelContainer.hasClass(newClass)) { | ||
| this._panelContainer.addClass(newClass); | ||
| } else if (toElement && !this._panelEl.hasClass(newClass)) { | ||
| this._panelEl.addClass(newClass); | ||
| } | ||
| }; | ||
|
|
||
|
|
@@ -1073,14 +1096,18 @@ MdPanelRef.prototype.addClass = function(newClass) { | |
| * Remove a class from the panel. DO NOT use this to hide/show the panel. | ||
| * | ||
| * @param {string} oldClass Class to be removed. | ||
| * @param {boolean} fromElement Whether or not to remove the class from the | ||
| * panel element instead of the container. | ||
| */ | ||
| MdPanelRef.prototype.removeClass = function(oldClass) { | ||
| MdPanelRef.prototype.removeClass = function(oldClass, fromElement) { | ||
| if (!this._panelContainer) { | ||
| throw new Error('Panel does not exist yet. Call open() or attach().'); | ||
| } | ||
|
|
||
| if (this._panelContainer.hasClass(oldClass)) { | ||
| if (!fromElement && this._panelContainer.hasClass(oldClass)) { | ||
| this._panelContainer.removeClass(oldClass); | ||
| } else if (fromElement && this._panelEl.hasClass(oldClass)) { | ||
| this._panelEl.removeClass(oldClass); | ||
| } | ||
| }; | ||
|
|
||
|
|
@@ -1089,13 +1116,19 @@ MdPanelRef.prototype.removeClass = function(oldClass) { | |
| * Toggle a class on the panel. DO NOT use this to hide/show the panel. | ||
| * | ||
| * @param {string} toggleClass The class to toggle. | ||
| * @param {boolean} onElement Whether or not to toggle the class on the panel | ||
| * element instead of the container. | ||
| */ | ||
| MdPanelRef.prototype.toggleClass = function(toggleClass) { | ||
| MdPanelRef.prototype.toggleClass = function(toggleClass, onElement) { | ||
| if (!this._panelContainer) { | ||
| throw new Error('Panel does not exist yet. Call open() or attach().'); | ||
| } | ||
|
|
||
| this._panelContainer.toggleClass(toggleClass); | ||
| if (!onElement) { | ||
| this._panelContainer.toggleClass(toggleClass); | ||
| } else { | ||
| this._panelEl.toggleClass(toggleClass); | ||
| } | ||
| }; | ||
|
|
||
|
|
||
|
|
@@ -1131,11 +1164,16 @@ MdPanelRef.prototype._createPanel = function() { | |
| self._panelEl = angular.element( | ||
| self._panelContainer[0].querySelector('.md-panel')); | ||
|
|
||
| // Add a custom CSS class. | ||
| // Add a custom CSS class to the panel element. | ||
| if (self._config['panelClass']) { | ||
| self._panelEl.addClass(self._config['panelClass']); | ||
| } | ||
|
|
||
| // Handle click and touch events for the panel container. | ||
| if (self._config['propagateContainerEvents']) { | ||
| self._panelContainer.css('pointer-events', 'none'); | ||
| } | ||
|
|
||
| // Panel may be outside the $rootElement, tell ngAnimate to animate | ||
| // regardless. | ||
| if (self._$animate.pin) { | ||
|
|
@@ -1193,6 +1231,20 @@ MdPanelRef.prototype._addStyles = function() { | |
| }; | ||
|
|
||
|
|
||
| /** | ||
| * Updates the position configuration of a panel | ||
| * @param {MdPanelPosition} position | ||
| */ | ||
| MdPanelRef.prototype.updatePosition = function(position) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You need to add this documentation also to the public documentation above, probably with a description of why you someone would use this. (~line 330) |
||
| if (!this._panelContainer) { | ||
| throw new Error('Panel does not exist yet. Call open() or attach().'); | ||
| } | ||
|
|
||
| this._config['position'] = position; | ||
| this._updatePosition(); | ||
| }; | ||
|
|
||
|
|
||
| /** | ||
| * Calculates and updates the position of the panel. | ||
| * @param {boolean=} opt_init | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -409,6 +409,34 @@ describe('$mdPanel', function() { | |
| }); | ||
| }); | ||
|
|
||
| describe('should cause the propagation of events', function() { | ||
| var config, wrapper; | ||
|
|
||
| it('to be stopped when propagateContainerEvents=false', function() { | ||
| config = { | ||
| propagateContainerEvents: false, | ||
| template: DEFAULT_TEMPLATE | ||
| }; | ||
|
|
||
| openPanel(config); | ||
|
|
||
| wrapper = angular.element(document.querySelector(PANEL_WRAPPER_CLASS)); | ||
| expect(wrapper.css('pointer-events')).not.toEqual('none'); | ||
| }); | ||
|
|
||
| it('to NOT be stopped when propagateContainerEvents=true', function() { | ||
| config = { | ||
| propagateContainerEvents: true, | ||
| template: DEFAULT_TEMPLATE | ||
| }; | ||
|
|
||
| openPanel(config); | ||
|
|
||
| wrapper = angular.element(document.querySelector(PANEL_WRAPPER_CLASS)); | ||
| expect(wrapper.css('pointer-events')).toEqual('none'); | ||
| }); | ||
| }); | ||
|
|
||
| it('should apply a custom css class to the panel', function() { | ||
| var customClass = 'custom-class'; | ||
|
|
||
|
|
@@ -815,7 +843,6 @@ describe('$mdPanel', function() { | |
| expect(onRemovingCalled).toBe(true); | ||
| }); | ||
|
|
||
|
|
||
| it('should call onDomRemoved if provided when removing the panel from ' + | ||
| 'the DOM', function() { | ||
| var onDomRemovedCalled = false; | ||
|
|
@@ -868,6 +895,87 @@ describe('$mdPanel', function() { | |
| }); | ||
| }); | ||
|
|
||
| describe('CSS class logic:', function() { | ||
| it('should add a class to the container/wrapper when toElement=false', | ||
| function() { | ||
| openPanel(DEFAULT_CONFIG); | ||
|
|
||
| panelRef.addClass('my-class'); | ||
|
|
||
| expect(PANEL_WRAPPER_CLASS).toHaveClass('my-class'); | ||
| expect(PANEL_EL).not.toHaveClass('my-class'); | ||
| }); | ||
|
|
||
| it('should add a class to the element when toElement=true', function() { | ||
| openPanel(DEFAULT_CONFIG); | ||
|
|
||
| panelRef.addClass('my-class', true); | ||
|
|
||
| expect(PANEL_WRAPPER_CLASS).not.toHaveClass('my-class'); | ||
| expect(PANEL_EL).toHaveClass('my-class'); | ||
| }); | ||
|
|
||
| it('should remove a class from the container/wrapper when fromElement=false', | ||
| function() { | ||
| openPanel(DEFAULT_CONFIG); | ||
|
|
||
| panelRef.addClass('my-class'); | ||
|
|
||
| expect(PANEL_WRAPPER_CLASS).toHaveClass('my-class'); | ||
| expect(PANEL_EL).not.toHaveClass('my-class'); | ||
|
|
||
| panelRef.removeClass('my-class'); | ||
|
|
||
| expect(PANEL_WRAPPER_CLASS).not.toHaveClass('my-class'); | ||
| expect(PANEL_EL).not.toHaveClass('my-class'); | ||
| }); | ||
|
|
||
| it('should remove a class from the element when fromElement=true', | ||
| function() { | ||
| openPanel(DEFAULT_CONFIG); | ||
|
|
||
| panelRef.addClass('my-class', true); | ||
|
|
||
| expect(PANEL_WRAPPER_CLASS).not.toHaveClass('my-class'); | ||
| expect(PANEL_EL).toHaveClass('my-class'); | ||
|
|
||
| panelRef.removeClass('my-class', true); | ||
|
|
||
| expect(PANEL_WRAPPER_CLASS).not.toHaveClass('my-class'); | ||
| expect(PANEL_EL).not.toHaveClass('my-class'); | ||
| }); | ||
|
|
||
| it('should toggle a class on the container/wrapper when onElement=false', | ||
| function() { | ||
| openPanel(DEFAULT_CONFIG); | ||
|
|
||
| panelRef.toggleClass('my-class'); | ||
|
|
||
| expect(PANEL_WRAPPER_CLASS).toHaveClass('my-class'); | ||
| expect(PANEL_EL).not.toHaveClass('my-class'); | ||
|
|
||
| panelRef.toggleClass('my-class'); | ||
|
|
||
| expect(PANEL_WRAPPER_CLASS).not.toHaveClass('my-class'); | ||
| expect(PANEL_EL).not.toHaveClass('my-class'); | ||
| }); | ||
|
|
||
| it('should toggle a class on the element when onElement=true', | ||
| function() { | ||
| openPanel(DEFAULT_CONFIG); | ||
|
|
||
| panelRef.toggleClass('my-class', true); | ||
|
|
||
| expect(PANEL_WRAPPER_CLASS).not.toHaveClass('my-class'); | ||
| expect(PANEL_EL).toHaveClass('my-class'); | ||
|
|
||
| panelRef.toggleClass('my-class', true); | ||
|
|
||
| expect(PANEL_WRAPPER_CLASS).not.toHaveClass('my-class'); | ||
| expect(PANEL_EL).not.toHaveClass('n-class'); | ||
| }); | ||
| }); | ||
|
|
||
| describe('should focus on the origin element on', function() { | ||
| var myButton; | ||
| var detachFocusConfig; | ||
|
|
@@ -1115,6 +1223,106 @@ describe('$mdPanel', function() { | |
| mdPanelPosition = $mdPanel.newPanelPosition(); | ||
| }); | ||
|
|
||
| describe('should update the position of an open panel', function() { | ||
| var xPosition, yPosition, myButton, myButtonRect; | ||
|
|
||
| beforeEach(function() { | ||
| xPosition = $mdPanel.xPosition; | ||
| yPosition = $mdPanel.yPosition; | ||
|
|
||
| myButton = '<button>myButton</button>'; | ||
| attachToBody(myButton); | ||
| myButton = angular.element(document.querySelector('button')); | ||
| myButtonRect = myButton[0].getBoundingClientRect(); | ||
| }); | ||
|
|
||
| it('between two absolute positions', function() { | ||
| var top = '50px'; | ||
| var left = '30px'; | ||
| var position = $mdPanel.newPanelPosition() | ||
| .absolute() | ||
| .top(top) | ||
| .left(left); | ||
|
|
||
| config['position'] = position; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: new line before this one |
||
|
|
||
| openPanel(config); | ||
|
|
||
| var panelRect = document.querySelector(PANEL_EL) | ||
| .getBoundingClientRect(); | ||
| expect(panelRect.top).toBeApproximately(parseInt(top)); | ||
| expect(panelRect.left).toBeApproximately(parseInt(left)); | ||
|
|
||
| var newTop = '500px'; | ||
| var newLeft = '300px'; | ||
| var newPosition = $mdPanel.newPanelPosition() | ||
| .absolute() | ||
| .top(newTop) | ||
| .left(newLeft); | ||
|
|
||
| panelRef.updatePosition(newPosition); | ||
|
|
||
| var newPanelRect = document.querySelector(PANEL_EL) | ||
| .getBoundingClientRect(); | ||
| expect(newPanelRect.top).toBeApproximately(parseInt(newTop)); | ||
| expect(newPanelRect.left).toBeApproximately(parseInt(newLeft)); | ||
| }); | ||
|
|
||
| it('between two relative positions', function() { | ||
| var position = $mdPanel.newPanelPosition() | ||
| .relativeTo(myButton[0]) | ||
| .addPanelPosition(xPosition.ALIGN_START, yPosition.ALIGN_TOPS); | ||
|
|
||
| config['position'] = position; | ||
|
|
||
| openPanel(config); | ||
|
|
||
| var panelRect = document.querySelector(PANEL_EL) | ||
| .getBoundingClientRect(); | ||
| expect(panelRect.top).toBeApproximately(myButtonRect.top); | ||
| expect(panelRect.left).toBeApproximately(myButtonRect.left); | ||
|
|
||
| var newPosition = $mdPanel.newPanelPosition() | ||
| .relativeTo(myButton) | ||
| .addPanelPosition(null, yPosition.ABOVE); | ||
|
|
||
| panelRef.updatePosition(newPosition); | ||
|
|
||
| var newPanelRect = document.querySelector(PANEL_EL) | ||
| .getBoundingClientRect(); | ||
| expect(newPanelRect.bottom).toBeApproximately(myButtonRect.top); | ||
| }); | ||
|
|
||
| it('from an absolute to a relative position', function() { | ||
| var top = '250px'; | ||
| var left = '400px'; | ||
| var position = $mdPanel.newPanelPosition() | ||
| .absolute() | ||
| .top(top) | ||
| .left(left); | ||
|
|
||
| config['position'] = position; | ||
|
|
||
| openPanel(config); | ||
|
|
||
| var panelRect = document.querySelector(PANEL_EL) | ||
| .getBoundingClientRect(); | ||
| expect(panelRect.top).toBeApproximately(parseInt(top)); | ||
| expect(panelRect.left).toBeApproximately(parseInt(left)); | ||
|
|
||
| var newPosition = $mdPanel.newPanelPosition() | ||
| .relativeTo(myButton[0]) | ||
| .addPanelPosition(xPosition.ALIGN_START, yPosition.ALIGN_TOPS); | ||
|
|
||
| panelRef.updatePosition(newPosition); | ||
|
|
||
| var newPanelRect = document.querySelector(PANEL_EL) | ||
| .getBoundingClientRect(); | ||
| expect(newPanelRect.top).toBeApproximately(myButtonRect.top); | ||
| expect(newPanelRect.left).toBeApproximately(myButtonRect.left); | ||
| }); | ||
| }); | ||
|
|
||
| describe('should offset the panel', function() { | ||
| it('horizontally', function() { | ||
| var left = '50px'; | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New params need to be added to the public documentation at the top of this file. (For add, remove, and toggle)