summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClement Ho <ClemMakesApps@gmail.com>2017-02-24 12:21:53 -0600
committerClement Ho <ClemMakesApps@gmail.com>2017-02-24 20:21:12 -0600
commite7926605e2225f5a956954e134282118b1ed4f9e (patch)
treecf7a724812f75d115b5cc30e60519642bc1ec561
parent4e4e7f20811bac3f9989a59a75c5754c79a27593 (diff)
downloadgitlab-ce-backspace-to-edit-visual-token.tar.gz
Add ability to edit visual token using backspacebackspace-to-edit-visual-token
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js.es67
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js.es628
-rw-r--r--spec/javascripts/filtered_search/filtered_search_manager_spec.js.es685
-rw-r--r--spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js.es690
-rw-r--r--spec/javascripts/helpers/filtered_search_spec_helper.js.es628
5 files changed, 200 insertions, 38 deletions
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6
index c1fc1111ecf..081c2fa9aeb 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6
@@ -63,6 +63,13 @@
// 8 = Backspace Key
// 46 = Delete Key
if (e.keyCode === 8 || e.keyCode === 46) {
+ const { lastVisualToken } = gl.FilteredSearchVisualTokens.getLastVisualToken();
+
+ if (this.filteredSearchInput.value === '' && lastVisualToken) {
+ this.filteredSearchInput.value = gl.FilteredSearchVisualTokens.getLastTokenPartial();
+ gl.FilteredSearchVisualTokens.removeLastTokenPartial();
+ }
+
// Reposition dropdown so that it is aligned with cursor
this.dropdownManager.updateCurrentDropdownOffset();
}
diff --git a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js.es6
index 124d1740767..33f9d2e2651 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js.es6
+++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js.es6
@@ -45,6 +45,34 @@ class FilteredSearchVisualTokens {
static addSearchVisualToken(searchTerm) {
FilteredSearchVisualTokens.addVisualTokenElement(searchTerm, null, true);
}
+
+ static getLastTokenPartial() {
+ const { lastVisualToken } = FilteredSearchVisualTokens.getLastVisualToken();
+
+ if (!lastVisualToken) return '';
+
+ const value = lastVisualToken.querySelector('.value');
+ const name = lastVisualToken.querySelector('.name');
+
+ const valueText = value ? value.innerText : '';
+ const nameText = name ? name.innerText : '';
+
+ return valueText || nameText;
+ }
+
+ static removeLastTokenPartial() {
+ const { lastVisualToken } = FilteredSearchVisualTokens.getLastVisualToken();
+
+ if (lastVisualToken) {
+ const value = lastVisualToken.querySelector('.value');
+
+ if (value) {
+ lastVisualToken.removeChild(value);
+ } else {
+ lastVisualToken.parentElement.removeChild(lastVisualToken);
+ }
+ }
+ }
}
window.gl = window.gl || {};
diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js.es6 b/spec/javascripts/filtered_search/filtered_search_manager_spec.js.es6
index 2440e55bb47..975fb1da40a 100644
--- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js.es6
+++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js.es6
@@ -4,6 +4,7 @@ require('~/filtered_search/filtered_search_token_keys');
require('~/filtered_search/filtered_search_tokenizer');
require('~/filtered_search/filtered_search_dropdown_manager');
require('~/filtered_search/filtered_search_manager');
+const FilteredSearchSpecHelper = require('../helpers/filtered_search_spec_helper');
(() => {
describe('Filtered Search Manager', () => {
@@ -107,12 +108,7 @@ require('~/filtered_search/filtered_search_manager');
});
it('should not render placeholder when there are tokens and no input', () => {
- tokensContainer.innerHTML = `
- <li class="js-visual-token filtered-search-token">
- <div class="name">label</div>
- <div class="value">~bug</div>
- </li>
- `;
+ tokensContainer.innerHTML = FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug');
const event = new Event('input');
input.dispatchEvent(event);
@@ -120,5 +116,82 @@ require('~/filtered_search/filtered_search_manager');
expect(input.placeholder).toEqual('');
});
});
+
+ describe('checkForBackspace', () => {
+ let tokensContainer;
+ const backspaceKey = 8;
+ const deleteKey = 46;
+
+ beforeEach(() => {
+ setFixtures(`
+ <form>
+ <ul class="tokens-container list-unstyled"></ul>
+ <input type='text' class='filtered-search' />
+ <button class="clear-search" type="button">
+ <i class="fa fa-times"></i>
+ </button>
+ </form>
+ `);
+
+ spyOn(gl.FilteredSearchManager.prototype, 'cleanup').and.callFake(() => {});
+ spyOn(gl.FilteredSearchManager.prototype, 'loadSearchParamsFromURL').and.callFake(() => {});
+ spyOn(gl.FilteredSearchManager.prototype, 'tokenChange').and.callFake(() => {});
+ spyOn(gl.FilteredSearchDropdownManager.prototype, 'setDropdown').and.callFake(() => {});
+ spyOn(gl.FilteredSearchDropdownManager.prototype, 'updateDropdownOffset').and.callFake(() => {});
+
+ spyOn(gl.utils, 'getParameterByName').and.returnValue(null);
+
+ input = document.querySelector('.filtered-search');
+ tokensContainer = document.querySelector('.tokens-container');
+ return new gl.FilteredSearchManager();
+ });
+
+ afterEach(() => {
+ input.outerHTML = '';
+ tokensContainer.innerHTML = '';
+ });
+
+ describe('tokens and no input', () => {
+ beforeEach(() => {
+ tokensContainer.innerHTML = FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug');
+ });
+
+ it('removes last token', () => {
+ spyOn(gl.FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough();
+
+ const event = new Event('keyup');
+ event.keyCode = backspaceKey;
+ input.dispatchEvent(event);
+
+ expect(gl.FilteredSearchVisualTokens.removeLastTokenPartial).toHaveBeenCalled();
+ });
+
+ it('sets the input', () => {
+ spyOn(gl.FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough();
+
+ const event = new Event('keyup');
+ event.keyCode = deleteKey;
+ input.dispatchEvent(event);
+
+ expect(gl.FilteredSearchVisualTokens.getLastTokenPartial).toHaveBeenCalled();
+ expect(input.value).toEqual('~bug');
+ });
+ });
+
+ it('does not remove token or change input when there is existing input', () => {
+ spyOn(gl.FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough();
+ spyOn(gl.FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough();
+
+ input.value = 'text';
+
+ const event = new Event('keyup');
+ event.keyCode = deleteKey;
+ input.dispatchEvent(event);
+
+ expect(gl.FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled();
+ expect(gl.FilteredSearchVisualTokens.getLastTokenPartial).not.toHaveBeenCalled();
+ expect(input.value).toEqual('text');
+ });
+ });
});
})();
diff --git a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js.es6 b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js.es6
index 4f07dfbd7d9..8c5150a6134 100644
--- a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js.es6
+++ b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js.es6
@@ -1,4 +1,5 @@
require('~/filtered_search/filtered_search_visual_tokens');
+const FilteredSearchSpecHelper = require('../helpers/filtered_search_spec_helper');
(() => {
describe('Filtered Search Visual Tokens', () => {
@@ -25,12 +26,7 @@ require('~/filtered_search/filtered_search_visual_tokens');
});
it('returns when there is one visual token', () => {
- tokensContainer.innerHTML = `
- <li class="js-visual-token filtered-search-token">
- <div class="name">label</div>
- <div class="value">~bug</div>
- </li>
- `;
+ tokensContainer.innerHTML = FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug');
const { lastVisualToken, isLastVisualTokenValid }
= gl.FilteredSearchVisualTokens.getLastVisualToken();
@@ -40,11 +36,7 @@ require('~/filtered_search/filtered_search_visual_tokens');
});
it('returns when there is an incomplete visual token', () => {
- tokensContainer.innerHTML = `
- <li class="js-visual-token filtered-search-token">
- <div class="name">Author</div>
- </li>
- `;
+ tokensContainer.innerHTML = FilteredSearchSpecHelper.createNameFilterVisualTokenHTML('Author');
const { lastVisualToken, isLastVisualTokenValid }
= gl.FilteredSearchVisualTokens.getLastVisualToken();
@@ -55,17 +47,9 @@ require('~/filtered_search/filtered_search_visual_tokens');
it('returns when there are multiple visual tokens', () => {
tokensContainer.innerHTML = `
- <li class="js-visual-token filtered-search-token">
- <div class="name">label</div>
- <div class="value">~bug</div>
- </li>
- <li class="js-visual-token filtered-search-term">
- <div class="name">search term</div>
- </li>
- <li class="js-visual-token filtered-search-token">
- <div class="name">author</div>
- <div class="value">@root</div>
- </li>
+ ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug')}
+ ${FilteredSearchSpecHelper.createSearchVisualTokenHTML('search term')}
+ ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('author', '@root')}
`;
const { lastVisualToken, isLastVisualTokenValid }
@@ -78,16 +62,9 @@ require('~/filtered_search/filtered_search_visual_tokens');
it('returns when there are multiple visual tokens and an incomplete visual token', () => {
tokensContainer.innerHTML = `
- <li class="js-visual-token filtered-search-token">
- <div class="name">label</div>
- <div class="value">~bug</div>
- </li>
- <li class="js-visual-token filtered-search-term">
- <div class="name">search term</div>
- </li>
- <li class="js-visual-token filtered-search-token">
- <div class="name">assignee</div>
- </li>
+ ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug')}
+ ${FilteredSearchSpecHelper.createSearchVisualTokenHTML('search term')}
+ ${FilteredSearchSpecHelper.createNameFilterVisualTokenHTML('assignee')}
`;
const { lastVisualToken, isLastVisualTokenValid }
@@ -182,5 +159,54 @@ require('~/filtered_search/filtered_search_visual_tokens');
expect(token.querySelector('.value')).toEqual(null);
});
});
+
+ describe('getLastTokenPartial', () => {
+ it('should get last token value', () => {
+ const value = '~bug';
+ tokensContainer.innerHTML = FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', value);
+
+ expect(gl.FilteredSearchVisualTokens.getLastTokenPartial()).toEqual(value);
+ });
+
+ it('should get last token name if there is no value', () => {
+ const name = 'assignee';
+ tokensContainer.innerHTML = FilteredSearchSpecHelper.createNameFilterVisualTokenHTML(name);
+
+ expect(gl.FilteredSearchVisualTokens.getLastTokenPartial()).toEqual(name);
+ });
+
+ it('should return empty when there are no tokens', () => {
+ expect(gl.FilteredSearchVisualTokens.getLastTokenPartial()).toEqual('');
+ });
+ });
+
+ describe('removeLastTokenPartial', () => {
+ it('should remove the last token value if it exists', () => {
+ tokensContainer.innerHTML = FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~"Community Contribution"');
+
+ expect(tokensContainer.querySelector('.js-visual-token .value')).not.toEqual(null);
+
+ gl.FilteredSearchVisualTokens.removeLastTokenPartial();
+
+ expect(tokensContainer.querySelector('.js-visual-token .value')).toEqual(null);
+ });
+
+ it('should remove the last token name if there is no value', () => {
+ tokensContainer.innerHTML = FilteredSearchSpecHelper.createNameFilterVisualTokenHTML('milestone');
+
+ expect(tokensContainer.querySelector('.js-visual-token .name')).not.toEqual(null);
+
+ gl.FilteredSearchVisualTokens.removeLastTokenPartial();
+
+ expect(tokensContainer.querySelector('.js-visual-token .name')).toEqual(null);
+ });
+
+ it('should not remove anything when there are no tokens', () => {
+ const html = tokensContainer.innerHTML;
+ gl.FilteredSearchVisualTokens.removeLastTokenPartial();
+
+ expect(tokensContainer.innerHTML).toEqual(html);
+ });
+ });
});
})();
diff --git a/spec/javascripts/helpers/filtered_search_spec_helper.js.es6 b/spec/javascripts/helpers/filtered_search_spec_helper.js.es6
new file mode 100644
index 00000000000..567efdd838c
--- /dev/null
+++ b/spec/javascripts/helpers/filtered_search_spec_helper.js.es6
@@ -0,0 +1,28 @@
+class FilteredSearchSpecHelper {
+ static createFilterVisualTokenHTML(name, value) {
+ return `
+ <li class="js-visual-token filtered-search-token">
+ <div class="name">${name}</div>
+ <div class="value">${value}</div>
+ </li>
+ `;
+ }
+
+ static createNameFilterVisualTokenHTML(name) {
+ return `
+ <li class="js-visual-token filtered-search-token">
+ <div class="name">${name}</div>
+ </li>
+ `;
+ }
+
+ static createSearchVisualTokenHTML(name) {
+ return `
+ <li class="js-visual-token filtered-search-term">
+ <div class="name">${name}</div>
+ </li>
+ `;
+ }
+}
+
+module.exports = FilteredSearchSpecHelper;