Commit f8dbea10 authored by Colin PUY's avatar Colin PUY

Favorites in home page

parent c8f44b93
......@@ -83,7 +83,16 @@
forceImport(uuid) {
return this.$http.post(`import/${this.type}/${uuid}`);
}
markAsFavorite(artifactId) {
return this.$http.put(`${this.baseUrl}/${artifactId}/favorite`, true);
}
unmarkAsFavorite(artifactId) {
return this.$http.put(`${this.baseUrl}/${artifactId}/favorite`, false);
}
}
return Repository;
}
......
(function() {
'use strict';
angular
.module('bonitasoft.designer.home')
.directive('favoriteButton', favoriteButton);
function favoriteButton() {
return {
scope: {
artifact: '=',
repository: '=artifactRepository'
},
controller: FavoriteButtonCtrl,
controllerAs: 'vm',
bindToController: true,
replace: true,
templateUrl: 'js/home/favorite-button.html'
};
}
function FavoriteButtonCtrl(gettextCatalog) {
var vm = this;
vm.toggleFavorite = function() {
if (vm.artifact.favorite) {
vm.repository.unmarkAsFavorite(vm.artifact.id);
} else {
vm.repository.markAsFavorite(vm.artifact.id);
}
vm.artifact.favorite = !vm.artifact.favorite;
};
vm.isFavorite = function() {
return vm.artifact.favorite;
};
vm.getTitle = function() {
return vm.artifact.favorite ?
gettextCatalog.getString('Unmark as favorite') :
gettextCatalog.getString('Mark as favorite');
};
}
})();
<button class="Artifact-button Artifact-button-favorite"
ng-class="{'Artifact-button-favorite--checked': vm.isFavorite()}"
ng-click="vm.toggleFavorite()"
type="button"
title="{{vm.getTitle()}}">
<i class="fa"
ng-class="{'fa-star-o': !vm.isFavorite(), 'fa-star': vm.isFavorite()}"></i>
</button>
<form class="col-xs-4 col-xs-offset-8" role="search">
<form class="form-inline HomeFilters-form" role="search">
<div class="form-group">
<input type="text" ng-model="filters.name" class="Home-SearchBox form-control" placeholder="Search">
</div>
<div class="form-group radio">
<label>
<input type="radio" name="all" ng-model="filters.favorite" ng-value="undefined">
<translate>All</translate>
</label>
<label>
<input type="radio" name="favorites" ng-model="filters.favorite" ng-value="true">
<translate>Favorites</translate>
</label>
</div>
</form>
......@@ -12,12 +12,17 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
(function() {
'use strict';
angular.module('bonitasoft.designer.home').directive('homePages', function() {
return {
restrict: 'E',
templateUrl: 'js/home/home-pages.html'
};
});
})();
(function() {
'use strict';
angular
.module('bonitasoft.designer.home')
.directive('homePages', function(pageRepo) {
return {
restrict: 'E',
templateUrl: 'js/home/home-pages.html',
link: (scope) => scope.pageRepo = pageRepo
};
});
})();
......@@ -60,6 +60,7 @@
</form>
</div>
<div class="Artifact-actions">
<favorite-button artifact-repository="pageRepo" artifact="page"></favorite-button>
<button class="Artifact-button Artifact-button--primary btn-page-rename "
ng-click="toggleItemEdition(page)"
type="button"
......
......@@ -14,10 +14,11 @@
*/
(function() {
'use strict';
angular.module('bonitasoft.designer.home').directive('homeWidgets', function() {
angular.module('bonitasoft.designer.home').directive('homeWidgets', function(widgetRepo) {
return {
restrict: 'E',
templateUrl: 'js/home/home-widgets.html'
templateUrl: 'js/home/home-widgets.html',
link: (scope) => scope.widgetRepo = widgetRepo
};
});
})();
......@@ -44,6 +44,7 @@
</a>
</div>
<div class="Artifact-actions">
<favorite-button artifact-repository="widgetRepo" artifact="widget"></favorite-button>
<file-download class="Artifact-button Artifact-button--primary btn-widget-export" href="exportWidgetUrl(widget)" title="{{'Export widget' | translate }}"></file-download>
<button class="Artifact-button btn-widget-delete"
type="button"
......
......@@ -2,7 +2,7 @@
<div class="container container--Home">
<div class="row">
<home-filters filters="filters"></home-filters>
<home-filters class="col-xs-12" filters="filters"></home-filters>
</div>
<div class="row row--Home">
<section class="col-sm-7 col--Home" id="pages">
......
......@@ -10,7 +10,7 @@
}
.HomeHeader:before {
content:"";
content: "";
display: inline-block;
vertical-align: middle;
width: 1px;
......@@ -18,7 +18,7 @@
}
.HomeHeader-logo {
position:absolute;
position: absolute;
top: 50%;
left: 3.6em;
transform: translate(0, -50%);
......@@ -43,7 +43,7 @@
}
.HomeHeader-help {
position:absolute;
position: absolute;
top: 50%;
right: 3.6em;
transform: translate(0, -50%);
......@@ -67,9 +67,10 @@
height: calc(~"100% - 100px");
padding: 1.5em;
}
.row--Home, .col--Home {
.row--Home, .col--Home {
height: 100%;
}
}
.Panel {
height: 100%;
......@@ -78,7 +79,7 @@
overflow: auto;
}
.Panel-heading{
.Panel-heading {
color: @color-default-panel-header-title;
background-color: @color-default-panel-header-bg;
display: table;
......@@ -86,18 +87,18 @@
font-size: 1.1em
}
.Panel-heading--primary{
.Panel-heading--primary {
color: @color-primary-panel-header-title;
background-color: @color-primary-panel-header-bg;
}
.Panel-icon{
.Panel-icon {
margin-right: .5em;
}
.Panel-title{
.Panel-title {
margin: 0;
padding:.8em;
padding: .8em;
font-size: 1.15em;
text-transform: uppercase;
font-weight: normal;
......@@ -131,7 +132,6 @@
background-color: saturate(lighten(@color-default-panel-header-button-bg, 10%), 10%);
}
.PanelButton--primary {
background-color: @color-primary-panel-header-button-bg;
}
......@@ -141,7 +141,6 @@
background-color: saturate(lighten(@color-primary-panel-header-button-bg, 10%), 10%);
}
.ArtifactList {
margin-top: 1em;
padding-left: 0;
......@@ -181,7 +180,7 @@
.Artifact-link span {
padding: .7em;
font-size: 1.2em;
width:100%;
width: 100%;
display: table-cell;
white-space: nowrap;
overflow: hidden;
......@@ -243,7 +242,7 @@
.Artifact-button {
border: none;
background: none;
color: @color-default-panel-item-action-icon;
color: @color-default-panel-item-action-icon;
}
.Artifact-button:hover {
......@@ -258,6 +257,18 @@
color: @color-primary-panel-item-active;
}
.Artifact-button-favorite:hover {
color: @Artifact-button--favorite-color;
}
.Artifact-button-favorite--checked {
color: @Artifact-button--favorite-color;
&:hover {
color: darken(@Artifact-button--favorite-color, 5%);
}
}
.CreateArtifact {
margin: .5em 2em;
}
......@@ -288,7 +299,7 @@
color: @color-primary-panel-create;
}
.CreateArtifact-label::after{
.CreateArtifact-label::after {
content: "";
position: absolute;
width: 115px;
......@@ -321,8 +332,21 @@
padding-left: 0.8em;
list-style: none;
}
.ImportReport--smallAndUnderlined{
.ImportReport--smallAndUnderlined {
text-decoration: underline;
font-size: 0.8em;
}
}
.HomeFilters-form {
padding-bottom: 0.5em;
}
.form-control.Home-SearchBox {
width: 25em;
margin-right: 1em;
}
.HomeFilters-form .radio label {
padding-left: 8px;
}
......@@ -70,6 +70,8 @@
@btn-bonita-help-bg: lighten(@btn-bonita-default-bg, 10%);
@btn-bonita-help-border: transparent;
@Artifact-button--favorite-color: #ffe502;
.btn-bonita-default {
.button-variant(@btn-bonita-default-color, @btn-bonita-default-bg, @btn-bonita-default-border);
}
......
......@@ -223,6 +223,7 @@ angular.module('bonitasoft.designer.e2e', ['ngMockE2E'])
name: 'favoriteWidget',
custom: true,
lastUpdate: 1447891242960,
favorite: true,
template: '<div>My {{ properties.qualifier }} widget just {{ properties.verb }}!</div>',
properties: [
{
......@@ -249,6 +250,7 @@ angular.module('bonitasoft.designer.e2e', ['ngMockE2E'])
name: 'Person',
type: 'page',
lastUpdate: 1447944407862,
favorite: true,
data: {
alreadyExistsData: { type: 'constant', value: 'aValue' },
jsonExample: { type: 'json', value: {} },
......@@ -565,7 +567,7 @@ angular.module('bonitasoft.designer.e2e', ['ngMockE2E'])
// get all light representation
$httpBackend.whenGET('rest/widgets?view=light').respond(function() {
var response = widgets.map(({id, name, custom, lastUpdate}) => ({id, name, custom, lastUpdate}));
var response = widgets.map(({id, name, custom, lastUpdate, favorite}) => ({id, name, custom, lastUpdate, favorite}));
return [200, response, {}];
});
......@@ -601,7 +603,7 @@ angular.module('bonitasoft.designer.e2e', ['ngMockE2E'])
* ******************************************************************************************************/
// get all (light representation)
$httpBackend.whenGET('rest/pages').respond(function() {
var response = pages.map(({id, name, type, lastUpdate}) => ({id, name, type, lastUpdate}));
var response = pages.map(({id, name, type, lastUpdate, favorite}) => ({id, name, type, lastUpdate, favorite}));
return [200, response, {}];
});
......
......@@ -16,4 +16,12 @@ export default class Home {
search(term) {
$('input.Home-SearchBox').clear().sendKeys(term);
}
filterFavorites() {
element(by.cssContainingText('home-filters label', 'Favorites')).click();
}
unfilterFavorites() {
element(by.cssContainingText('home-filters label', 'All')).click();
}
}
......@@ -4,7 +4,6 @@ describe('home page', function() {
let ALL_PAGE_NAMES = ['Person', 'empty'];
let ALL_WIDGET_NAMES = ['awesomeWidget', 'favoriteWidget'];
var home;
beforeEach(function() {
......@@ -187,4 +186,14 @@ describe('home page', function() {
expect(home.getListedPageNames()).toEqual(ALL_PAGE_NAMES);
expect(home.getListedWidgetNames()).toEqual(ALL_WIDGET_NAMES);
});
it('should filter favorite widgets and pages', function() {
home.filterFavorites();
expect(home.getListedPageNames()).toEqual(['Person']);
expect(home.getListedWidgetNames()).toEqual(['favoriteWidget']);
home.unfilterFavorites();
expect(home.getListedPageNames()).toEqual(ALL_PAGE_NAMES);
expect(home.getListedWidgetNames()).toEqual(ALL_WIDGET_NAMES);
});
});
......@@ -193,10 +193,27 @@ describe('pageRepo', function() {
pageRepo.createAsset('my-page', asset);
$httpBackend.flush();
});
it('should call import/page/uuid when forceImport is called', () => {
var uuid = 'zezaerze-zerz-zer-zer';
$httpBackend.expectPOST('import/page/' + uuid).respond('');
pageRepo.forceImport(uuid);
$httpBackend.flush();
});
it('should mark a page as favorite', function() {
$httpBackend.expectPUT('rest/pages/page-id/favorite', true).respond('');
pageRepo.markAsFavorite('page-id');
$httpBackend.flush();
});
it('should unmark a page as favorite', function() {
$httpBackend.expectPUT('rest/pages/page-id/favorite', false).respond('');
pageRepo.unmarkAsFavorite('page-id');
$httpBackend.flush();
});
});
......@@ -202,10 +202,27 @@ describe('widgetRepo', function() {
widgetRepo.incrementOrderAsset('my-widget', asset);
$httpBackend.flush();
});
it('should call import/widget/uuid when forceImport is called', () => {
var uuid = 'zezaerze-zerz-zer-zer';
$httpBackend.expectPOST('import/widget/' + uuid).respond('');
widgetRepo.forceImport(uuid);
$httpBackend.flush();
});
it('should mark a widget as favorite', function() {
$httpBackend.expectPUT('rest/widgets/widget-id/favorite', true).respond('');
widgetRepo.markAsFavorite('widget-id');
$httpBackend.flush();
});
it('should unmark a widget as favorite', function() {
$httpBackend.expectPUT('rest/widgets/widget-id/favorite', false).respond('');
widgetRepo.unmarkAsFavorite('widget-id');
$httpBackend.flush();
});
});
describe('favorite button', function () {
var $scope, element, artifact, controller, pageRepo;
beforeEach(angular.mock.module('bonitasoft.designer.home'));
beforeEach(inject(function ($compile, $rootScope, _pageRepo_) {
$scope = $rootScope.$new();
artifact = {
type: 'page',
id: 'pageId'
};
pageRepo = _pageRepo_;
$scope.repository = pageRepo;
$scope.artifact = artifact;
element = $compile('<favorite-button artifact-repository="repository" artifact="artifact"></favorite-button>')($scope);
$scope.$apply();
controller = element.controller('favoriteButton');
}));
describe('controller', function () {
it('should mark an artifact as favorite', function () {
spyOn(pageRepo, 'markAsFavorite');
artifact.favorite = false;
controller.toggleFavorite();
expect(artifact.favorite).toBeTruthy();
expect(pageRepo.markAsFavorite).toHaveBeenCalledWith(artifact.id);
});
it('should unmark an artifact as favorite', function () {
spyOn(pageRepo, 'unmarkAsFavorite');
artifact.favorite = true;
controller.toggleFavorite();
expect(artifact.favorite).toBeFalsy();
expect(pageRepo.unmarkAsFavorite).toHaveBeenCalledWith(artifact.id);
});
it('should say if artifact is favorite or not', function () {
artifact.favorite = true;
expect(controller.isFavorite()).toBeTruthy();
artifact.favorite = false;
expect(controller.isFavorite()).toBeFalsy();
});
it('should get different title depending on artifact favorite state', function () {
artifact.favorite = true;
expect(controller.getTitle()).toBe('Unmark as favorite');
artifact.favorite = false;
expect(controller.getTitle()).toBe('Mark as favorite');
});
});
describe('directive', function () {
it('should toogle favorite state while clicking on it', function () {
spyOn(controller, 'toggleFavorite');
element.click();
expect(controller.toggleFavorite).toHaveBeenCalled();
});
it('should display title from controller', function() {
spyOn(controller, 'getTitle').and.returnValue('title from controller');
$scope.$apply();
expect(element.attr('title')).toBe('title from controller');
});
it('should display an icon depending on artifact favorite state', function() {
artifact.favorite = true;
$scope.$apply();
expect(element.find('i').attr('class')).toBe('fa fa-star');
artifact.favorite = false;
$scope.$apply();
expect(element.find('i').attr('class')).toBe('fa fa-star-o');
});
it('should have a special class name when artifact is marked as favorite', function() {
artifact.favorite = true;
$scope.$apply();
expect(element.attr('class')).toContain('Artifact-button-favorite--checked');
artifact.favorite = false;
$scope.$apply();
expect(element.attr('class')).not.toContain('Artifact-button-favorite--checked');
});
});
});
describe('home filters', () => {
var element, $scope;
beforeEach(angular.mock.module('bonitasoft.designer.home'));
beforeEach(inject(function ($compile, $rootScope) {
$scope = $rootScope.$new();
$scope.filters = {};
element = $compile('<home-filters filters="filters"></home-filters>')($scope);
$scope.$apply();
}));
describe('directive', () => {
it('should toggle favorite filter', function() {
element.find('input[name="favorites"]').click().trigger('click');
expect($scope.filters.favorite).toBe(true);
element.find('input[name="all"]').click().trigger('click');
expect($scope.filters.favorite).toBeUndefined();
});
});
});
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