Browse Source
Merge pull request #4374 from nextcloud/contactsmenu_popover
Merge pull request #4374 from nextcloud/contactsmenu_popover
add contacts popoverpull/4537/head
committed by
GitHub
17 changed files with 643 additions and 8 deletions
-
6apps/comments/css/comments.css
-
20apps/comments/js/commentstabview.js
-
2apps/comments/tests/js/commentstabviewSpec.js
-
17core/Controller/ContactsMenuController.php
-
19core/css/share.scss
-
1core/js/core.json
-
117core/js/jquery.contactsmenu.js
-
3core/js/merged-template-prepend.json
-
5core/js/sharedialogresharerinfoview.js
-
15core/js/sharedialogshareelistview.js
-
213core/js/tests/specs/jquery.contactsmenuSpec.js
-
1core/routes.php
-
44lib/private/Contacts/ContactsMenu/ContactsStore.php
-
17lib/private/Contacts/ContactsMenu/Manager.php
-
31tests/Core/Controller/ContactsMenuControllerTest.php
-
95tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php
-
45tests/lib/Contacts/ContactsMenu/ManagerTest.php
@ -0,0 +1,117 @@ |
|||
/** |
|||
* Copyright (c) 2017 Georg Ehrke <oc.list@georgehrke.com> |
|||
* This file is licensed under the Affero General Public License version 3 or |
|||
* later. |
|||
* See the COPYING-README file. |
|||
*/ |
|||
|
|||
(function ($) { |
|||
var ENTRY = '' |
|||
+ '<li>' |
|||
+ ' <a href="{{hyperlink}}">' |
|||
+ ' {{#if icon}}<img src="{{icon}}">{{/if}}' |
|||
+ ' <span>{{title}}</span>' |
|||
+ ' </a>' |
|||
+ '</li>'; |
|||
|
|||
var LIST = '' |
|||
+ '<div class="menu popovermenu bubble hidden contactsmenu-popover">' |
|||
+ ' <ul>' |
|||
+ ' <li>' |
|||
+ ' <a>' |
|||
+ ' <span class="icon-loading-small"></span>' |
|||
+ ' </a>' |
|||
+ ' </li>' |
|||
+ ' </ul>' |
|||
+ '</div>'; |
|||
|
|||
$.fn.contactsMenu = function(shareWith, shareType, appendTo) { |
|||
// 0 - user, 4 - email, 6 - remote
|
|||
var allowedTypes = [0, 4, 6]; |
|||
if (allowedTypes.indexOf(shareType) === -1) { |
|||
return; |
|||
} |
|||
|
|||
var $div = this; |
|||
appendTo.append(LIST); |
|||
var $list = appendTo.find('div.contactsmenu-popover'); |
|||
|
|||
$div.click(function() { |
|||
if (!$list.hasClass('hidden')) { |
|||
$list.addClass('hidden'); |
|||
$list.hide(); |
|||
return; |
|||
} |
|||
|
|||
$list.removeClass('hidden'); |
|||
$list.show(); |
|||
|
|||
if ($list.hasClass('loaded')) { |
|||
return; |
|||
} |
|||
|
|||
$list.addClass('loaded'); |
|||
$.ajax(OC.generateUrl('/contactsmenu/findOne'), { |
|||
method: 'POST', |
|||
data: { |
|||
shareType: shareType, |
|||
shareWith: shareWith |
|||
} |
|||
}).then(function(data) { |
|||
$list.find('ul').find('li').addClass('hidden'); |
|||
|
|||
var actions; |
|||
if (!data.topAction) { |
|||
actions = [{ |
|||
hyperlink: '#', |
|||
title: t('core', 'No action available') |
|||
}]; |
|||
} else { |
|||
actions = [data.topAction].concat(data.actions); |
|||
} |
|||
|
|||
actions.forEach(function(action) { |
|||
var template = Handlebars.compile(ENTRY); |
|||
$list.find('ul').append(template(action)); |
|||
}); |
|||
|
|||
if (actions.length === 0) { |
|||
|
|||
} |
|||
}, function(jqXHR) { |
|||
$list.find('ul').find('li').addClass('hidden'); |
|||
|
|||
var title; |
|||
if (jqXHR.status === 404) { |
|||
title = t('core', 'No action available'); |
|||
} else { |
|||
title = t('core', 'Error fetching contact actions'); |
|||
} |
|||
|
|||
var template = Handlebars.compile(ENTRY); |
|||
$list.find('ul').append(template({ |
|||
hyperlink: '#', |
|||
title: title |
|||
})); |
|||
}); |
|||
}); |
|||
|
|||
$(document).click(function(event) { |
|||
var clickedList = ($list.has(event.target).length > 0); |
|||
var clickedTarget = ($div.has(event.target).length > 0); |
|||
|
|||
$div.each(function() { |
|||
if ($(this).is(event.target)) { |
|||
clickedTarget = true; |
|||
} |
|||
}); |
|||
|
|||
if (clickedList || clickedTarget) { |
|||
return; |
|||
} |
|||
|
|||
$list.addClass('hidden'); |
|||
$list.hide(); |
|||
}); |
|||
}; |
|||
}(jQuery)); |
|||
@ -0,0 +1,213 @@ |
|||
/** |
|||
* Copyright (c) 2017 Georg Ehrke <oc.list@georgehrke.com> |
|||
* |
|||
* This file is licensed under the Affero General Public License version 3 |
|||
* or later. |
|||
* |
|||
* See the COPYING-README file. |
|||
* |
|||
*/ |
|||
|
|||
describe('jquery.contactsMenu tests', function() { |
|||
|
|||
var $selector1, $selector2, $appendTo; |
|||
|
|||
beforeEach(function() { |
|||
$('#testArea').append($('<div id="selector1">')); |
|||
$('#testArea').append($('<div id="selector2">')); |
|||
$('#testArea').append($('<div id="appendTo">')); |
|||
$selector1 = $('#selector1'); |
|||
$selector2 = $('#selector2'); |
|||
$appendTo = $('#appendTo'); |
|||
}); |
|||
|
|||
afterEach(function() { |
|||
$selector1.remove(); |
|||
$selector2.remove(); |
|||
$appendTo.remove(); |
|||
}); |
|||
|
|||
describe('shareType', function() { |
|||
it('stops if type not supported', function() { |
|||
$selector1.contactsMenu('user', 1, $appendTo); |
|||
expect($appendTo.children().length).toEqual(0); |
|||
|
|||
$selector1.contactsMenu('user', 2, $appendTo); |
|||
expect($appendTo.children().length).toEqual(0); |
|||
|
|||
$selector1.contactsMenu('user', 3, $appendTo); |
|||
expect($appendTo.children().length).toEqual(0); |
|||
|
|||
$selector1.contactsMenu('user', 5, $appendTo); |
|||
expect($appendTo.children().length).toEqual(0); |
|||
}); |
|||
|
|||
it('append list if shareType supported', function() { |
|||
$selector1.contactsMenu('user', 0, $appendTo); |
|||
expect($appendTo.children().length).toEqual(1); |
|||
expect($appendTo.html()).toEqual('<div class="menu popovermenu bubble hidden contactsmenu-popover"> <ul> <li> <a> <span class="icon-loading-small"></span> </a> </li> </ul></div>'); |
|||
}); |
|||
}); |
|||
|
|||
describe('open on click', function() { |
|||
it('with one selector', function() { |
|||
$selector1.contactsMenu('user', 0, $appendTo); |
|||
expect($appendTo.children().length).toEqual(1); |
|||
expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(true); |
|||
$selector1.click(); |
|||
expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(false); |
|||
}); |
|||
|
|||
it('with multiple selectors - 1', function() { |
|||
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo); |
|||
|
|||
expect($appendTo.children().length).toEqual(1); |
|||
expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(true); |
|||
$selector1.click(); |
|||
expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(false); |
|||
}); |
|||
|
|||
it('with multiple selectors - 2', function() { |
|||
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo); |
|||
|
|||
expect($appendTo.children().length).toEqual(1); |
|||
expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(true); |
|||
$selector2.click(); |
|||
expect($appendTo.find('div.contactsmenu-popover').hasClass('hidden')).toEqual(false); |
|||
}); |
|||
|
|||
it ('should close when clicking the selector again - 1', function() { |
|||
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo); |
|||
|
|||
expect($appendTo.children().length).toEqual(1); |
|||
expect($appendTo.find('div').hasClass('hidden')).toEqual(true); |
|||
$selector1.click(); |
|||
expect($appendTo.find('div').hasClass('hidden')).toEqual(false); |
|||
$selector1.click(); |
|||
expect($appendTo.find('div').hasClass('hidden')).toEqual(true); |
|||
}); |
|||
|
|||
it ('should close when clicking the selector again - 1', function() { |
|||
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo); |
|||
|
|||
expect($appendTo.children().length).toEqual(1); |
|||
expect($appendTo.find('div').hasClass('hidden')).toEqual(true); |
|||
$selector1.click(); |
|||
expect($appendTo.find('div').hasClass('hidden')).toEqual(false); |
|||
$selector2.click(); |
|||
expect($appendTo.find('div').hasClass('hidden')).toEqual(true); |
|||
}); |
|||
}); |
|||
|
|||
describe('send requests to the server and render', function() { |
|||
it('load a topaction only', function() { |
|||
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo); |
|||
$selector1.click(); |
|||
|
|||
fakeServer.requests[0].respond( |
|||
200, |
|||
{ 'Content-Type': 'application/json; charset=utf-8' }, |
|||
JSON.stringify({ |
|||
"id": null, |
|||
"fullName": "Name 123", |
|||
"topAction": { |
|||
"title": "bar@baz.wtf", |
|||
"icon": "foo.svg", |
|||
"hyperlink": "mailto:bar%40baz.wtf"}, |
|||
"actions": [] |
|||
}) |
|||
); |
|||
expect(fakeServer.requests[0].method).toEqual('POST'); |
|||
expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne'); |
|||
|
|||
expect($appendTo.html()).toEqual('<div class="menu popovermenu bubble contactsmenu-popover loaded" style="display: block;"> <ul> <li class="hidden"> <a> <span class="icon-loading-small"></span> </a> </li> <li> <a href="mailto:bar%40baz.wtf"> <img src="foo.svg"> <span>bar@baz.wtf</span> </a></li></ul></div>'); |
|||
}); |
|||
|
|||
it('load topaction and more actions', function() { |
|||
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo); |
|||
$selector1.click(); |
|||
|
|||
fakeServer.requests[0].respond( |
|||
200, |
|||
{ 'Content-Type': 'application/json; charset=utf-8' }, |
|||
JSON.stringify({ |
|||
"id": null, |
|||
"fullName": "Name 123", |
|||
"topAction": { |
|||
"title": "bar@baz.wtf", |
|||
"icon": "foo.svg", |
|||
"hyperlink": "mailto:bar%40baz.wtf"}, |
|||
"actions": [{ |
|||
"title": "Details", |
|||
"icon": "details.svg", |
|||
"hyperlink": "http:\/\/localhost\/index.php\/apps\/contacts" |
|||
}] |
|||
}) |
|||
); |
|||
expect(fakeServer.requests[0].method).toEqual('POST'); |
|||
expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne'); |
|||
|
|||
expect($appendTo.html()).toEqual('<div class="menu popovermenu bubble contactsmenu-popover loaded" style="display: block;"> <ul> <li class="hidden"> <a> <span class="icon-loading-small"></span> </a> </li> <li> <a href="mailto:bar%40baz.wtf"> <img src="foo.svg"> <span>bar@baz.wtf</span> </a></li><li> <a href="http://localhost/index.php/apps/contacts"> <img src="details.svg"> <span>Details</span> </a></li></ul></div>'); |
|||
}); |
|||
|
|||
it('load no actions', function() { |
|||
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo); |
|||
$selector1.click(); |
|||
|
|||
fakeServer.requests[0].respond( |
|||
200, |
|||
{ 'Content-Type': 'application/json; charset=utf-8' }, |
|||
JSON.stringify({ |
|||
"id": null, |
|||
"fullName": "Name 123", |
|||
"topAction": null, |
|||
"actions": [] |
|||
}) |
|||
); |
|||
expect(fakeServer.requests[0].method).toEqual('POST'); |
|||
expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne'); |
|||
|
|||
expect($appendTo.html()).toEqual('<div class="menu popovermenu bubble contactsmenu-popover loaded" style="display: block;"> <ul> <li class="hidden"> <a> <span class="icon-loading-small"></span> </a> </li> <li> <a href="#"> <span>No action available</span> </a></li></ul></div>'); |
|||
}); |
|||
|
|||
it('should throw an error', function() { |
|||
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo); |
|||
$selector1.click(); |
|||
|
|||
fakeServer.requests[0].respond( |
|||
400, |
|||
{ 'Content-Type': 'application/json; charset=utf-8' }, |
|||
JSON.stringify([]) |
|||
); |
|||
expect(fakeServer.requests[0].method).toEqual('POST'); |
|||
expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne'); |
|||
|
|||
expect($appendTo.html()).toEqual('<div class="menu popovermenu bubble contactsmenu-popover loaded" style="display: block;"> <ul> <li class="hidden"> <a> <span class="icon-loading-small"></span> </a> </li> <li> <a href="#"> <span>Error fetching contact actions</span> </a></li></ul></div>'); |
|||
}); |
|||
|
|||
it('should handle 404', function() { |
|||
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo); |
|||
$selector1.click(); |
|||
|
|||
fakeServer.requests[0].respond( |
|||
404, |
|||
{ 'Content-Type': 'application/json; charset=utf-8' }, |
|||
JSON.stringify([]) |
|||
); |
|||
expect(fakeServer.requests[0].method).toEqual('POST'); |
|||
expect(fakeServer.requests[0].url).toEqual('http://localhost/index.php/contactsmenu/findOne'); |
|||
|
|||
expect($appendTo.html()).toEqual('<div class="menu popovermenu bubble contactsmenu-popover loaded" style="display: block;"> <ul> <li class="hidden"> <a> <span class="icon-loading-small"></span> </a> </li> <li> <a href="#"> <span>No action available</span> </a></li></ul></div>'); |
|||
}); |
|||
}); |
|||
|
|||
it('click anywhere else to close the menu', function() { |
|||
$('#selector1, #selector2').contactsMenu('user', 0, $appendTo); |
|||
|
|||
expect($appendTo.find('div').hasClass('hidden')).toEqual(true); |
|||
$selector1.click(); |
|||
expect($appendTo.find('div').hasClass('hidden')).toEqual(false); |
|||
$(document).click(); |
|||
expect($appendTo.find('div').hasClass('hidden')).toEqual(true); |
|||
}); |
|||
}); |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue