summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMorris Jobke <hey@morrisjobke.de>2017-10-25 01:07:51 +0200
committerGitHub <noreply@github.com>2017-10-25 01:07:51 +0200
commit1978867a11a9baf1ec50555264e65bcfd679c7e8 (patch)
tree6a41c13f05e5ebc4cb32250669e5a66f8f3fe52b
parent5d84daa167ca7d631175f54fc949d3f78e854978 (diff)
parent065ab6bfff47b4e869b76dacf3b077fd0aff8ad5 (diff)
downloadnextcloud-server-1978867a11a9baf1ec50555264e65bcfd679c7e8.tar.gz
nextcloud-server-1978867a11a9baf1ec50555264e65bcfd679c7e8.zip
Merge pull request #6709 from nextcloud/show-checkbox-where-the-favourite-icon-is-now
Show checkbox where the favourite icon is now
-rw-r--r--apps/files/css/files.scss100
-rw-r--r--apps/files/css/mobile.scss4
-rw-r--r--apps/files/js/fileactions.js13
-rw-r--r--apps/files/js/fileactionsmenu.js5
-rw-r--r--apps/files/js/filelist.js49
-rw-r--r--apps/files/js/tagsplugin.js75
-rw-r--r--apps/files/templates/list.php10
-rw-r--r--apps/files/tests/js/fileactionsmenuSpec.js28
-rw-r--r--apps/files/tests/js/filelistSpec.js30
-rw-r--r--apps/files/tests/js/tagspluginspec.js48
-rw-r--r--apps/files_trashbin/templates/index.php10
-rw-r--r--core/search/css/results.css10
-rw-r--r--tests/acceptance/features/app-files.feature16
-rw-r--r--tests/acceptance/features/bootstrap/FilesAppContext.php48
14 files changed, 289 insertions, 157 deletions
diff --git a/apps/files/css/files.scss b/apps/files/css/files.scss
index 8ae8fac6c21..c03b1ce6fad 100644
--- a/apps/files/css/files.scss
+++ b/apps/files/css/files.scss
@@ -230,9 +230,6 @@ table th#headerName {
position: relative;
height: 50px;
}
-.has-favorites #headerName-container {
- padding-left: 50px;
-}
table th#headerSize, table td.filesize {
text-align: right;
@@ -294,7 +291,12 @@ table td.filename a.name {
line-height: 50px;
padding: 0;
}
-table td.filename label.icon-loading-small {
+table td.filename .thumbnail-wrapper {
+ position: absolute;
+ width: 50px;
+ height: 50px;
+}
+table td.filename .thumbnail-wrapper.icon-loading-small {
&:after {
z-index: 10;
}
@@ -306,10 +308,10 @@ table td.filename .thumbnail {
display: inline-block;
width: 32px;
height: 32px;
+ background-size: 32px;
margin-left: 9px;
margin-top: 9px;
cursor: pointer;
- float: left;
position: absolute;
z-index: 4;
}
@@ -319,12 +321,9 @@ table td.filename input.filename {
margin-left: 48px;
cursor: text;
}
-.has-favorites table td.filename input.filename {
- margin-left: 52px;
-}
table td.filename a, table td.login, table td.logout, table td.download, table td.upload, table td.create, table td.delete { padding:3px 8px 8px 3px; }
-table td.filename .nametext, .uploadtext, .modified, .column-last>span:first-child { float:left; padding:15px 0; }
+table td.filename .nametext, .modified, .column-last>span:first-child { float:left; padding:15px 0; }
.modified, .column-last>span:first-child {
position: relative;
@@ -336,22 +335,23 @@ table td.filename .nametext, .uploadtext, .modified, .column-last>span:first-chi
/* TODO fix usability bug (accidental file/folder selection) */
table td.filename .nametext {
position: absolute;
- left: 55px;
padding: 0;
+ padding-left: 55px;
overflow: hidden;
text-overflow: ellipsis;
width: 70%;
max-width: 800px;
height: 100%;
+ z-index: 10;
+}
+table td.filename .uploadtext {
+ position: absolute;
+ left: 55px;
}
/* ellipsis on file names */
table td.filename .nametext .innernametext {
max-width: calc(100% - 100px) !important;
}
-.has-favorites #fileList td.filename a.name {
- left: 50px;
- margin-right: 50px;
-}
.hide-hidden-files #fileList tr.hidden-file,
.hide-hidden-files #fileList tr.hidden-file.dragging {
@@ -437,49 +437,27 @@ table td.filename .uploadtext {
opacity: .5;
}
+table td.selection {
+ padding: 0;
+}
+
/* File checkboxes */
-#fileList tr td.filename>.selectCheckBox + label:before {
- opacity: 0;
- position: absolute;
- bottom: 4px;
- right: 0;
- z-index: 10;
+#fileList tr td.selection>.selectCheckBox + label:before {
+ opacity: 0.3;
}
-/* Show checkbox when hovering, checked, or selected */
-#fileList tr:hover td.filename>.selectCheckBox + label:before,
-#fileList tr:focus td.filename>.selectCheckBox + label:before,
-#fileList tr td.filename>.selectCheckBox:checked + label:before,
-#fileList tr.selected td.filename>.selectCheckBox + label:before {
+/* Show checkbox with full opacity when hovering, checked, or selected */
+#fileList tr:hover td.selection>.selectCheckBox + label:before,
+#fileList tr:focus td.selection>.selectCheckBox + label:before,
+#fileList tr td.selection>.selectCheckBox:checked + label:before,
+#fileList tr.selected td.selection>.selectCheckBox + label:before {
opacity: 1;
}
/* Use label to have bigger clickable size for checkbox */
-#fileList tr td.filename>.selectCheckBox + label,
+#fileList tr td.selection>.selectCheckBox + label,
.select-all + label {
- background-position: 30px 30px;
- height: 50px;
- position: absolute;
- width: 50px;
- z-index: 5;
-}
-#fileList tr td.filename>.selectCheckBox {
- /* sometimes checkbox height is bigger (KDE/Qt), so setting to absolute
- * to prevent it to increase the height */
- position: absolute;
- z-index: 10;
-}
-.select-all + label {
- top: 0;
-}
-.select-all + label:before {
- position: absolute;
- top: 18px;
- left: 18px;
- z-index: 10;
-}
-.has-favorites .select-all {
- left: 68px;
+ padding: 16px;
}
#fileList tr td.filename {
@@ -500,10 +478,11 @@ table td.filename .uploadtext {
display: inline-block;
float: left;
}
-#fileList tr td.filename .action-favorite {
+#fileList tr td.filename .favorite-mark {
+ position: absolute;
display: block;
- float: left;
- width: 30px;
+ top: -6px;
+ right: -6px;
line-height: 100%;
text-align: center;
}
@@ -615,7 +594,7 @@ a.action > img {
padding-left: 6px;
}
-#fileList .action.action-favorite.permanent {
+#fileList .favorite-mark.permanent {
opacity: 1;
}
@@ -659,9 +638,6 @@ table tr.summary td {
.summary .info {
margin-left: 40px;
}
-.has-favorites .summary .info {
- margin-left: 90px;
-}
table.dragshadow {
width:auto;
@@ -714,12 +690,24 @@ table.dragshadow td.size {
#filestable .filename .action .icon,
#filestable .selectedActions a .icon,
+#filestable .filename .favorite-mark .icon,
#controls .actions .button .icon {
display: inline-block;
vertical-align: middle;
background-size: 16px 16px;
}
+#filestable .filename .favorite-mark {
+ // Override default icons to always hide the star icon and always show the
+ // starred icon even when hovered or focused.
+ & .icon-star {
+ background-image: none;
+ }
+ & .icon-starred {
+ background-image: url('../../../core/img/actions/starred.svg?v=1');
+ }
+}
+
#filestable .filename .action .icon.hidden,
#filestable .selectedActions a .icon.hidden,
#controls .actions .button .icon.hidden {
diff --git a/apps/files/css/mobile.scss b/apps/files/css/mobile.scss
index eefc92c816b..e7b75910fa9 100644
--- a/apps/files/css/mobile.scss
+++ b/apps/files/css/mobile.scss
@@ -24,10 +24,6 @@ table td.date {
table td {
padding: 0;
}
-/* and accordingly fix left margin of file list summary on mobile */
-.summary .info {
- margin-left: 105px;
-}
/* remove shift for multiselect bar to account for missing navigation */
table.multiselect thead {
diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js
index 3da9b06b0d3..0f320c8b3c7 100644
--- a/apps/files/js/fileactions.js
+++ b/apps/files/js/fileactions.js
@@ -706,7 +706,7 @@
* @property {String} mime mime type
* @property {int} permissions permissions
* @property {(Function|String)} icon icon path to the icon or function that returns it (deprecated, use iconClass instead)
- * @property {(Function|String)} iconClass class name of the icon (recommended for theming)
+ * @property {(String|OCA.Files.FileActions~iconClassFunction)} iconClass class name of the icon (recommended for theming)
* @property {OCA.Files.FileActions~renderActionFunction} [render] optional rendering function
* @property {OCA.Files.FileActions~actionHandler} actionHandler action handler function
*/
@@ -746,6 +746,17 @@
*/
/**
+ * Icon class function for actions.
+ * The function returns the icon class of the action using
+ * the given context information.
+ *
+ * @callback OCA.Files.FileActions~iconClassFunction
+ * @param {String} fileName name of the file on which the action must be performed
+ * @param {OCA.Files.FileActionContext} context action context
+ * @return {String} icon class
+ */
+
+ /**
* Action handler function for file actions
*
* @callback OCA.Files.FileActions~actionHandler
diff --git a/apps/files/js/fileactionsmenu.js b/apps/files/js/fileactionsmenu.js
index 45d2bd83049..b8022f13734 100644
--- a/apps/files/js/fileactionsmenu.js
+++ b/apps/files/js/fileactionsmenu.js
@@ -115,6 +115,11 @@
item = _.extend({}, item);
item.displayName = item.displayName(self._context);
}
+ if (_.isFunction(item.iconClass)) {
+ var fileName = self._context.$file.attr('data-file');
+ item = _.extend({}, item);
+ item.iconClass = item.iconClass(fileName, self._context);
+ }
return item;
});
items = items.sort(function(actionA, actionB) {
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index cc23ac73979..0d45c29b25a 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -332,7 +332,7 @@
this.$fileList.on('click','td.filename>a.name, td.filesize, td.date', _.bind(this._onClickFile, this));
- this.$fileList.on('change', 'td.filename>.selectCheckBox', _.bind(this._onClickFileCheckbox, this));
+ this.$fileList.on('change', 'td.selection>.selectCheckBox', _.bind(this._onClickFileCheckbox, this));
this.$el.on('show', _.bind(this._onShow, this));
this.$el.on('urlChanged', _.bind(this._onUrlChanged, this));
this.$el.find('.select-all').click(_.bind(this._onClickSelectAll, this));
@@ -593,7 +593,7 @@
* @param {bool} state true to select, false to deselect
*/
_selectFileEl: function($tr, state, showDetailsView) {
- var $checkbox = $tr.find('td.filename>.selectCheckBox');
+ var $checkbox = $tr.find('td.selection>.selectCheckBox');
var oldData = !!this._selectedFiles[$tr.data('id')];
var data;
$checkbox.prop('checked', state);
@@ -649,7 +649,7 @@
else {
this._lastChecked = $tr;
}
- var $checkbox = $tr.find('td.filename>.selectCheckBox');
+ var $checkbox = $tr.find('td.selection>.selectCheckBox');
this._selectFileEl($tr, !$checkbox.prop('checked'));
this.updateSelectionSummary();
} else {
@@ -704,7 +704,7 @@
*/
_onClickSelectAll: function(e) {
var checked = $(e.target).prop('checked');
- this.$fileList.find('td.filename>.selectCheckBox').prop('checked', checked)
+ this.$fileList.find('td.selection>.selectCheckBox').prop('checked', checked)
.closest('tr').toggleClass('selected', checked);
this._selectedFiles = {};
this._selectionSummary.clear();
@@ -1063,6 +1063,13 @@
this.$fileList.empty();
+ if (this._allowSelection) {
+ // The results table, which has no selection column, checks
+ // whether the main table has a selection column or not in order
+ // to align its contents with those of the main table.
+ this.$el.addClass('has-selection');
+ }
+
// clear "Select all" checkbox
this.$el.find('.select-all').prop('checked', false);
@@ -1192,6 +1199,20 @@
path = this.getCurrentDirectory();
}
+ // selection td
+ if (this._allowSelection) {
+ td = $('<td class="selection"></td>');
+
+ td.append(
+ '<input id="select-' + this.id + '-' + fileData.id +
+ '" type="checkbox" class="selectCheckBox checkbox"/><label for="select-' + this.id + '-' + fileData.id + '">' +
+ '<span class="hidden-visually">' + t('files', 'Select') + '</span>' +
+ '</label>'
+ );
+
+ tr.append(td);
+ }
+
// filename td
td = $('<td class="filename"></td>');
@@ -1203,22 +1224,13 @@
else {
linkUrl = this.getDownloadUrl(name, path, type === 'dir');
}
- if (this._allowSelection) {
- td.append(
- '<input id="select-' + this.id + '-' + fileData.id +
- '" type="checkbox" class="selectCheckBox checkbox"/><label for="select-' + this.id + '-' + fileData.id + '">' +
- '<div class="thumbnail" style="background-image:url(' + icon + '); background-size: 32px;"></div>' +
- '<span class="hidden-visually">' + t('files', 'Select') + '</span>' +
- '</label>'
- );
- } else {
- td.append('<div class="thumbnail" style="background-image:url(' + icon + '); background-size: 32px;"></div>');
- }
var linkElem = $('<a></a>').attr({
"class": "name",
"href": linkUrl
});
+ linkElem.append('<div class="thumbnail-wrapper"><div class="thumbnail" style="background-image:url(' + icon + ');"></div></div>');
+
// from here work on the display name
name = fileData.displayName || name;
@@ -2614,6 +2626,13 @@
*/
_createSummary: function() {
var $tr = $('<tr class="summary"></tr>');
+
+ if (this._allowSelection) {
+ // Dummy column for selection, as all rows must have the same
+ // number of columns.
+ $tr.append('<td></td>');
+ }
+
this.$el.find('tfoot').append($tr);
return new OCA.Files.FileSummary($tr, {config: this._filesConfig});
diff --git a/apps/files/js/tagsplugin.js b/apps/files/js/tagsplugin.js
index 9bd20be4bf8..2286477750c 100644
--- a/apps/files/js/tagsplugin.js
+++ b/apps/files/js/tagsplugin.js
@@ -17,12 +17,12 @@
PROPERTY_FAVORITE: '{' + OC.Files.Client.NS_OWNCLOUD + '}favorite'
});
- var TEMPLATE_FAVORITE_ACTION =
- '<a href="#" ' +
- 'class="action action-favorite {{#isFavorite}}permanent{{/isFavorite}}">' +
+ var TEMPLATE_FAVORITE_MARK =
+ '<div ' +
+ 'class="favorite-mark {{#isFavorite}}permanent{{/isFavorite}}">' +
'<span class="icon {{iconClass}}" />' +
'<span class="hidden-visually">{{altText}}</span>' +
- '</a>';
+ '</div>';
/**
* Returns the icon class for the matching state
@@ -42,24 +42,24 @@
*/
function renderStar(state) {
if (!this._template) {
- this._template = Handlebars.compile(TEMPLATE_FAVORITE_ACTION);
+ this._template = Handlebars.compile(TEMPLATE_FAVORITE_MARK);
}
return this._template({
isFavorite: state,
- altText: state ? t('files', 'Favorited') : t('files', 'Favorite'),
+ altText: state ? t('files', 'Favorited') : t('files', 'Not favorited'),
iconClass: getStarIconClass(state)
});
}
/**
- * Toggle star icon on action element
+ * Toggle star icon on favorite mark element
*
- * @param {Object} action element
+ * @param {Object} $favoriteMarkEl favorite mark element
* @param {boolean} state true if starred, false otherwise
*/
- function toggleStar($actionEl, state) {
- $actionEl.removeClass('icon-star icon-starred').addClass(getStarIconClass(state));
- $actionEl.toggleClass('permanent', state);
+ function toggleStar($favoriteMarkEl, state) {
+ $favoriteMarkEl.removeClass('icon-star icon-starred').addClass(getStarIconClass(state));
+ $favoriteMarkEl.toggleClass('permanent', state);
}
OCA.Files = OCA.Files || {};
@@ -67,8 +67,9 @@
/**
* @namespace OCA.Files.TagsPlugin
*
- * Extends the file actions and file list to include a favorite action icon
- * and addition "data-tags" and "data-favorite" attributes.
+ * Extends the file actions and file list to include a favorite mark icon
+ * and a favorite action in the file actions menu; it also adds "data-tags"
+ * and "data-favorite" attributes to file elements.
*/
OCA.Files.TagsPlugin = {
name: 'Tags',
@@ -84,22 +85,38 @@
_extendFileActions: function(fileActions) {
var self = this;
- // register "star" action
+
fileActions.registerAction({
name: 'Favorite',
- displayName: t('files', 'Favorite'),
+ displayName: function(context) {
+ var $file = context.$file;
+ var isFavorite = $file.data('favorite') === true;
+
+ if (isFavorite) {
+ return t('files', 'Remove from favorites');
+ }
+
+ // As it is currently not possible to provide a context for
+ // the i18n strings "Add to favorites" was used instead of
+ // "Favorite" to remove the ambiguity between verb and noun
+ // when it is translated.
+ return t('files', 'Add to favorites');
+ },
mime: 'all',
+ order: -23,
permissions: OC.PERMISSION_READ,
- type: OCA.Files.FileActions.TYPE_INLINE,
- render: function(actionSpec, isDefault, context) {
+ iconClass: function(fileName, context) {
var $file = context.$file;
var isFavorite = $file.data('favorite') === true;
- var $icon = $(renderStar(isFavorite));
- $file.find('td:first>.favorite').replaceWith($icon);
- return $icon;
+
+ if (isFavorite) {
+ return 'icon-starred';
+ }
+
+ return 'icon-star';
},
actionHandler: function(fileName, context) {
- var $actionEl = context.$file.find('.action-favorite');
+ var $favoriteMarkEl = context.$file.find('.favorite-mark');
var $file = context.$file;
var fileInfo = context.fileList.files[$file.index()];
var dir = context.dir || context.fileList.getCurrentDirectory();
@@ -118,14 +135,14 @@
}
// pre-toggle the star
- toggleStar($actionEl, !isFavorite);
+ toggleStar($favoriteMarkEl, !isFavorite);
context.fileInfoModel.trigger('busy', context.fileInfoModel, true);
self.applyFileTags(
dir + '/' + fileName,
tags,
- $actionEl,
+ $favoriteMarkEl,
isFavorite
).then(function(result) {
context.fileInfoModel.trigger('busy', context.fileInfoModel, false);
@@ -145,17 +162,19 @@
_extendFileList: function(fileList) {
// extend row prototype
- fileList.$el.addClass('has-favorites');
var oldCreateRow = fileList._createRow;
fileList._createRow = function(fileData) {
var $tr = oldCreateRow.apply(this, arguments);
+ var isFavorite = false;
if (fileData.tags) {
$tr.attr('data-tags', fileData.tags.join('|'));
if (fileData.tags.indexOf(OC.TAG_FAVORITE) >= 0) {
$tr.attr('data-favorite', true);
+ isFavorite = true;
}
}
- $tr.find('td:first').prepend('<div class="favorite"></div>');
+ var $icon = $(renderStar(isFavorite));
+ $tr.find('td.filename .thumbnail').append($icon);
return $tr;
};
var oldElementToFile = fileList.elementToFile;
@@ -215,10 +234,10 @@
*
* @param {String} fileName path to the file or folder to tag
* @param {Array.<String>} tagNames array of tag names
- * @param {Object} $actionEl element
+ * @param {Object} $favoriteMarkEl favorite mark element
* @param {boolean} isFavorite Was the item favorited before
*/
- applyFileTags: function(fileName, tagNames, $actionEl, isFavorite) {
+ applyFileTags: function(fileName, tagNames, $favoriteMarkEl, isFavorite) {
var encodedPath = OC.encodePath(fileName);
while (encodedPath[0] === '/') {
encodedPath = encodedPath.substr(1);
@@ -238,7 +257,7 @@
message = ': ' + response.responseJSON.message;
}
OC.Notification.show(t('files', 'An error occurred while trying to update the tags' + message), {type: 'error'});
- toggleStar($actionEl, isFavorite);
+ toggleStar($favoriteMarkEl, isFavorite);
});
}
};
diff --git a/apps/files/templates/list.php b/apps/files/templates/list.php
index 761a67be6a2..f980081336f 100644
--- a/apps/files/templates/list.php
+++ b/apps/files/templates/list.php
@@ -41,12 +41,14 @@
<table id="filestable" data-allow-public-upload="<?php p($_['publicUploadEnabled'])?>" data-preview-x="32" data-preview-y="32">
<thead>
<tr>
+ <th id="headerSelection" class="hidden column-selection">
+ <input type="checkbox" id="select_all_files" class="select-all checkbox"/>
+ <label for="select_all_files">
+ <span class="hidden-visually"><?php p($l->t('Select all'))?></span>
+ </label>
+ </th>
<th id='headerName' class="hidden column-name">
<div id="headerName-container">
- <input type="checkbox" id="select_all_files" class="select-all checkbox"/>
- <label for="select_all_files">
- <span class="hidden-visually"><?php p($l->t('Select all'))?></span>
- </label>
<a class="name sort columntitle" data-sort="name"><span><?php p($l->t( 'Name' )); ?></span><span class="sort-indicator"></span></a>
<span id="selectedActionsList" class="selectedActions">
<a href="" class="copy-move">
diff --git a/apps/files/tests/js/fileactionsmenuSpec.js b/apps/files/tests/js/fileactionsmenuSpec.js
index 3028db2b3ac..926516b3043 100644
--- a/apps/files/tests/js/fileactionsmenuSpec.js
+++ b/apps/files/tests/js/fileactionsmenuSpec.js
@@ -205,6 +205,34 @@ describe('OCA.Files.FileActionsMenu tests', function() {
expect(displayNameStub.calledWith(menuContext)).toEqual(true);
expect(menu.$el.find('a[data-action=Something]').text()).toEqual('Test');
});
+ it('uses plain iconClass', function() {
+ fileActions.registerAction({
+ name: 'Something',
+ mime: 'text/plain',
+ permissions: OC.PERMISSION_ALL,
+ iconClass: 'test'
+ });
+
+ menu.render();
+
+ expect(menu.$el.find('a[data-action=Something]').children('span.icon').hasClass('test')).toEqual(true);
+ });
+ it('calls iconClass function', function() {
+ var iconClassStub = sinon.stub().returns('test');
+
+ fileActions.registerAction({
+ name: 'Something',
+ mime: 'text/plain',
+ permissions: OC.PERMISSION_ALL,
+ iconClass: iconClassStub
+ });
+
+ menu.render();
+
+ expect(iconClassStub.calledOnce).toEqual(true);
+ expect(iconClassStub.calledWith(menuContext.$file.attr('data-file'), menuContext)).toEqual(true);
+ expect(menu.$el.find('a[data-action=Something]').children('span.icon').hasClass('test')).toEqual(true);
+ });
});
describe('action handler', function() {
diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js
index 836a5e5ce71..1f8d38c59ce 100644
--- a/apps/files/tests/js/filelistSpec.js
+++ b/apps/files/tests/js/filelistSpec.js
@@ -712,8 +712,14 @@ describe('OCA.Files.FileList tests', function() {
fileList.add(testFiles[i], {silent: true});
}
+ $tr = fileList.findFileEl('One.txt');
+ expect($tr.find('a.name').css('display')).not.toEqual('none');
+
// trigger rename prompt
fileList.rename('One.txt');
+
+ expect($tr.find('a.name').css('display')).toEqual('none');
+
$input = fileList.$fileList.find('input.filename');
$input.val('Two.jpg');
@@ -735,12 +741,12 @@ describe('OCA.Files.FileList tests', function() {
$tr = fileList.findFileEl('One.txt');
expect($tr.length).toEqual(1);
expect($tr.find('a .nametext').text().trim()).toEqual('One.txt');
- expect($tr.find('a.name').is(':visible')).toEqual(true);
+ expect($tr.find('a.name').css('display')).not.toEqual('none');
$tr = fileList.findFileEl('Two.jpg');
expect($tr.length).toEqual(1);
expect($tr.find('a .nametext').text().trim()).toEqual('Two.jpg');
- expect($tr.find('a.name').is(':visible')).toEqual(true);
+ expect($tr.find('a.name').css('display')).not.toEqual('none');
// input and form are gone
expect(fileList.$fileList.find('input.filename').length).toEqual(0);
@@ -750,7 +756,7 @@ describe('OCA.Files.FileList tests', function() {
doRename();
expect(fileList.findFileEl('Tu_after_three.txt').find('.thumbnail').parent().attr('class'))
- .toEqual('icon-loading-small');
+ .toContain('icon-loading-small');
deferredRename.reject(409);
@@ -838,7 +844,7 @@ describe('OCA.Files.FileList tests', function() {
fileList.move('One.txt', '/somedir');
expect(fileList.findFileEl('One.txt').find('.thumbnail').parent().attr('class'))
- .toEqual('icon-loading-small');
+ .toContain('icon-loading-small');
expect(moveStub.calledOnce).toEqual(true);
@@ -935,7 +941,7 @@ describe('OCA.Files.FileList tests', function() {
fileList.copy('One.txt', '/somedir');
expect(fileList.findFileEl('One.txt').find('.thumbnail').parent().attr('class'))
- .toEqual('icon-loading-small');
+ .toContain('icon-loading-small');
expect(copyStub.calledOnce).toEqual(true);
@@ -1741,7 +1747,7 @@ describe('OCA.Files.FileList tests', function() {
it('Selects a file when clicking its checkbox', function() {
var $tr = fileList.findFileEl('One.txt');
expect($tr.find('input:checkbox').prop('checked')).toEqual(false);
- $tr.find('td.filename input:checkbox').click();
+ $tr.find('td.selection input:checkbox').click();
expect($tr.find('input:checkbox').prop('checked')).toEqual(true);
});
@@ -1779,7 +1785,7 @@ describe('OCA.Files.FileList tests', function() {
var $tr = fileList.findFileEl('One.txt');
var $tr2 = fileList.findFileEl('Three.pdf');
var e;
- $tr.find('td.filename input:checkbox').click();
+ $tr.find('td.selection input:checkbox').click();
e = new $.Event('click');
e.shiftKey = true;
$tr2.find('td.filename .name').trigger(e);
@@ -1797,7 +1803,7 @@ describe('OCA.Files.FileList tests', function() {
var $tr = fileList.findFileEl('One.txt');
var $tr2 = fileList.findFileEl('Three.pdf');
var e;
- $tr2.find('td.filename input:checkbox').click();
+ $tr2.find('td.selection input:checkbox').click();
e = new $.Event('click');
e.shiftKey = true;
$tr.find('td.filename .name').trigger(e);
@@ -1813,13 +1819,13 @@ describe('OCA.Files.FileList tests', function() {
});
it('Selecting all files will automatically check "select all" checkbox', function() {
expect($('.select-all').prop('checked')).toEqual(false);
- $('#fileList tr td.filename input:checkbox').click();
+ $('#fileList tr td.selection input:checkbox').click();
expect($('.select-all').prop('checked')).toEqual(true);
});
it('Selecting all files on the first visible page will not automatically check "select all" checkbox', function() {
fileList.setFiles(generateFiles(0, 41));
expect($('.select-all').prop('checked')).toEqual(false);
- $('#fileList tr td.filename input:checkbox').click();
+ $('#fileList tr td.selection input:checkbox').click();
expect($('.select-all').prop('checked')).toEqual(false);
});
it('Selecting all files also selects hidden files when invisible', function() {
@@ -1831,7 +1837,7 @@ describe('OCA.Files.FileList tests', function() {
size: 150
}));
$('.select-all').click();
- expect($tr.find('td.filename input:checkbox').prop('checked')).toEqual(true);
+ expect($tr.find('td.selection input:checkbox').prop('checked')).toEqual(true);
expect(_.pluck(fileList.getSelectedFiles(), 'name')).toContain('.hidden');
});
it('Clicking "select all" will select/deselect all files', function() {
@@ -3150,7 +3156,7 @@ describe('OCA.Files.FileList tests', function() {
fileList.showFileBusyState('Two.jpg', true);
expect($tr.hasClass('busy')).toEqual(true);
expect($tr.find('.thumbnail').parent().attr('class'))
- .toEqual('icon-loading-small');
+ .toContain('icon-loading-small');
fileList.showFileBusyState('Two.jpg', false);
diff --git a/apps/files/tests/js/tagspluginspec.js b/apps/files/tests/js/tagspluginspec.js
index a4efc08aa53..363a8bb0e19 100644
--- a/apps/files/tests/js/tagspluginspec.js
+++ b/apps/files/tests/js/tagspluginspec.js
@@ -49,39 +49,39 @@ describe('OCA.Files.TagsPlugin tests', function() {
describe('Favorites icon', function() {
it('renders favorite icon and extra data', function() {
- var $action, $tr;
+ var $favoriteMark, $tr;
fileList.setFiles(testFiles);
$tr = fileList.$el.find('tbody tr:first');
- $action = $tr.find('.action-favorite');
- expect($action.length).toEqual(1);
- expect($action.hasClass('permanent')).toEqual(false);
+ $favoriteMark = $tr.find('.favorite-mark');
+ expect($favoriteMark.length).toEqual(1);
+ expect($favoriteMark.hasClass('permanent')).toEqual(false);
expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2']);
expect($tr.attr('data-favorite')).not.toBeDefined();
});
it('renders permanent favorite icon and extra data', function() {
- var $action, $tr;
+ var $favoriteMark, $tr;
testFiles[0].tags.push(OC.TAG_FAVORITE);
fileList.setFiles(testFiles);
$tr = fileList.$el.find('tbody tr:first');
- $action = $tr.find('.action-favorite');
- expect($action.length).toEqual(1);
- expect($action.hasClass('permanent')).toEqual(true);
+ $favoriteMark = $tr.find('.favorite-mark');
+ expect($favoriteMark.length).toEqual(1);
+ expect($favoriteMark.hasClass('permanent')).toEqual(true);
expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2', OC.TAG_FAVORITE]);
expect($tr.attr('data-favorite')).toEqual('true');
});
- it('adds has-favorites class on table', function() {
- expect(fileList.$el.hasClass('has-favorites')).toEqual(true);
- });
});
describe('Applying tags', function() {
- it('sends request to server and updates icon', function() {
+ it('through FileActionsMenu sends request to server and updates icon', function() {
var request;
fileList.setFiles(testFiles);
var $tr = fileList.findFileEl('One.txt');
- var $action = $tr.find('.action-favorite');
- $action.click();
+ var $favoriteMark = $tr.find('.favorite-mark');
+ var $showMenuAction = $tr.find('.action-menu');
+ $showMenuAction.click();
+ var $favoriteActionInMenu = $tr.find('.fileActionsMenu .action-favorite');
+ $favoriteActionInMenu.click();
expect(fakeServer.requests.length).toEqual(1);
request = fakeServer.requests[0];
@@ -94,15 +94,21 @@ describe('OCA.Files.TagsPlugin tests', function() {
// re-read the element as it was re-inserted
$tr = fileList.findFileEl('One.txt');
- $action = $tr.find('.action-favorite');
+ $favoriteMark = $tr.find('.favorite-mark');
+ $showMenuAction = $tr.find('.action-menu');
expect($tr.attr('data-favorite')).toEqual('true');
expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2', 'tag3', OC.TAG_FAVORITE]);
expect(fileList.files[0].tags).toEqual(['tag1', 'tag2', 'tag3', OC.TAG_FAVORITE]);
- expect($action.find('.icon').hasClass('icon-star')).toEqual(false);
- expect($action.find('.icon').hasClass('icon-starred')).toEqual(true);
+ expect($favoriteMark.find('.icon').hasClass('icon-star')).toEqual(false);
+ expect($favoriteMark.find('.icon').hasClass('icon-starred')).toEqual(true);
- $action.click();
+ // show again the menu and get the new action, as the menu was
+ // closed and removed (and with it, the previous action) when that
+ // action was clicked
+ $showMenuAction.click();
+ $favoriteActionInMenu = $tr.find('.fileActionsMenu .action-favorite');
+ $favoriteActionInMenu.click();
expect(fakeServer.requests.length).toEqual(2);
request = fakeServer.requests[1];
@@ -115,13 +121,13 @@ describe('OCA.Files.TagsPlugin tests', function() {
// re-read the element as it was re-inserted
$tr = fileList.findFileEl('One.txt');
- $action = $tr.find('.action-favorite');
+ $favoriteMark = $tr.find('.favorite-mark');
expect($tr.attr('data-favorite')).toBeFalsy();
expect($tr.attr('data-tags').split('|')).toEqual(['tag1', 'tag2', 'tag3']);
expect(fileList.files[0].tags).toEqual(['tag1', 'tag2', 'tag3']);
- expect($action.find('.icon').hasClass('icon-star')).toEqual(true);
- expect($action.find('.icon').hasClass('icon-starred')).toEqual(false);
+ expect($favoriteMark.find('.icon').hasClass('icon-star')).toEqual(true);
+ expect($favoriteMark.find('.icon').hasClass('icon-starred')).toEqual(false);
});
});
describe('elementToFile', function() {
diff --git a/apps/files_trashbin/templates/index.php b/apps/files_trashbin/templates/index.php
index c3f0304d408..b3ebca2867b 100644
--- a/apps/files_trashbin/templates/index.php
+++ b/apps/files_trashbin/templates/index.php
@@ -21,12 +21,14 @@
<table id="filestable">
<thead>
<tr>
+ <th id="headerSelection" class="hidden column-selection">
+ <input type="checkbox" id="select_all_trash" class="select-all checkbox"/>
+ <label for="select_all_trash">
+ <span class="hidden-visually"><?php p($l->t('Select all'))?></span>
+ </label>
+ </th>
<th id='headerName' class="hidden column-name">
<div id="headerName-container">
- <input type="checkbox" id="select_all_trash" class="select-all checkbox"/>
- <label for="select_all_trash">
- <span class="hidden-visually"><?php p($l->t('Select all'))?></span>
- </label>
<a class="name sort columntitle" data-sort="name"><span><?php p($l->t( 'Name' )); ?></span><span class="sort-indicator"></span></a>
<span id="selectedActionsList" class='selectedActions'>
<a href="" class="undelete">
diff --git a/core/search/css/results.css b/core/search/css/results.css
index 2e3791a47f0..e2ccfe36ef8 100644
--- a/core/search/css/results.css
+++ b/core/search/css/results.css
@@ -30,8 +30,8 @@
padding: 28px 0 28px 56px;
font-size: 18px;
}
-.has-favorites:not(.hidden) ~ #searchresults .status {
- padding-left: 102px;
+.has-selection:not(.hidden) ~ #searchresults .status {
+ padding-left: 105px;
}
#searchresults .status.fixed {
position: fixed;
@@ -54,7 +54,7 @@
}
#searchresults td {
- padding: 5px 19px;
+ padding: 5px 14px;
font-style: normal;
vertical-align: middle;
border-bottom: none;
@@ -67,8 +67,8 @@
background-position: right center;
background-repeat: no-repeat;
}
-.has-favorites:not(.hidden) ~ #searchresults td.icon {
- width: 86px;
+.has-selection:not(.hidden) ~ #searchresults td.icon {
+ width: 91px;
background-size: 32px;
}
diff --git a/tests/acceptance/features/app-files.feature b/tests/acceptance/features/app-files.feature
index 37e01bcada2..ac2d05fac2c 100644
--- a/tests/acceptance/features/app-files.feature
+++ b/tests/acceptance/features/app-files.feature
@@ -145,6 +145,14 @@ Feature: app-files
Given I am logged in
And I create a new folder named "A name alphabetically lower than welcome.txt"
And I see that "A name alphabetically lower than welcome.txt" precedes "welcome.txt" in the file list
+ # To mark the file as favorite the file actions menu has to be shown but, as
+ # the details view is opened automatically when the folder is created,
+ # clicking on the menu trigger could fail if it is covered by the details
+ # view due to its opening animation. Instead of ensuring that the animations
+ # of the contents and the details view have both finished it is easier to
+ # close the details view and wait until it is closed before continuing.
+ And I close the details view
+ And I see that the details view is closed
When I mark "welcome.txt" as favorite
Then I see that "welcome.txt" is marked as favorite
And I see that "welcome.txt" precedes "A name alphabetically lower than welcome.txt" in the file list
@@ -153,6 +161,14 @@ Feature: app-files
Given I am logged in
And I create a new folder named "A name alphabetically lower than welcome.txt"
And I see that "A name alphabetically lower than welcome.txt" precedes "welcome.txt" in the file list
+ # To mark the file as favorite the file actions menu has to be shown but, as
+ # the details view is opened automatically when the folder is created,
+ # clicking on the menu trigger could fail if it is covered by the details
+ # view due to its opening animation. Instead of ensuring that the animations
+ # of the contents and the details view have both finished it is easier to
+ # close the details view and wait until it is closed before continuing.
+ And I close the details view
+ And I see that the details view is closed
And I mark "welcome.txt" as favorite
And I see that "welcome.txt" is marked as favorite
And I see that "welcome.txt" precedes "A name alphabetically lower than welcome.txt" in the file list
diff --git a/tests/acceptance/features/bootstrap/FilesAppContext.php b/tests/acceptance/features/bootstrap/FilesAppContext.php
index bb088c0a2c3..338823a9478 100644
--- a/tests/acceptance/features/bootstrap/FilesAppContext.php
+++ b/tests/acceptance/features/bootstrap/FilesAppContext.php
@@ -80,6 +80,15 @@ class FilesAppContext implements Context, ActorAwareInterface {
/**
* @return Locator
*/
+ public static function closeDetailsViewButton() {
+ return Locator::forThe()->css(".icon-close")->
+ descendantOf(self::currentSectionDetailsView())->
+ describedAs("Close current section details view in Files app");
+ }
+
+ /**
+ * @return Locator
+ */
public static function fileDetailsInCurrentSectionDetailsViewWithText($fileDetailsText) {
return Locator::forThe()->xpath("//span[normalize-space() = '$fileDetailsText']")->
descendantOf(self::fileDetailsInCurrentSectionDetailsView())->
@@ -278,16 +287,16 @@ class FilesAppContext implements Context, ActorAwareInterface {
/**
* @return Locator
*/
- public static function favoriteActionForFile($fileName) {
- return Locator::forThe()->css(".action-favorite")->descendantOf(self::rowForFile($fileName))->
- describedAs("Favorite action for file $fileName in Files app");
+ public static function favoriteMarkForFile($fileName) {
+ return Locator::forThe()->css(".favorite-mark")->descendantOf(self::rowForFile($fileName))->
+ describedAs("Favorite mark for file $fileName in Files app");
}
/**
* @return Locator
*/
public static function notFavoritedStateIconForFile($fileName) {
- return Locator::forThe()->css(".icon-star")->descendantOf(self::favoriteActionForFile($fileName))->
+ return Locator::forThe()->css(".icon-star")->descendantOf(self::favoriteMarkForFile($fileName))->
describedAs("Not favorited state icon for file $fileName in Files app");
}
@@ -295,7 +304,7 @@ class FilesAppContext implements Context, ActorAwareInterface {
* @return Locator
*/
public static function favoritedStateIconForFile($fileName) {
- return Locator::forThe()->css(".icon-starred")->descendantOf(self::favoriteActionForFile($fileName))->
+ return Locator::forThe()->css(".icon-starred")->descendantOf(self::favoriteMarkForFile($fileName))->
describedAs("Favorited state icon for file $fileName in Files app");
}
@@ -341,6 +350,20 @@ class FilesAppContext implements Context, ActorAwareInterface {
/**
* @return Locator
*/
+ public static function addToFavoritesMenuItem() {
+ return self::fileActionsMenuItemFor("Add to favorites");
+ }
+
+ /**
+ * @return Locator
+ */
+ public static function removeFromFavoritesMenuItem() {
+ return self::fileActionsMenuItemFor("Remove from favorites");
+ }
+
+ /**
+ * @return Locator
+ */
public static function viewFileInFolderMenuItem() {
return self::fileActionsMenuItemFor("View in folder");
}
@@ -374,6 +397,13 @@ class FilesAppContext implements Context, ActorAwareInterface {
}
/**
+ * @Given I close the details view
+ */
+ public function iCloseTheDetailsView() {
+ $this->actor->find(self::closeDetailsViewButton(), 10)->click();
+ }
+
+ /**
* @Given I open the input field for tags in the details view
*/
public function iOpenTheInputFieldForTagsInTheDetailsView() {
@@ -393,7 +423,9 @@ class FilesAppContext implements Context, ActorAwareInterface {
public function iMarkAsFavorite($fileName) {
$this->iSeeThatIsNotMarkedAsFavorite($fileName);
- $this->actor->find(self::favoriteActionForFile($fileName), 10)->click();
+ $this->actor->find(self::fileActionsMenuButtonForFile($fileName), 10)->click();
+
+ $this->actor->find(self::addToFavoritesMenuItem(), 2)->click();
}
/**
@@ -402,7 +434,9 @@ class FilesAppContext implements Context, ActorAwareInterface {
public function iUnmarkAsFavorite($fileName) {
$this->iSeeThatIsMarkedAsFavorite($fileName);
- $this->actor->find(self::favoriteActionForFile($fileName), 10)->click();
+ $this->actor->find(self::fileActionsMenuButtonForFile($fileName), 10)->click();
+
+ $this->actor->find(self::removeFromFavoritesMenuItem(), 2)->click();
}
/**