Skip to content
Snippets Groups Projects
Commit 91a37498 authored by Clement Ho's avatar Clement Ho Committed by Jacob Schatz
Browse files

Add button to delete filters from filtered search bar

Former-commit-id: 09f09139
parent c8ea5828
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -77,13 +77,14 @@ class FilteredSearchManager {
this.checkForEnterWrapper = this.checkForEnter.bind(this);
this.onClearSearchWrapper = this.onClearSearch.bind(this);
this.checkForBackspaceWrapper = this.checkForBackspace.bind(this);
this.removeSelectedTokenWrapper = this.removeSelectedToken.bind(this);
this.removeSelectedTokenKeydownWrapper = this.removeSelectedTokenKeydown.bind(this);
this.unselectEditTokensWrapper = this.unselectEditTokens.bind(this);
this.editTokenWrapper = this.editToken.bind(this);
this.tokenChange = this.tokenChange.bind(this);
this.addInputContainerFocusWrapper = this.addInputContainerFocus.bind(this);
this.removeInputContainerFocusWrapper = this.removeInputContainerFocus.bind(this);
this.onrecentSearchesItemSelectedWrapper = this.onrecentSearchesItemSelected.bind(this);
this.removeTokenWrapper = this.removeToken.bind(this);
 
this.filteredSearchInputForm.addEventListener('submit', this.handleFormSubmit);
this.filteredSearchInput.addEventListener('input', this.setDropdownWrapper);
Loading
Loading
@@ -96,12 +97,13 @@ class FilteredSearchManager {
this.filteredSearchInput.addEventListener('keyup', this.tokenChange);
this.filteredSearchInput.addEventListener('focus', this.addInputContainerFocusWrapper);
this.tokensContainer.addEventListener('click', FilteredSearchManager.selectToken);
this.tokensContainer.addEventListener('click', this.removeTokenWrapper);
this.tokensContainer.addEventListener('dblclick', this.editTokenWrapper);
this.clearSearchButton.addEventListener('click', this.onClearSearchWrapper);
document.addEventListener('click', gl.FilteredSearchVisualTokens.unselectTokens);
document.addEventListener('click', this.unselectEditTokensWrapper);
document.addEventListener('click', this.removeInputContainerFocusWrapper);
document.addEventListener('keydown', this.removeSelectedTokenWrapper);
document.addEventListener('keydown', this.removeSelectedTokenKeydownWrapper);
eventHub.$on('recentSearchesItemSelected', this.onrecentSearchesItemSelectedWrapper);
}
 
Loading
Loading
@@ -117,12 +119,13 @@ class FilteredSearchManager {
this.filteredSearchInput.removeEventListener('keyup', this.tokenChange);
this.filteredSearchInput.removeEventListener('focus', this.addInputContainerFocusWrapper);
this.tokensContainer.removeEventListener('click', FilteredSearchManager.selectToken);
this.tokensContainer.removeEventListener('click', this.removeTokenWrapper);
this.tokensContainer.removeEventListener('dblclick', this.editTokenWrapper);
this.clearSearchButton.removeEventListener('click', this.onClearSearchWrapper);
document.removeEventListener('click', gl.FilteredSearchVisualTokens.unselectTokens);
document.removeEventListener('click', this.unselectEditTokensWrapper);
document.removeEventListener('click', this.removeInputContainerFocusWrapper);
document.removeEventListener('keydown', this.removeSelectedTokenWrapper);
document.removeEventListener('keydown', this.removeSelectedTokenKeydownWrapper);
eventHub.$off('recentSearchesItemSelected', this.onrecentSearchesItemSelectedWrapper);
}
 
Loading
Loading
@@ -195,14 +198,28 @@ class FilteredSearchManager {
 
static selectToken(e) {
const button = e.target.closest('.selectable');
const removeButtonSelected = e.target.closest('.remove-token');
 
if (button) {
if (!removeButtonSelected && button) {
e.preventDefault();
e.stopPropagation();
gl.FilteredSearchVisualTokens.selectToken(button);
}
}
 
removeToken(e) {
const removeButtonSelected = e.target.closest('.remove-token');
if (removeButtonSelected) {
e.preventDefault();
e.stopPropagation();
const button = e.target.closest('.selectable');
gl.FilteredSearchVisualTokens.selectToken(button, true);
this.removeSelectedToken();
}
}
unselectEditTokens(e) {
const inputContainer = this.container.querySelector('.filtered-search-box');
const isElementInFilteredSearch = inputContainer && inputContainer.contains(e.target);
Loading
Loading
@@ -248,16 +265,21 @@ class FilteredSearchManager {
}
}
 
removeSelectedToken(e) {
removeSelectedTokenKeydown(e) {
// 8 = Backspace Key
// 46 = Delete Key
if (e.keyCode === 8 || e.keyCode === 46) {
gl.FilteredSearchVisualTokens.removeSelectedToken();
this.handleInputPlaceholder();
this.toggleClearSearchButton();
this.removeSelectedToken();
}
}
 
removeSelectedToken() {
gl.FilteredSearchVisualTokens.removeSelectedToken();
this.handleInputPlaceholder();
this.toggleClearSearchButton();
this.dropdownManager.updateCurrentDropdownOffset();
}
onClearSearch(e) {
e.preventDefault();
this.clearSearch();
Loading
Loading
Loading
Loading
@@ -16,11 +16,11 @@ class FilteredSearchVisualTokens {
[].forEach.call(otherTokens, t => t.classList.remove('selected'));
}
 
static selectToken(tokenButton) {
static selectToken(tokenButton, forceSelection = false) {
const selected = tokenButton.classList.contains('selected');
FilteredSearchVisualTokens.unselectTokens();
 
if (!selected) {
if (!selected || forceSelection) {
tokenButton.classList.add('selected');
}
}
Loading
Loading
@@ -38,7 +38,12 @@ class FilteredSearchVisualTokens {
return `
<div class="selectable" role="button">
<div class="name"></div>
<div class="value"></div>
<div class="value-container">
<div class="value"></div>
<div class="remove-token" role="button">
<i class="fa fa-close"></i>
</div>
</div>
</div>
`;
}
Loading
Loading
@@ -122,7 +127,8 @@ class FilteredSearchVisualTokens {
 
if (value) {
const button = lastVisualToken.querySelector('.selectable');
button.removeChild(value);
const valueContainer = lastVisualToken.querySelector('.value-container');
button.removeChild(valueContainer);
lastVisualToken.innerHTML = button.innerHTML;
} else {
lastVisualToken.closest('.tokens-container').removeChild(lastVisualToken);
Loading
Loading
Loading
Loading
@@ -104,6 +104,24 @@
padding: 2px 7px;
}
 
.value {
padding-right: 0;
}
.remove-token {
display: inline-block;
padding-left: 4px;
padding-right: 8px;
.fa-close {
color: $gl-text-color-disabled;
}
&:hover .fa-close {
color: $gl-text-color-secondary;
}
}
.name {
background-color: $filter-name-resting-color;
color: $filter-name-text-color;
Loading
Loading
@@ -112,7 +130,7 @@
text-transform: capitalize;
}
 
.value {
.value-container {
background-color: $white-normal;
color: $filter-value-text-color;
border-radius: 0 2px 2px 0;
Loading
Loading
@@ -124,7 +142,7 @@
background-color: $filter-name-selected-color;
}
 
.value {
.value-container {
background-color: $filter-value-selected-color;
}
}
Loading
Loading
---
title: Add button to delete filters from filtered search bar
merge_request:
author:
Loading
Loading
@@ -12,7 +12,7 @@ describe 'Filter issues', js: true, feature: true do
let!(:wontfix) { create(:label, project: project, title: "Won't fix") }
 
let!(:bug_label) { create(:label, project: project, title: 'bug') }
let!(:caps_sensitive_label) { create(:label, project: project, title: 'CAPS_sensitive') }
let!(:caps_sensitive_label) { create(:label, project: project, title: 'CaPs') }
let!(:milestone) { create(:milestone, title: "8", project: project, start_date: 2.days.ago) }
let!(:multiple_words_label) { create(:label, project: project, title: "Two words") }
 
Loading
Loading
Loading
Loading
@@ -26,6 +26,10 @@ describe('Filtered Search Manager', () => {
element.dispatchEvent(event);
}
 
function getVisualTokens() {
return tokensContainer.querySelectorAll('.js-visual-token');
}
beforeEach(() => {
setFixtures(`
<div class="filtered-search-box">
Loading
Loading
@@ -170,11 +174,37 @@ describe('Filtered Search Manager', () => {
});
});
 
describe('removeSelectedToken', () => {
function getVisualTokens() {
return tokensContainer.querySelectorAll('.js-visual-token');
}
describe('removeToken', () => {
it('removes token even when it is already selected', () => {
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', 'none', true),
);
tokensContainer.querySelector('.js-visual-token .remove-token').click();
expect(tokensContainer.querySelector('.js-visual-token')).toEqual(null);
});
 
describe('unselected token', () => {
beforeEach(() => {
spyOn(gl.FilteredSearchManager.prototype, 'removeSelectedToken').and.callThrough();
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', 'none'),
);
tokensContainer.querySelector('.js-visual-token .remove-token').click();
});
it('removes token when remove button is selected', () => {
expect(tokensContainer.querySelector('.js-visual-token')).toEqual(null);
});
it('calls removeSelectedToken', () => {
expect(manager.removeSelectedToken).toHaveBeenCalled();
});
});
});
describe('removeSelectedTokenKeydown', () => {
beforeEach(() => {
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', 'none', true),
Loading
Loading
@@ -224,6 +254,31 @@ describe('Filtered Search Manager', () => {
});
});
 
describe('removeSelectedToken', () => {
beforeEach(() => {
spyOn(gl.FilteredSearchVisualTokens, 'removeSelectedToken').and.callThrough();
spyOn(gl.FilteredSearchManager.prototype, 'handleInputPlaceholder').and.callThrough();
spyOn(gl.FilteredSearchManager.prototype, 'toggleClearSearchButton').and.callThrough();
manager.removeSelectedToken();
});
it('calls FilteredSearchVisualTokens.removeSelectedToken', () => {
expect(gl.FilteredSearchVisualTokens.removeSelectedToken).toHaveBeenCalled();
});
it('calls handleInputPlaceholder', () => {
expect(manager.handleInputPlaceholder).toHaveBeenCalled();
});
it('calls toggleClearSearchButton', () => {
expect(manager.toggleClearSearchButton).toHaveBeenCalled();
});
it('calls update dropdown offset', () => {
expect(manager.dropdownManager.updateDropdownOffset).toHaveBeenCalled();
});
});
describe('unselects token', () => {
beforeEach(() => {
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(`
Loading
Loading
Loading
Loading
@@ -214,8 +214,12 @@ describe('Filtered Search Visual Tokens', () => {
expect(tokenElement.querySelector('.name')).toEqual(jasmine.anything());
});
 
it('contains value container div', () => {
expect(tokenElement.querySelector('.value-container')).toEqual(jasmine.anything());
});
it('contains value div', () => {
expect(tokenElement.querySelector('.value')).toEqual(jasmine.anything());
expect(tokenElement.querySelector('.value-container .value')).toEqual(jasmine.anything());
});
 
it('contains selectable class', () => {
Loading
Loading
@@ -225,6 +229,16 @@ describe('Filtered Search Visual Tokens', () => {
it('contains button role', () => {
expect(tokenElement.getAttribute('role')).toEqual('button');
});
describe('remove token', () => {
it('contains remove-token button', () => {
expect(tokenElement.querySelector('.value-container .remove-token')).toEqual(jasmine.anything());
});
it('contains fa-close icon', () => {
expect(tokenElement.querySelector('.remove-token .fa-close')).toEqual(jasmine.anything());
});
});
});
 
describe('addVisualTokenElement', () => {
Loading
Loading
Loading
Loading
@@ -10,7 +10,12 @@ class FilteredSearchSpecHelper {
li.innerHTML = `
<div class="selectable ${isSelected ? 'selected' : ''}" role="button">
<div class="name">${name}</div>
<div class="value">${value}</div>
<div class="value-container">
<div class="value">${value}</div>
<div class="remove-token" role="button">
<i class="fa fa-close"></i>
</div>
</div>
</div>
`;
 
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment