Commit b382243d authored by Nick Stenning's avatar Nick Stenning

Merge pull request #2514 from robertknight/t87-group_scope_dropdown_ui

T87 - Improve group scope dropdown ui
parents 89bfa6b3 8699869d
......@@ -134,7 +134,7 @@ module.exports = angular.module('h', [
.directive('deepCount', require('./directive/deep-count'))
.directive('formInput', require('./directive/form-input'))
.directive('formValidate', require('./directive/form-validate'))
.directive('groupList', require('./directive/group-list'))
.directive('groupList', require('./directive/group-list').directive)
.directive('markdown', require('./directive/markdown'))
.directive('privacy', require('./directive/privacy').directive)
.directive('simpleSearch', require('./directive/simple-search'))
......
'use strict';
// @ngInject
function GroupListController($scope) {
$scope.expandedGroupId = undefined;
// show the share link for the specified group or clear it if
// null
$scope.toggleShareLink = function (groupId) {
if (!groupId || $scope.expandedGroupId === groupId) {
$scope.expandedGroupId = undefined;
} else {
$scope.expandedGroupId = groupId;
}
};
$scope.shouldShowShareLink = function (groupId) {
return $scope.expandedGroupId === groupId;
}
$scope.sortedGroups = function () {
return $scope.groups.all().concat().sort(function (a, b) {
if (a.public !== b.public) {
return a.public ? -1 : 1;
}
return a.name.localeCompare(b.name);
});
}
}
/**
* @ngdoc directive
* @name groupList
* @restrict AE
* @description Displays a list of groups of which the user is a member.
*/
// @ngInject
module.exports = function (groups) {
function groupList(groups, $window) {
return {
link: function (scope, elem, attrs) {
scope.groups = groups;
controller: GroupListController,
link: function ($scope, elem, attrs) {
$scope.groups = groups;
$scope.createNewGroup = function() {
$window.open('/groups/new', '_blank');
}
$scope.$watch('expandedGroupId', function (activeGroupId) {
if (activeGroupId) {
// wait for the share link field to be revealed and then select
// the link's text
setTimeout(function() {
var activeShareLinkField = elem[0].querySelector('.share-link-field[data-group-id=' + activeGroupId + ']');
activeShareLinkField.focus();
activeShareLinkField.select();
}, 0);
}
});
},
restrict: 'AE',
scope: {},
templateUrl: 'group_list.html'
};
};
module.exports = {
directive: groupList,
Controller: GroupListController
};
......@@ -125,6 +125,7 @@ var directive = function () {
};
};
exports.PrivacyController = PrivacyController;
exports.directive = directive;
module.exports = {
directive: directive,
Controller: PrivacyController
};
'use strict';
var groupList = require('../group-list');
describe('GroupListController', function () {
var controller;
var $scope;
beforeEach(function () {
$scope = {};
controller = new groupList.Controller($scope);
});
it('toggles share links', function () {
$scope.toggleShareLink('group-a');
assert.equal($scope.expandedGroupId, 'group-a');
$scope.toggleShareLink('group-a');
assert.equal($scope.expandedGroupId, undefined);
$scope.toggleShareLink('group-b');
assert.equal($scope.expandedGroupId, 'group-b');
$scope.toggleShareLink('group-c');
assert.equal($scope.expandedGroupId, 'group-c');
});
it('shows share link for selected group', function () {
assert.equal($scope.shouldShowShareLink('group-a'), false);
$scope.toggleShareLink('group-a');
assert.equal($scope.shouldShowShareLink('group-a'), true);
$scope.toggleShareLink('group-b');
assert.equal($scope.shouldShowShareLink('group-a'), false);
assert.equal($scope.shouldShowShareLink('group-b'), true);
});
it('sorts groups', function () {
$scope.groups = {
all: function () {
return [{
id: 'c',
name: 'Zebrafish Study Group'
},{
id: 'a',
name: 'Antimatter Research'
},{
public: true
}];
}
};
var sorted = $scope.sortedGroups();
assert.ok(sorted[0].public);
assert.equal(sorted[1].name, 'Antimatter Research');
assert.equal(sorted[2].name, 'Zebrafish Study Group');
});
});
// returns true if a jQuery-like element has
// been hidden directly via an ng-show directive.
//
// This does not check whether the element is a descendant
// of a hidden element
function isElementHidden(element) {
return element.hasClass('ng-hide');
}
describe('groupList', function () {
var $compile;
var $scope;
var GROUP_LINK = 'https://hypothes.is/groups/hdevs';
var groups = [{
id: 'public',
public: true
},{
id: 'h-devs',
name: 'Hypothesis Developers',
url: GROUP_LINK
}];
before(function() {
angular.module('app', [])
.directive('groupList', groupList.directive)
.factory('groups', function () {
return {
all: function () {
return groups;
}
};
});
});
beforeEach(function () {
angular.mock.module('app');
angular.mock.module('h.templates');
});
beforeEach(angular.mock.inject(function (_$compile_, _$rootScope_) {
$compile = _$compile_;
$scope = _$rootScope_.$new();
}));
function createGroupList() {
var element = $compile('<group-list></group-list>')($scope);
$scope.$digest();
return element;
}
it('should render groups', function () {
var element = createGroupList();
var groupItems = element.find('.group-item');
assert.equal(groupItems.length, groups.length + 1);
});
it('should render share links', function () {
var element = createGroupList();
var shareLinks = element.find('.share-link-container');
assert.equal(shareLinks.length, 1);
var linkField = element.find('.share-link-field');
assert.equal(linkField.length, 1);
assert.equal(linkField[0].value, GROUP_LINK);
});
it('should toggle share link on click', function () {
var element = createGroupList();
var toggleLink = element.find('.share-link-toggle');
var expander = element.find('.share-link-expander');
assert.ok(isElementHidden(expander));
toggleLink.click();
assert.ok(!isElementHidden(expander));
toggleLink.click();
assert.ok(isElementHidden(expander));
});
});
'use strict';
var PrivacyController = require('../privacy').PrivacyController;
var PrivacyController = require('../privacy').Controller;
describe('PrivacyController', function () {
var fakeScope;
......
@import 'compass';
@import 'compass/css3/flexbox';
$base-font-size: 12px;
$base-line-height: 20px;
......@@ -82,27 +83,99 @@ ol {
/* The groups dropdown list. */
$group-list-width: 225px;
$group-list-width: 270px;
.group-list .dropdown {
white-space: nowrap;
}
.group-list {
.dropdown {
white-space: nowrap;
}
.group-list .dropdown-menu {
width: $group-list-width;
}
.dropdown-menu {
width: $group-list-width;
.group-list .dropdown-menu li {
@include pie-clearfix;
}
.group-name {
overflow: hidden;
text-overflow: ellipsis;
width: $group-list-width - 30px;
}
}
.group-item {
@include display-flex;
@include flex-direction(row);
// IE 10 support
display: -ms-flexbox;
-ms-flex-direction: row;
padding: 10px;
cursor: pointer;
&:hover {
.share-link-toggle {
color: inherit;
}
.group-name-link {
color: $brand-color;
}
}
&.selected {
.group-name-link {
font-weight: 600;
}
}
}
.group-list .dropdown-menu .group-name {
float: left;
overflow: hidden;
text-overflow: ellipsis;
width: $group-list-width - 30px;
.group-icon-container {
margin-right: 10px;
}
.group-details {
margin-right: 20px;
}
.share-link-container {
font-size: $body1-font-size;
line-height: $body1-line-height;
margin-top: 1px;
white-space: normal;
}
.share-link-toggle {
color: $gray-light;
}
.share-link-toggle:hover {
text-decoration: underline;
color: $gray-dark;
}
.share-link-field {
padding: 3px;
padding-top: 5px;
padding-bottom: 5px;
margin-top: 12px;
margin-bottom: 8px;
border: 1px solid $gray-lighter;
width: 100%;
}
.new-group-btn {
background-color: $gray-lightest;
.group-item {
padding-top: 12px;
padding-bottom: 12px;
}
.h-icon-add {
font-weight: bold;
}
}
}
/* The user account dropdown menu */
.user-picker {
.avatar {
border-radius: 2px;
......
......@@ -180,25 +180,17 @@ html {
li:not(.ng-hide) {
text-align: left;
a {
& > a {
display: block;
line-height: 1;
padding: 1em;
white-space: nowrap;
}
&:hover {
color: black;
}
&.selected {
color: black;
font-weight: 600;
line-height: 1;
&:before {
font-size: .7em;
:hover {
color: $brand-color;
}
}
&.inactive {
font-weight: 400;
color: $gray-lighter;
......
......@@ -67,6 +67,9 @@ $alt-font-family: $serif-font-family !default;
$headings-font-family: inherit !default;
$headings-color: inherit !default;
$body1-font-size: 12px;
$body1-line-height: 1.4em;
//STANCE COLORS
$positive: #3aab39;
$negative: #d11c2b;
......
......@@ -2,29 +2,60 @@
<span class="dropdown-toggle"
data-toggle="dropdown"
role="button"
ng-switch on="groups.focused().public">
ng-switch on="groups.focused().public"
ng-click="toggleShareLink(undefined)">
<i class="h-icon-public" ng-switch-when="true"></i>
<i class="h-icon-group" ng-switch-default></i>
{{groups.focused().name}}
<i class="h-icon-arrow-drop-down"></i>
</span>
<ul class="dropdown-menu pull-right" role="menu">
<li ng-repeat="group in groups.all()"
ng-class="group.id == groups.focused().id? 'selected' : ''">
<a class="group-name"
href=""
ng-click="groups.focus(group.id)"
ng-switch on="group.public">
<i class="h-icon-public" ng-switch-when="true"></i>
<i class="h-icon-group" ng-switch-default></i>
{{group.name}}
</a>
<a ng-href="{{group.url}}" ng-if="group.url"
target="_blank" class="h-icon-link" title="Share this group"></a>
<li ng-repeat="group in sortedGroups()">
<div ng-class="{'group-item': true, selected: group.id == groups.focused().id}"
ng-click="groups.focus(group.id)">
<!-- the group icon !-->
<div class="group-icon-container" ng-switch on="group.public">
<i class="h-icon-public" ng-switch-when="true"></i>
<i class="h-icon-group" ng-switch-default></i>
</div>
<!-- the group name and share link !-->
<div class="group-details">
<div class="group-name-container">
<a class="group-name-link"
href=""
title="{{ group.public ? 'Show public annotations' : 'Show and create annotations in ' + group.name }}">
{{group.name}}
</a>
</div>
<div class="share-link-container" ng-click="$event.stopPropagation()" ng-if="!group.public">
<div>
<a class="share-link-toggle"
href=""
ng-click="toggleShareLink(group.id)"
title="Invite others to join this group">
Invite others to join this group
</a>
</div>
<div class="share-link-expander" ng-show="shouldShowShareLink(group.id)">
<input class="share-link-field" data-group-id="{{group.id}}" ng-value="group.url">
<p>
You can invite other people to join this group
by sending them this link
</p>
</div>
</div>
</div>
</div>
</li>
<li>
<a href="/groups/new" target="_blank"><i class="h-icon-add"></i>
New Group</a>
<li class="new-group-btn">
<div class="group-item" ng-click="createNewGroup()">
<div class="group-icon-container"><i class="h-icon-add"></i></div>
<div class="group-details">
<a href="" title="Create a new group to share annotations">
New group
</a>
</div>
</div>
</li>
</ul>
</div>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment