-
Notifications
You must be signed in to change notification settings - Fork 6.7k
feat(modal): add container option #4599
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -276,16 +276,16 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) | |
}); | ||
|
||
function removeModalWindow(modalInstance, elementToReceiveFocus) { | ||
var body = $document.find('body').eq(0); | ||
var modalWindow = openedWindows.get(modalInstance).value; | ||
var container = $document.find(modalWindow.container).eq(0); | ||
|
||
//clean up the stack | ||
openedWindows.remove(modalInstance); | ||
|
||
removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() { | ||
var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS; | ||
openedClasses.remove(modalBodyClass, modalInstance); | ||
body.toggleClass(modalBodyClass, openedClasses.hasKey(modalBodyClass)); | ||
container.toggleClass(modalBodyClass, openedClasses.hasKey(modalBodyClass)); | ||
toggleTopWindowClass(true); | ||
}); | ||
checkRemoveBackdrop(); | ||
|
@@ -294,7 +294,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) | |
if (elementToReceiveFocus && elementToReceiveFocus.focus) { | ||
elementToReceiveFocus.focus(); | ||
} else { | ||
body.focus(); | ||
container.focus(); | ||
} | ||
} | ||
|
||
|
@@ -413,14 +413,19 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) | |
backdrop: modal.backdrop, | ||
keyboard: modal.keyboard, | ||
openedClass: modal.openedClass, | ||
windowTopClass: modal.windowTopClass | ||
windowTopClass: modal.windowTopClass, | ||
container: modal.container | ||
}); | ||
|
||
openedClasses.put(modalBodyClass, modalInstance); | ||
|
||
var body = $document.find('body').eq(0), | ||
var container = $document.find(modal.container).eq(0), | ||
currBackdropIndex = backdropIndex(); | ||
|
||
if (!container.length) { | ||
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. We shouldn't mention jqLite because if the user includes the full jQuery library before Angular, Angular will use the full jQuery library. This goes back to how we want to do error handling as mentioned above. 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. Updated error message to |
||
throw new Error('Container not found. Make sure that the container passed is in DOM.'); | ||
} | ||
|
||
if (currBackdropIndex >= 0 && !backdropDomEl) { | ||
backdropScope = $rootScope.$new(true); | ||
backdropScope.index = currBackdropIndex; | ||
|
@@ -430,7 +435,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) | |
angularBackgroundDomEl.attr('modal-animation', 'true'); | ||
} | ||
backdropDomEl = $compile(angularBackgroundDomEl)(backdropScope); | ||
body.append(backdropDomEl); | ||
container.append(backdropDomEl); | ||
} | ||
|
||
var angularDomEl = angular.element('<div uib-modal-window="modal-window"></div>'); | ||
|
@@ -449,8 +454,8 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) | |
var modalDomEl = $compile(angularDomEl)(modal.scope); | ||
openedWindows.top().value.modalDomEl = modalDomEl; | ||
openedWindows.top().value.modalOpener = modalOpener; | ||
body.append(modalDomEl); | ||
body.addClass(modalBodyClass); | ||
container.append(modalDomEl); | ||
container.addClass(modalBodyClass); | ||
|
||
$modalStack.clearFocusListCache(); | ||
}; | ||
|
@@ -603,6 +608,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) | |
//merge and clean up options | ||
modalOptions = angular.extend({}, $modalProvider.options, modalOptions); | ||
modalOptions.resolve = modalOptions.resolve || {}; | ||
modalOptions.container = modalOptions.container || 'body'; | ||
|
||
//verify options | ||
if (!modalOptions.template && !modalOptions.templateUrl) { | ||
|
@@ -669,7 +675,8 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) | |
windowClass: modalOptions.windowClass, | ||
windowTemplateUrl: modalOptions.windowTemplateUrl, | ||
size: modalOptions.size, | ||
openedClass: modalOptions.openedClass | ||
openedClass: modalOptions.openedClass, | ||
container: modalOptions.container | ||
}); | ||
modalOpenedDeferred.resolve(true); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -833,6 +833,48 @@ describe('$uibModal', function () { | |
expect($document.find('.modal-backdrop')).not.toHaveClass('fade'); | ||
}); | ||
}); | ||
|
||
describe('container', function() { | ||
it('should be added to body by default', function() { | ||
var modal = open({template: '<div>Content</div>'}); | ||
|
||
expect($document).toHaveModalsOpen(1); | ||
expect($document).toHaveModalOpenWithContent('Content', 'div'); | ||
}); | ||
|
||
it('should not be added to body if container is passed', function() { | ||
var element = angular.element('<section>Some content</section>'); | ||
angular.element(document.body).append(element); | ||
|
||
var modal = open({template: '<div>Content</div>', container: element}); | ||
|
||
expect($document).not.toHaveModalOpenWithContent('Content', 'div'); | ||
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. it is not sufficient to test that the body doesn't have a modal. you need to check that the modal is on the component you've specified and only on that component. 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. Thanks. Added couple of more test cases |
||
}); | ||
|
||
it('should be added to container if container is passed', function() { | ||
var element = angular.element('<section>Some content</section>'); | ||
angular.element(document.body).append(element); | ||
|
||
expect($document.find('section').children('div.modal').length).toBe(0); | ||
open({template: '<div>Content</div>', container: element}); | ||
expect($document.find('section').children('div.modal').length).toBe(1); | ||
}); | ||
|
||
it('should throw error if container is not found', function() { | ||
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. Still not sure how we want to handle the error. If we throw something, should we display a link to an error page like core Angular does (and other Angular libraries)? 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. I don't think we should handle this error as there is a need for the user to fix the expectation. Let me know if you want me to point the error to docs page. |
||
expect(function(){ | ||
open({template: '<div>Content</div>', container: $document.find('aside')}); | ||
}).toThrow(new Error('Container not found. Make sure that the container passed is in DOM.')); | ||
}); | ||
|
||
it('should be removed from container when dismissed', function() { | ||
var modal = open({template: '<div>Content</div>'}); | ||
|
||
expect($document).toHaveModalsOpen(1); | ||
|
||
dismiss(modal); | ||
expect($document).toHaveModalsOpen(0); | ||
}); | ||
}); | ||
|
||
describe('openedClass', function() { | ||
var body; | ||
|
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.
what happens if
modal.container
refers to a container that doesn't exist?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.
This is not going to work. jqLite is strictly limited to finding by tag name so what's the use of being able to specify something other than
<body>
here?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.
modal.container
should probably default to$document.find('body')
, but allow the user to link to a jqLite wrapped DOM node for the api.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.
@icfantv
Now I am throwing the following error is container is not found
@wesleycho
Good idea. I can change to that approach if we all agree
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.
I'm ok with this. The documentation should state that the value of of the container attribute is an
angular.element
.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.
I still think we need to only grab the first one. If I'm reading this right, if someone passed in multiple objects, the modal would get appended to each of them.
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.
Does jqLite support it? That is my main concern
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.
Sorry. Support what? jqLite supports both
.eq()
and.append()
with no strings attached (according to this).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.
Ah, thought for some reason Angular didn't support
.eq
in jqLite for a while - looks like I was wrong.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.
I guess logging error makes more sense here as user has explicitly passed the invalid container element. Let me know if you want me to default it to
body
.