summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlfredo Sumaran <alfredo@gitlab.com>2017-04-17 02:01:11 -0500
committerAlfredo Sumaran <alfredo@gitlab.com>2017-04-17 02:01:11 -0500
commit08a09c6b62b7052b657b0e56fcaa0acb53991a10 (patch)
treec4c668894a1b4e85e175ec42c8a357bd5d53623f
parenteeaeb2752a589c9046659d58d4a3f6be8030b699 (diff)
downloadgitlab-ce-remove-iife-filtered-search-bundle.tar.gz
Remove IIFEs in filtered_search_bundle.jsremove-iife-filtered-search-bundle
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_hint.js122
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_non_user.js76
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_user.js106
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_utils.js298
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown.js194
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js298
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js776
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_token_keys.js168
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_tokenizer.js100
-rw-r--r--spec/javascripts/filtered_search/dropdown_user_spec.js94
-rw-r--r--spec/javascripts/filtered_search/dropdown_utils_spec.js446
-rw-r--r--spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js144
-rw-r--r--spec/javascripts/filtered_search/filtered_search_manager_spec.js394
-rw-r--r--spec/javascripts/filtered_search/filtered_search_token_keys_spec.js208
-rw-r--r--spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js256
15 files changed, 1825 insertions, 1855 deletions
diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js
index 381c40c03d8..3e7a892756c 100644
--- a/app/assets/javascripts/filtered_search/dropdown_hint.js
+++ b/app/assets/javascripts/filtered_search/dropdown_hint.js
@@ -2,82 +2,80 @@ import Filter from '~/droplab/plugins/filter';
require('./filtered_search_dropdown');
-(() => {
- class DropdownHint extends gl.FilteredSearchDropdown {
- constructor(droplab, dropdown, input, filter) {
- super(droplab, dropdown, input, filter);
- this.config = {
- Filter: {
- template: 'hint',
- filterFunction: gl.DropdownUtils.filterHint.bind(null, input),
- },
- };
- }
-
- itemClicked(e) {
- const { selected } = e.detail;
+class DropdownHint extends gl.FilteredSearchDropdown {
+ constructor(droplab, dropdown, input, filter) {
+ super(droplab, dropdown, input, filter);
+ this.config = {
+ Filter: {
+ template: 'hint',
+ filterFunction: gl.DropdownUtils.filterHint.bind(null, input),
+ },
+ };
+ }
- if (selected.tagName === 'LI') {
- if (selected.hasAttribute('data-value')) {
- this.dismissDropdown();
- } else if (selected.getAttribute('data-action') === 'submit') {
- this.dismissDropdown();
- this.dispatchFormSubmitEvent();
- } else {
- const token = selected.querySelector('.js-filter-hint').innerText.trim();
- const tag = selected.querySelector('.js-filter-tag').innerText.trim();
+ itemClicked(e) {
+ const { selected } = e.detail;
- if (tag.length) {
- // Get previous input values in the input field and convert them into visual tokens
- const previousInputValues = this.input.value.split(' ');
- const searchTerms = [];
+ if (selected.tagName === 'LI') {
+ if (selected.hasAttribute('data-value')) {
+ this.dismissDropdown();
+ } else if (selected.getAttribute('data-action') === 'submit') {
+ this.dismissDropdown();
+ this.dispatchFormSubmitEvent();
+ } else {
+ const token = selected.querySelector('.js-filter-hint').innerText.trim();
+ const tag = selected.querySelector('.js-filter-tag').innerText.trim();
- previousInputValues.forEach((value, index) => {
- searchTerms.push(value);
+ if (tag.length) {
+ // Get previous input values in the input field and convert them into visual tokens
+ const previousInputValues = this.input.value.split(' ');
+ const searchTerms = [];
- if (index === previousInputValues.length - 1
- && token.indexOf(value.toLowerCase()) !== -1) {
- searchTerms.pop();
- }
- });
+ previousInputValues.forEach((value, index) => {
+ searchTerms.push(value);
- if (searchTerms.length > 0) {
- gl.FilteredSearchVisualTokens.addSearchVisualToken(searchTerms.join(' '));
+ if (index === previousInputValues.length - 1
+ && token.indexOf(value.toLowerCase()) !== -1) {
+ searchTerms.pop();
}
+ });
- gl.FilteredSearchDropdownManager.addWordToInput(token.replace(':', ''), '', false, this.container);
+ if (searchTerms.length > 0) {
+ gl.FilteredSearchVisualTokens.addSearchVisualToken(searchTerms.join(' '));
}
- this.dismissDropdown();
- this.dispatchInputEvent();
+
+ gl.FilteredSearchDropdownManager.addWordToInput(token.replace(':', ''), '', false, this.container);
}
+ this.dismissDropdown();
+ this.dispatchInputEvent();
}
}
+ }
- renderContent() {
- const dropdownData = [];
+ renderContent() {
+ const dropdownData = [];
- [].forEach.call(this.input.closest('.filtered-search-box-input-container').querySelectorAll('.dropdown-menu'), (dropdownMenu) => {
- const { icon, hint, tag, type } = dropdownMenu.dataset;
- if (icon && hint && tag) {
- dropdownData.push(
- Object.assign({
- icon: `fa-${icon}`,
- hint,
- tag: `&lt;${tag}&gt;`,
- }, type && { type }),
- );
- }
- });
+ [].forEach.call(this.input.closest('.filtered-search-box-input-container').querySelectorAll('.dropdown-menu'), (dropdownMenu) => {
+ const { icon, hint, tag, type } = dropdownMenu.dataset;
+ if (icon && hint && tag) {
+ dropdownData.push(
+ Object.assign({
+ icon: `fa-${icon}`,
+ hint,
+ tag: `&lt;${tag}&gt;`,
+ }, type && { type }),
+ );
+ }
+ });
- this.droplab.changeHookList(this.hookId, this.dropdown, [Filter], this.config);
- this.droplab.setData(this.hookId, dropdownData);
- }
+ this.droplab.changeHookList(this.hookId, this.dropdown, [Filter], this.config);
+ this.droplab.setData(this.hookId, dropdownData);
+ }
- init() {
- this.droplab.addHook(this.input, this.dropdown, [Filter], this.config).init();
- }
+ init() {
+ this.droplab.addHook(this.input, this.dropdown, [Filter], this.config).init();
}
+}
- window.gl = window.gl || {};
- gl.DropdownHint = DropdownHint;
-})();
+window.gl = window.gl || {};
+gl.DropdownHint = DropdownHint;
diff --git a/app/assets/javascripts/filtered_search/dropdown_non_user.js b/app/assets/javascripts/filtered_search/dropdown_non_user.js
index 6296965b911..982dc4b61be 100644
--- a/app/assets/javascripts/filtered_search/dropdown_non_user.js
+++ b/app/assets/javascripts/filtered_search/dropdown_non_user.js
@@ -5,48 +5,46 @@ import Filter from '~/droplab/plugins/filter';
require('./filtered_search_dropdown');
-(() => {
- class DropdownNonUser extends gl.FilteredSearchDropdown {
- constructor(droplab, dropdown, input, filter, endpoint, symbol) {
- super(droplab, dropdown, input, filter);
- this.symbol = symbol;
- this.config = {
- Ajax: {
- endpoint,
- method: 'setData',
- loadingTemplate: this.loadingTemplate,
- onError() {
- /* eslint-disable no-new */
- new Flash('An error occured fetching the dropdown data.');
- /* eslint-enable no-new */
- },
+class DropdownNonUser extends gl.FilteredSearchDropdown {
+ constructor(droplab, dropdown, input, filter, endpoint, symbol) {
+ super(droplab, dropdown, input, filter);
+ this.symbol = symbol;
+ this.config = {
+ Ajax: {
+ endpoint,
+ method: 'setData',
+ loadingTemplate: this.loadingTemplate,
+ onError() {
+ /* eslint-disable no-new */
+ new Flash('An error occured fetching the dropdown data.');
+ /* eslint-enable no-new */
},
- Filter: {
- filterFunction: gl.DropdownUtils.filterWithSymbol.bind(null, this.symbol, input),
- template: 'title',
- },
- };
- }
+ },
+ Filter: {
+ filterFunction: gl.DropdownUtils.filterWithSymbol.bind(null, this.symbol, input),
+ template: 'title',
+ },
+ };
+ }
- itemClicked(e) {
- super.itemClicked(e, (selected) => {
- const title = selected.querySelector('.js-data-value').innerText.trim();
- return `${this.symbol}${gl.DropdownUtils.getEscapedText(title)}`;
- });
- }
+ itemClicked(e) {
+ super.itemClicked(e, (selected) => {
+ const title = selected.querySelector('.js-data-value').innerText.trim();
+ return `${this.symbol}${gl.DropdownUtils.getEscapedText(title)}`;
+ });
+ }
- renderContent(forceShowList = false) {
- this.droplab
- .changeHookList(this.hookId, this.dropdown, [Ajax, Filter], this.config);
- super.renderContent(forceShowList);
- }
+ renderContent(forceShowList = false) {
+ this.droplab
+ .changeHookList(this.hookId, this.dropdown, [Ajax, Filter], this.config);
+ super.renderContent(forceShowList);
+ }
- init() {
- this.droplab
- .addHook(this.input, this.dropdown, [Ajax, Filter], this.config).init();
- }
+ init() {
+ this.droplab
+ .addHook(this.input, this.dropdown, [Ajax, Filter], this.config).init();
}
+}
- window.gl = window.gl || {};
- gl.DropdownNonUser = DropdownNonUser;
-})();
+window.gl = window.gl || {};
+gl.DropdownNonUser = DropdownNonUser;
diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js b/app/assets/javascripts/filtered_search/dropdown_user.js
index 38b5d315bcf..74cec3d75fe 100644
--- a/app/assets/javascripts/filtered_search/dropdown_user.js
+++ b/app/assets/javascripts/filtered_search/dropdown_user.js
@@ -4,69 +4,67 @@ import AjaxFilter from '~/droplab/plugins/ajax_filter';
require('./filtered_search_dropdown');
-(() => {
- class DropdownUser extends gl.FilteredSearchDropdown {
- constructor(droplab, dropdown, input, filter) {
- super(droplab, dropdown, input, filter);
- this.config = {
- AjaxFilter: {
- endpoint: `${gon.relative_url_root || ''}/autocomplete/users.json`,
- searchKey: 'search',
- params: {
- per_page: 20,
- active: true,
- project_id: this.getProjectId(),
- current_user: true,
- },
- searchValueFunction: this.getSearchInput.bind(this),
- loadingTemplate: this.loadingTemplate,
- onError() {
- /* eslint-disable no-new */
- new Flash('An error occured fetching the dropdown data.');
- /* eslint-enable no-new */
- },
+class DropdownUser extends gl.FilteredSearchDropdown {
+ constructor(droplab, dropdown, input, filter) {
+ super(droplab, dropdown, input, filter);
+ this.config = {
+ AjaxFilter: {
+ endpoint: `${gon.relative_url_root || ''}/autocomplete/users.json`,
+ searchKey: 'search',
+ params: {
+ per_page: 20,
+ active: true,
+ project_id: this.getProjectId(),
+ current_user: true,
},
- };
- }
-
- itemClicked(e) {
- super.itemClicked(e,
- selected => selected.querySelector('.dropdown-light-content').innerText.trim());
- }
-
- renderContent(forceShowList = false) {
- this.droplab.changeHookList(this.hookId, this.dropdown, [AjaxFilter], this.config);
- super.renderContent(forceShowList);
- }
+ searchValueFunction: this.getSearchInput.bind(this),
+ loadingTemplate: this.loadingTemplate,
+ onError() {
+ /* eslint-disable no-new */
+ new Flash('An error occured fetching the dropdown data.');
+ /* eslint-enable no-new */
+ },
+ },
+ };
+ }
- getProjectId() {
- return this.input.getAttribute('data-project-id');
- }
+ itemClicked(e) {
+ super.itemClicked(e,
+ selected => selected.querySelector('.dropdown-light-content').innerText.trim());
+ }
- getSearchInput() {
- const query = gl.DropdownUtils.getSearchInput(this.input);
- const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query);
+ renderContent(forceShowList = false) {
+ this.droplab.changeHookList(this.hookId, this.dropdown, [AjaxFilter], this.config);
+ super.renderContent(forceShowList);
+ }
- let value = lastToken || '';
+ getProjectId() {
+ return this.input.getAttribute('data-project-id');
+ }
- if (value[0] === '@') {
- value = value.slice(1);
- }
+ getSearchInput() {
+ const query = gl.DropdownUtils.getSearchInput(this.input);
+ const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query);
- // Removes the first character if it is a quotation so that we can search
- // with multiple words
- if (value[0] === '"' || value[0] === '\'') {
- value = value.slice(1);
- }
+ let value = lastToken || '';
- return value;
+ if (value[0] === '@') {
+ value = value.slice(1);
}
- init() {
- this.droplab.addHook(this.input, this.dropdown, [AjaxFilter], this.config).init();
+ // Removes the first character if it is a quotation so that we can search
+ // with multiple words
+ if (value[0] === '"' || value[0] === '\'') {
+ value = value.slice(1);
}
+
+ return value;
+ }
+
+ init() {
+ this.droplab.addHook(this.input, this.dropdown, [AjaxFilter], this.config).init();
}
+}
- window.gl = window.gl || {};
- gl.DropdownUser = DropdownUser;
-})();
+window.gl = window.gl || {};
+gl.DropdownUser = DropdownUser;
diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js b/app/assets/javascripts/filtered_search/dropdown_utils.js
index 6c5c20447f7..bc7c1dffece 100644
--- a/app/assets/javascripts/filtered_search/dropdown_utils.js
+++ b/app/assets/javascripts/filtered_search/dropdown_utils.js
@@ -1,183 +1,181 @@
import FilteredSearchContainer from './container';
-(() => {
- class DropdownUtils {
- static getEscapedText(text) {
- let escapedText = text;
- const hasSpace = text.indexOf(' ') !== -1;
- const hasDoubleQuote = text.indexOf('"') !== -1;
-
- // Encapsulate value with quotes if it has spaces
- // Known side effect: values's with both single and double quotes
- // won't escape properly
- if (hasSpace) {
- if (hasDoubleQuote) {
- escapedText = `'${text}'`;
- } else {
- // Encapsulate singleQuotes or if it hasSpace
- escapedText = `"${text}"`;
- }
+class DropdownUtils {
+ static getEscapedText(text) {
+ let escapedText = text;
+ const hasSpace = text.indexOf(' ') !== -1;
+ const hasDoubleQuote = text.indexOf('"') !== -1;
+
+ // Encapsulate value with quotes if it has spaces
+ // Known side effect: values's with both single and double quotes
+ // won't escape properly
+ if (hasSpace) {
+ if (hasDoubleQuote) {
+ escapedText = `'${text}'`;
+ } else {
+ // Encapsulate singleQuotes or if it hasSpace
+ escapedText = `"${text}"`;
}
-
- return escapedText;
}
- static filterWithSymbol(filterSymbol, input, item) {
- const updatedItem = item;
- const searchInput = gl.DropdownUtils.getSearchInput(input);
+ return escapedText;
+ }
+
+ static filterWithSymbol(filterSymbol, input, item) {
+ const updatedItem = item;
+ const searchInput = gl.DropdownUtils.getSearchInput(input);
- const title = updatedItem.title.toLowerCase();
- let value = searchInput.toLowerCase();
- let symbol = '';
+ const title = updatedItem.title.toLowerCase();
+ let value = searchInput.toLowerCase();
+ let symbol = '';
- // Remove the symbol for filter
- if (value[0] === filterSymbol) {
- symbol = value[0];
- value = value.slice(1);
- }
+ // Remove the symbol for filter
+ if (value[0] === filterSymbol) {
+ symbol = value[0];
+ value = value.slice(1);
+ }
- // Removes the first character if it is a quotation so that we can search
- // with multiple words
- if ((value[0] === '"' || value[0] === '\'') && title.indexOf(' ') !== -1) {
- value = value.slice(1);
- }
+ // Removes the first character if it is a quotation so that we can search
+ // with multiple words
+ if ((value[0] === '"' || value[0] === '\'') && title.indexOf(' ') !== -1) {
+ value = value.slice(1);
+ }
+
+ // Eg. filterSymbol = ~ for labels
+ const matchWithoutSymbol = symbol === filterSymbol && title.indexOf(value) !== -1;
+ const match = title.indexOf(`${symbol}${value}`) !== -1;
- // Eg. filterSymbol = ~ for labels
- const matchWithoutSymbol = symbol === filterSymbol && title.indexOf(value) !== -1;
- const match = title.indexOf(`${symbol}${value}`) !== -1;
+ updatedItem.droplab_hidden = !match && !matchWithoutSymbol;
- updatedItem.droplab_hidden = !match && !matchWithoutSymbol;
+ return updatedItem;
+ }
- return updatedItem;
+ static filterHint(input, item) {
+ const updatedItem = item;
+ const searchInput = gl.DropdownUtils.getSearchQuery(input);
+ const { lastToken, tokens } = gl.FilteredSearchTokenizer.processTokens(searchInput);
+ const lastKey = lastToken.key || lastToken || '';
+ const allowMultiple = item.type === 'array';
+ const itemInExistingTokens = tokens.some(t => t.key === item.hint);
+
+ if (!allowMultiple && itemInExistingTokens) {
+ updatedItem.droplab_hidden = true;
+ } else if (!lastKey || searchInput.split('').last() === ' ') {
+ updatedItem.droplab_hidden = false;
+ } else if (lastKey) {
+ const split = lastKey.split(':');
+ const tokenName = split[0].split(' ').last();
+
+ const match = updatedItem.hint.indexOf(tokenName.toLowerCase()) === -1;
+ updatedItem.droplab_hidden = tokenName ? match : false;
}
- static filterHint(input, item) {
- const updatedItem = item;
- const searchInput = gl.DropdownUtils.getSearchQuery(input);
- const { lastToken, tokens } = gl.FilteredSearchTokenizer.processTokens(searchInput);
- const lastKey = lastToken.key || lastToken || '';
- const allowMultiple = item.type === 'array';
- const itemInExistingTokens = tokens.some(t => t.key === item.hint);
-
- if (!allowMultiple && itemInExistingTokens) {
- updatedItem.droplab_hidden = true;
- } else if (!lastKey || searchInput.split('').last() === ' ') {
- updatedItem.droplab_hidden = false;
- } else if (lastKey) {
- const split = lastKey.split(':');
- const tokenName = split[0].split(' ').last();
-
- const match = updatedItem.hint.indexOf(tokenName.toLowerCase()) === -1;
- updatedItem.droplab_hidden = tokenName ? match : false;
- }
+ return updatedItem;
+ }
+
+ static setDataValueIfSelected(filter, selected) {
+ const dataValue = selected.getAttribute('data-value');
- return updatedItem;
+ if (dataValue) {
+ gl.FilteredSearchDropdownManager.addWordToInput(filter, dataValue, true);
}
- static setDataValueIfSelected(filter, selected) {
- const dataValue = selected.getAttribute('data-value');
+ // Return boolean based on whether it was set
+ return dataValue !== null;
+ }
- if (dataValue) {
- gl.FilteredSearchDropdownManager.addWordToInput(filter, dataValue, true);
- }
+ // Determines the full search query (visual tokens + input)
+ static getSearchQuery(untilInput = false) {
+ const container = FilteredSearchContainer.container;
+ const tokens = [].slice.call(container.querySelectorAll('.tokens-container li'));
+ const values = [];
- // Return boolean based on whether it was set
- return dataValue !== null;
+ if (untilInput) {
+ const inputIndex = _.findIndex(tokens, t => t.classList.contains('input-token'));
+ // Add one to include input-token to the tokens array
+ tokens.splice(inputIndex + 1);
}
- // Determines the full search query (visual tokens + input)
- static getSearchQuery(untilInput = false) {
- const container = FilteredSearchContainer.container;
- const tokens = [].slice.call(container.querySelectorAll('.tokens-container li'));
- const values = [];
+ tokens.forEach((token) => {
+ if (token.classList.contains('js-visual-token')) {
+ const name = token.querySelector('.name');
+ const value = token.querySelector('.value');
+ const symbol = value && value.dataset.symbol ? value.dataset.symbol : '';
+ let valueText = '';
- if (untilInput) {
- const inputIndex = _.findIndex(tokens, t => t.classList.contains('input-token'));
- // Add one to include input-token to the tokens array
- tokens.splice(inputIndex + 1);
- }
+ if (value && value.innerText) {
+ valueText = value.innerText;
+ }
- tokens.forEach((token) => {
- if (token.classList.contains('js-visual-token')) {
- const name = token.querySelector('.name');
- const value = token.querySelector('.value');
- const symbol = value && value.dataset.symbol ? value.dataset.symbol : '';
- let valueText = '';
-
- if (value && value.innerText) {
- valueText = value.innerText;
- }
-
- if (token.className.indexOf('filtered-search-token') !== -1) {
- values.push(`${name.innerText.toLowerCase()}:${symbol}${valueText}`);
- } else {
- values.push(name.innerText);
- }
- } else if (token.classList.contains('input-token')) {
- const { isLastVisualTokenValid } =
- gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
-
- const input = FilteredSearchContainer.container.querySelector('.filtered-search');
- const inputValue = input && input.value;
-
- if (isLastVisualTokenValid) {
- values.push(inputValue);
- } else {
- const previous = values.pop();
- values.push(`${previous}${inputValue}`);
- }
+ if (token.className.indexOf('filtered-search-token') !== -1) {
+ values.push(`${name.innerText.toLowerCase()}:${symbol}${valueText}`);
+ } else {
+ values.push(name.innerText);
}
- });
+ } else if (token.classList.contains('input-token')) {
+ const { isLastVisualTokenValid } =
+ gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
- return values
- .map(value => value.trim())
- .join(' ');
- }
+ const input = FilteredSearchContainer.container.querySelector('.filtered-search');
+ const inputValue = input && input.value;
- static getSearchInput(filteredSearchInput) {
- const inputValue = filteredSearchInput.value;
- const { right } = gl.DropdownUtils.getInputSelectionPosition(filteredSearchInput);
+ if (isLastVisualTokenValid) {
+ values.push(inputValue);
+ } else {
+ const previous = values.pop();
+ values.push(`${previous}${inputValue}`);
+ }
+ }
+ });
- return inputValue.slice(0, right);
- }
+ return values
+ .map(value => value.trim())
+ .join(' ');
+ }
- static getInputSelectionPosition(input) {
- const selectionStart = input.selectionStart;
- let inputValue = input.value;
- // Replace all spaces inside quote marks with underscores
- // (will continue to match entire string until an end quote is found if any)
- // This helps with matching the beginning & end of a token:key
- inputValue = inputValue.replace(/(('[^']*'{0,1})|("[^"]*"{0,1})|:\s+)/g, str => str.replace(/\s/g, '_'));
-
- // Get the right position for the word selected
- // Regex matches first space
- let right = inputValue.slice(selectionStart).search(/\s/);
-
- if (right >= 0) {
- right += selectionStart;
- } else if (right < 0) {
- right = inputValue.length;
- }
+ static getSearchInput(filteredSearchInput) {
+ const inputValue = filteredSearchInput.value;
+ const { right } = gl.DropdownUtils.getInputSelectionPosition(filteredSearchInput);
- // Get the left position for the word selected
- // Regex matches last non-whitespace character
- let left = inputValue.slice(0, right).search(/\S+$/);
+ return inputValue.slice(0, right);
+ }
- if (selectionStart === 0) {
- left = 0;
- } else if (selectionStart === inputValue.length && left < 0) {
- left = inputValue.length;
- } else if (left < 0) {
- left = selectionStart;
- }
+ static getInputSelectionPosition(input) {
+ const selectionStart = input.selectionStart;
+ let inputValue = input.value;
+ // Replace all spaces inside quote marks with underscores
+ // (will continue to match entire string until an end quote is found if any)
+ // This helps with matching the beginning & end of a token:key
+ inputValue = inputValue.replace(/(('[^']*'{0,1})|("[^"]*"{0,1})|:\s+)/g, str => str.replace(/\s/g, '_'));
+
+ // Get the right position for the word selected
+ // Regex matches first space
+ let right = inputValue.slice(selectionStart).search(/\s/);
+
+ if (right >= 0) {
+ right += selectionStart;
+ } else if (right < 0) {
+ right = inputValue.length;
+ }
+
+ // Get the left position for the word selected
+ // Regex matches last non-whitespace character
+ let left = inputValue.slice(0, right).search(/\S+$/);
- return {
- left,
- right,
- };
+ if (selectionStart === 0) {
+ left = 0;
+ } else if (selectionStart === inputValue.length && left < 0) {
+ left = inputValue.length;
+ } else if (left < 0) {
+ left = selectionStart;
}
+
+ return {
+ left,
+ right,
+ };
}
+}
- window.gl = window.gl || {};
- gl.DropdownUtils = DropdownUtils;
-})();
+window.gl = window.gl || {};
+gl.DropdownUtils = DropdownUtils;
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
index d58eeeebf81..4209ca0d6e2 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
@@ -1,124 +1,122 @@
-(() => {
- const DATA_DROPDOWN_TRIGGER = 'data-dropdown-trigger';
-
- class FilteredSearchDropdown {
- constructor(droplab, dropdown, input, filter) {
- this.droplab = droplab;
- this.hookId = input && input.id;
- this.input = input;
- this.filter = filter;
- this.dropdown = dropdown;
- this.loadingTemplate = `<div class="filter-dropdown-loading">
- <i class="fa fa-spinner fa-spin"></i>
- </div>`;
- this.bindEvents();
- }
-
- bindEvents() {
- this.itemClickedWrapper = this.itemClicked.bind(this);
- this.dropdown.addEventListener('click.dl', this.itemClickedWrapper);
- }
+const DATA_DROPDOWN_TRIGGER = 'data-dropdown-trigger';
+
+class FilteredSearchDropdown {
+ constructor(droplab, dropdown, input, filter) {
+ this.droplab = droplab;
+ this.hookId = input && input.id;
+ this.input = input;
+ this.filter = filter;
+ this.dropdown = dropdown;
+ this.loadingTemplate = `<div class="filter-dropdown-loading">
+ <i class="fa fa-spinner fa-spin"></i>
+ </div>`;
+ this.bindEvents();
+ }
- unbindEvents() {
- this.dropdown.removeEventListener('click.dl', this.itemClickedWrapper);
- }
+ bindEvents() {
+ this.itemClickedWrapper = this.itemClicked.bind(this);
+ this.dropdown.addEventListener('click.dl', this.itemClickedWrapper);
+ }
- getCurrentHook() {
- return this.droplab.hooks.filter(h => h.id === this.hookId)[0] || null;
- }
+ unbindEvents() {
+ this.dropdown.removeEventListener('click.dl', this.itemClickedWrapper);
+ }
- itemClicked(e, getValueFunction) {
- const { selected } = e.detail;
+ getCurrentHook() {
+ return this.droplab.hooks.filter(h => h.id === this.hookId)[0] || null;
+ }
- if (selected.tagName === 'LI' && selected.innerHTML) {
- const dataValueSet = gl.DropdownUtils.setDataValueIfSelected(this.filter, selected);
+ itemClicked(e, getValueFunction) {
+ const { selected } = e.detail;
- if (!dataValueSet) {
- const value = getValueFunction(selected);
- gl.FilteredSearchDropdownManager.addWordToInput(this.filter, value, true);
- }
+ if (selected.tagName === 'LI' && selected.innerHTML) {
+ const dataValueSet = gl.DropdownUtils.setDataValueIfSelected(this.filter, selected);
- this.resetFilters();
- this.dismissDropdown();
- this.dispatchInputEvent();
+ if (!dataValueSet) {
+ const value = getValueFunction(selected);
+ gl.FilteredSearchDropdownManager.addWordToInput(this.filter, value, true);
}
- }
- setAsDropdown() {
- this.input.setAttribute(DATA_DROPDOWN_TRIGGER, `#${this.dropdown.id}`);
+ this.resetFilters();
+ this.dismissDropdown();
+ this.dispatchInputEvent();
}
+ }
- setOffset(offset = 0) {
- if (window.innerWidth > 480) {
- this.dropdown.style.left = `${offset}px`;
- } else {
- this.dropdown.style.left = '0px';
- }
+ setAsDropdown() {
+ this.input.setAttribute(DATA_DROPDOWN_TRIGGER, `#${this.dropdown.id}`);
+ }
+
+ setOffset(offset = 0) {
+ if (window.innerWidth > 480) {
+ this.dropdown.style.left = `${offset}px`;
+ } else {
+ this.dropdown.style.left = '0px';
}
+ }
- renderContent(forceShowList = false) {
- const currentHook = this.getCurrentHook();
- if (forceShowList && currentHook && currentHook.list.hidden) {
- currentHook.list.show();
- }
+ renderContent(forceShowList = false) {
+ const currentHook = this.getCurrentHook();
+ if (forceShowList && currentHook && currentHook.list.hidden) {
+ currentHook.list.show();
}
+ }
- render(forceRenderContent = false, forceShowList = false) {
- this.setAsDropdown();
+ render(forceRenderContent = false, forceShowList = false) {
+ this.setAsDropdown();
- const currentHook = this.getCurrentHook();
- const firstTimeInitialized = currentHook === null;
+ const currentHook = this.getCurrentHook();
+ const firstTimeInitialized = currentHook === null;
- if (firstTimeInitialized || forceRenderContent) {
- this.renderContent(forceShowList);
- } else if (currentHook.list.list.id !== this.dropdown.id) {
- this.renderContent(forceShowList);
- }
+ if (firstTimeInitialized || forceRenderContent) {
+ this.renderContent(forceShowList);
+ } else if (currentHook.list.list.id !== this.dropdown.id) {
+ this.renderContent(forceShowList);
}
+ }
- dismissDropdown() {
- // Focusing on the input will dismiss dropdown
- // (default droplab functionality)
- this.input.focus();
- }
+ dismissDropdown() {
+ // Focusing on the input will dismiss dropdown
+ // (default droplab functionality)
+ this.input.focus();
+ }
- dispatchInputEvent() {
- // Propogate input change to FilteredSearchDropdownManager
- // so that it can determine which dropdowns to open
- this.input.dispatchEvent(new CustomEvent('input', {
- bubbles: true,
- cancelable: true,
- }));
- }
+ dispatchInputEvent() {
+ // Propogate input change to FilteredSearchDropdownManager
+ // so that it can determine which dropdowns to open
+ this.input.dispatchEvent(new CustomEvent('input', {
+ bubbles: true,
+ cancelable: true,
+ }));
+ }
- dispatchFormSubmitEvent() {
- // dispatchEvent() is necessary as form.submit() does not
- // trigger event handlers
- this.input.form.dispatchEvent(new Event('submit'));
- }
+ dispatchFormSubmitEvent() {
+ // dispatchEvent() is necessary as form.submit() does not
+ // trigger event handlers
+ this.input.form.dispatchEvent(new Event('submit'));
+ }
- hideDropdown() {
- const currentHook = this.getCurrentHook();
- if (currentHook) {
- currentHook.list.hide();
- }
+ hideDropdown() {
+ const currentHook = this.getCurrentHook();
+ if (currentHook) {
+ currentHook.list.hide();
}
+ }
- resetFilters() {
- const hook = this.getCurrentHook();
-
- if (hook) {
- const data = hook.list.data || [];
- const results = data.map((o) => {
- const updated = o;
- updated.droplab_hidden = false;
- return updated;
- });
- hook.list.render(results);
- }
+ resetFilters() {
+ const hook = this.getCurrentHook();
+
+ if (hook) {
+ const data = hook.list.data || [];
+ const results = data.map((o) => {
+ const updated = o;
+ updated.droplab_hidden = false;
+ return updated;
+ });
+ hook.list.render(results);
}
}
+}
- window.gl = window.gl || {};
- gl.FilteredSearchDropdown = FilteredSearchDropdown;
-})();
+window.gl = window.gl || {};
+gl.FilteredSearchDropdown = FilteredSearchDropdown;
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
index ec481b9ef97..49a6cd1ac77 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
@@ -1,191 +1,189 @@
import DropLab from '~/droplab/drop_lab';
import FilteredSearchContainer from './container';
-(() => {
- class FilteredSearchDropdownManager {
- constructor(baseEndpoint = '', page) {
- this.container = FilteredSearchContainer.container;
- this.baseEndpoint = baseEndpoint.replace(/\/$/, '');
- this.tokenizer = gl.FilteredSearchTokenizer;
- this.filteredSearchTokenKeys = gl.FilteredSearchTokenKeys;
- this.filteredSearchInput = this.container.querySelector('.filtered-search');
- this.page = page;
-
- this.setupMapping();
-
- this.cleanupWrapper = this.cleanup.bind(this);
- document.addEventListener('beforeunload', this.cleanupWrapper);
+class FilteredSearchDropdownManager {
+ constructor(baseEndpoint = '', page) {
+ this.container = FilteredSearchContainer.container;
+ this.baseEndpoint = baseEndpoint.replace(/\/$/, '');
+ this.tokenizer = gl.FilteredSearchTokenizer;
+ this.filteredSearchTokenKeys = gl.FilteredSearchTokenKeys;
+ this.filteredSearchInput = this.container.querySelector('.filtered-search');
+ this.page = page;
+
+ this.setupMapping();
+
+ this.cleanupWrapper = this.cleanup.bind(this);
+ document.addEventListener('beforeunload', this.cleanupWrapper);
+ }
+
+ cleanup() {
+ if (this.droplab) {
+ this.droplab.destroy();
+ this.droplab = null;
}
- cleanup() {
- if (this.droplab) {
- this.droplab.destroy();
- this.droplab = null;
- }
+ this.setupMapping();
- this.setupMapping();
+ document.removeEventListener('beforeunload', this.cleanupWrapper);
+ }
- document.removeEventListener('beforeunload', this.cleanupWrapper);
- }
+ setupMapping() {
+ this.mapping = {
+ author: {
+ reference: null,
+ gl: 'DropdownUser',
+ element: this.container.querySelector('#js-dropdown-author'),
+ },
+ assignee: {
+ reference: null,
+ gl: 'DropdownUser',
+ element: this.container.querySelector('#js-dropdown-assignee'),
+ },
+ milestone: {
+ reference: null,
+ gl: 'DropdownNonUser',
+ extraArguments: [`${this.baseEndpoint}/milestones.json`, '%'],
+ element: this.container.querySelector('#js-dropdown-milestone'),
+ },
+ label: {
+ reference: null,
+ gl: 'DropdownNonUser',
+ extraArguments: [`${this.baseEndpoint}/labels.json`, '~'],
+ element: this.container.querySelector('#js-dropdown-label'),
+ },
+ hint: {
+ reference: null,
+ gl: 'DropdownHint',
+ element: this.container.querySelector('#js-dropdown-hint'),
+ },
+ };
+ }
- setupMapping() {
- this.mapping = {
- author: {
- reference: null,
- gl: 'DropdownUser',
- element: this.container.querySelector('#js-dropdown-author'),
- },
- assignee: {
- reference: null,
- gl: 'DropdownUser',
- element: this.container.querySelector('#js-dropdown-assignee'),
- },
- milestone: {
- reference: null,
- gl: 'DropdownNonUser',
- extraArguments: [`${this.baseEndpoint}/milestones.json`, '%'],
- element: this.container.querySelector('#js-dropdown-milestone'),
- },
- label: {
- reference: null,
- gl: 'DropdownNonUser',
- extraArguments: [`${this.baseEndpoint}/labels.json`, '~'],
- element: this.container.querySelector('#js-dropdown-label'),
- },
- hint: {
- reference: null,
- gl: 'DropdownHint',
- element: this.container.querySelector('#js-dropdown-hint'),
- },
- };
+ static addWordToInput(tokenName, tokenValue = '', clicked = false) {
+ const input = FilteredSearchContainer.container.querySelector('.filtered-search');
+
+ gl.FilteredSearchVisualTokens.addFilterVisualToken(tokenName, tokenValue);
+ input.value = '';
+
+ if (clicked) {
+ gl.FilteredSearchVisualTokens.moveInputToTheRight();
}
+ }
- static addWordToInput(tokenName, tokenValue = '', clicked = false) {
- const input = FilteredSearchContainer.container.querySelector('.filtered-search');
+ updateCurrentDropdownOffset() {
+ this.updateDropdownOffset(this.currentDropdown);
+ }
- gl.FilteredSearchVisualTokens.addFilterVisualToken(tokenName, tokenValue);
- input.value = '';
+ updateDropdownOffset(key) {
+ // Always align dropdown with the input field
+ let offset = this.filteredSearchInput.getBoundingClientRect().left - this.container.querySelector('.scroll-container').getBoundingClientRect().left;
- if (clicked) {
- gl.FilteredSearchVisualTokens.moveInputToTheRight();
- }
- }
+ const maxInputWidth = 240;
+ const currentDropdownWidth = this.mapping[key].element.clientWidth || maxInputWidth;
- updateCurrentDropdownOffset() {
- this.updateDropdownOffset(this.currentDropdown);
+ // Make sure offset never exceeds the input container
+ const offsetMaxWidth = this.container.querySelector('.scroll-container').clientWidth - currentDropdownWidth;
+ if (offsetMaxWidth < offset) {
+ offset = offsetMaxWidth;
}
- updateDropdownOffset(key) {
- // Always align dropdown with the input field
- let offset = this.filteredSearchInput.getBoundingClientRect().left - this.container.querySelector('.scroll-container').getBoundingClientRect().left;
+ this.mapping[key].reference.setOffset(offset);
+ }
- const maxInputWidth = 240;
- const currentDropdownWidth = this.mapping[key].element.clientWidth || maxInputWidth;
+ load(key, firstLoad = false) {
+ const mappingKey = this.mapping[key];
+ const glClass = mappingKey.gl;
+ const element = mappingKey.element;
+ let forceShowList = false;
- // Make sure offset never exceeds the input container
- const offsetMaxWidth = this.container.querySelector('.scroll-container').clientWidth - currentDropdownWidth;
- if (offsetMaxWidth < offset) {
- offset = offsetMaxWidth;
- }
+ if (!mappingKey.reference) {
+ const dl = this.droplab;
+ const defaultArguments = [null, dl, element, this.filteredSearchInput, key];
+ const glArguments = defaultArguments.concat(mappingKey.extraArguments || []);
- this.mapping[key].reference.setOffset(offset);
+ // Passing glArguments to `new gl[glClass](<arguments>)`
+ mappingKey.reference = new (Function.prototype.bind.apply(gl[glClass], glArguments))();
}
- load(key, firstLoad = false) {
- const mappingKey = this.mapping[key];
- const glClass = mappingKey.gl;
- const element = mappingKey.element;
- let forceShowList = false;
-
- if (!mappingKey.reference) {
- const dl = this.droplab;
- const defaultArguments = [null, dl, element, this.filteredSearchInput, key];
- const glArguments = defaultArguments.concat(mappingKey.extraArguments || []);
+ if (firstLoad) {
+ mappingKey.reference.init();
+ }
- // Passing glArguments to `new gl[glClass](<arguments>)`
- mappingKey.reference = new (Function.prototype.bind.apply(gl[glClass], glArguments))();
- }
+ if (this.currentDropdown === 'hint') {
+ // Force the dropdown to show if it was clicked from the hint dropdown
+ forceShowList = true;
+ }
- if (firstLoad) {
- mappingKey.reference.init();
- }
+ this.updateDropdownOffset(key);
+ mappingKey.reference.render(firstLoad, forceShowList);
- if (this.currentDropdown === 'hint') {
- // Force the dropdown to show if it was clicked from the hint dropdown
- forceShowList = true;
- }
+ this.currentDropdown = key;
+ }
- this.updateDropdownOffset(key);
- mappingKey.reference.render(firstLoad, forceShowList);
+ loadDropdown(dropdownName = '') {
+ let firstLoad = false;
- this.currentDropdown = key;
+ if (!this.droplab) {
+ firstLoad = true;
+ this.droplab = new DropLab();
}
- loadDropdown(dropdownName = '') {
- let firstLoad = false;
+ const match = this.filteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase());
+ const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key
+ && this.mapping[match.key];
+ const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint';
- if (!this.droplab) {
- firstLoad = true;
- this.droplab = new DropLab();
- }
+ if (shouldOpenFilterDropdown || shouldOpenHintDropdown) {
+ const key = match && match.key ? match.key : 'hint';
+ this.load(key, firstLoad);
+ }
+ }
- const match = this.filteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase());
- const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key
- && this.mapping[match.key];
- const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint';
+ setDropdown() {
+ const query = gl.DropdownUtils.getSearchQuery(true);
+ const { lastToken, searchToken } = this.tokenizer.processTokens(query);
- if (shouldOpenFilterDropdown || shouldOpenHintDropdown) {
- const key = match && match.key ? match.key : 'hint';
- this.load(key, firstLoad);
- }
+ if (this.currentDropdown) {
+ this.updateCurrentDropdownOffset();
}
- setDropdown() {
- const query = gl.DropdownUtils.getSearchQuery(true);
- const { lastToken, searchToken } = this.tokenizer.processTokens(query);
-
- if (this.currentDropdown) {
- this.updateCurrentDropdownOffset();
- }
-
- if (lastToken === searchToken && lastToken !== null) {
- // Token is not fully initialized yet because it has no value
- // Eg. token = 'label:'
-
- const split = lastToken.split(':');
- const dropdownName = split[0].split(' ').last();
- this.loadDropdown(split.length > 1 ? dropdownName : '');
- } else if (lastToken) {
- // Token has been initialized into an object because it has a value
- this.loadDropdown(lastToken.key);
- } else {
- this.loadDropdown('hint');
- }
+ if (lastToken === searchToken && lastToken !== null) {
+ // Token is not fully initialized yet because it has no value
+ // Eg. token = 'label:'
+
+ const split = lastToken.split(':');
+ const dropdownName = split[0].split(' ').last();
+ this.loadDropdown(split.length > 1 ? dropdownName : '');
+ } else if (lastToken) {
+ // Token has been initialized into an object because it has a value
+ this.loadDropdown(lastToken.key);
+ } else {
+ this.loadDropdown('hint');
}
+ }
- resetDropdowns() {
- if (!this.currentDropdown) {
- return;
- }
+ resetDropdowns() {
+ if (!this.currentDropdown) {
+ return;
+ }
- // Force current dropdown to hide
- this.mapping[this.currentDropdown].reference.hideDropdown();
+ // Force current dropdown to hide
+ this.mapping[this.currentDropdown].reference.hideDropdown();
- // Re-Load dropdown
- this.setDropdown();
+ // Re-Load dropdown
+ this.setDropdown();
- // Reset filters for current dropdown
- this.mapping[this.currentDropdown].reference.resetFilters();
+ // Reset filters for current dropdown
+ this.mapping[this.currentDropdown].reference.resetFilters();
- // Reposition dropdown so that it is aligned with cursor
- this.updateDropdownOffset(this.currentDropdown);
- }
+ // Reposition dropdown so that it is aligned with cursor
+ this.updateDropdownOffset(this.currentDropdown);
+ }
- destroyDroplab() {
- this.droplab.destroy();
- }
+ destroyDroplab() {
+ this.droplab.destroy();
}
+}
- window.gl = window.gl || {};
- gl.FilteredSearchDropdownManager = FilteredSearchDropdownManager;
-})();
+window.gl = window.gl || {};
+gl.FilteredSearchDropdownManager = FilteredSearchDropdownManager;
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index b93a8f1d322..a5eb33dd9de 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -6,489 +6,487 @@ import RecentSearchesStore from './stores/recent_searches_store';
import RecentSearchesService from './services/recent_searches_service';
import eventHub from './event_hub';
-(() => {
- class FilteredSearchManager {
- constructor(page) {
- this.container = FilteredSearchContainer.container;
- this.filteredSearchInput = this.container.querySelector('.filtered-search');
- this.filteredSearchInputForm = this.filteredSearchInput.form;
- this.clearSearchButton = this.container.querySelector('.clear-search');
- this.tokensContainer = this.container.querySelector('.tokens-container');
- this.filteredSearchTokenKeys = gl.FilteredSearchTokenKeys;
-
- this.recentSearchesStore = new RecentSearchesStore();
- let recentSearchesKey = 'issue-recent-searches';
- if (page === 'merge_requests') {
- recentSearchesKey = 'merge-request-recent-searches';
- }
- this.recentSearchesService = new RecentSearchesService(recentSearchesKey);
-
- // Fetch recent searches from localStorage
- this.fetchingRecentSearchesPromise = this.recentSearchesService.fetch()
- .catch(() => {
- // eslint-disable-next-line no-new
- new Flash('An error occured while parsing recent searches');
- // Gracefully fail to empty array
- return [];
- })
- .then((searches) => {
- // Put any searches that may have come in before
- // we fetched the saved searches ahead of the already saved ones
- const resultantSearches = this.recentSearchesStore.setRecentSearches(
- this.recentSearchesStore.state.recentSearches.concat(searches),
- );
- this.recentSearchesService.save(resultantSearches);
- });
-
- if (this.filteredSearchInput) {
- this.tokenizer = gl.FilteredSearchTokenizer;
- this.dropdownManager = new gl.FilteredSearchDropdownManager(this.filteredSearchInput.getAttribute('data-base-endpoint') || '', page);
-
- this.recentSearchesRoot = new RecentSearchesRoot(
- this.recentSearchesStore,
- this.recentSearchesService,
- document.querySelector('.js-filtered-search-history-dropdown'),
+class FilteredSearchManager {
+ constructor(page) {
+ this.container = FilteredSearchContainer.container;
+ this.filteredSearchInput = this.container.querySelector('.filtered-search');
+ this.filteredSearchInputForm = this.filteredSearchInput.form;
+ this.clearSearchButton = this.container.querySelector('.clear-search');
+ this.tokensContainer = this.container.querySelector('.tokens-container');
+ this.filteredSearchTokenKeys = gl.FilteredSearchTokenKeys;
+
+ this.recentSearchesStore = new RecentSearchesStore();
+ let recentSearchesKey = 'issue-recent-searches';
+ if (page === 'merge_requests') {
+ recentSearchesKey = 'merge-request-recent-searches';
+ }
+ this.recentSearchesService = new RecentSearchesService(recentSearchesKey);
+
+ // Fetch recent searches from localStorage
+ this.fetchingRecentSearchesPromise = this.recentSearchesService.fetch()
+ .catch(() => {
+ // eslint-disable-next-line no-new
+ new Flash('An error occured while parsing recent searches');
+ // Gracefully fail to empty array
+ return [];
+ })
+ .then((searches) => {
+ // Put any searches that may have come in before
+ // we fetched the saved searches ahead of the already saved ones
+ const resultantSearches = this.recentSearchesStore.setRecentSearches(
+ this.recentSearchesStore.state.recentSearches.concat(searches),
);
- this.recentSearchesRoot.init();
+ this.recentSearchesService.save(resultantSearches);
+ });
- this.bindEvents();
- this.loadSearchParamsFromURL();
- this.dropdownManager.setDropdown();
+ if (this.filteredSearchInput) {
+ this.tokenizer = gl.FilteredSearchTokenizer;
+ this.dropdownManager = new gl.FilteredSearchDropdownManager(this.filteredSearchInput.getAttribute('data-base-endpoint') || '', page);
- this.cleanupWrapper = this.cleanup.bind(this);
- document.addEventListener('beforeunload', this.cleanupWrapper);
- }
- }
+ this.recentSearchesRoot = new RecentSearchesRoot(
+ this.recentSearchesStore,
+ this.recentSearchesService,
+ document.querySelector('.js-filtered-search-history-dropdown'),
+ );
+ this.recentSearchesRoot.init();
- cleanup() {
- this.unbindEvents();
- document.removeEventListener('beforeunload', this.cleanupWrapper);
+ this.bindEvents();
+ this.loadSearchParamsFromURL();
+ this.dropdownManager.setDropdown();
- if (this.recentSearchesRoot) {
- this.recentSearchesRoot.destroy();
- }
+ this.cleanupWrapper = this.cleanup.bind(this);
+ document.addEventListener('beforeunload', this.cleanupWrapper);
}
+ }
- bindEvents() {
- this.handleFormSubmit = this.handleFormSubmit.bind(this);
- this.setDropdownWrapper = this.dropdownManager.setDropdown.bind(this.dropdownManager);
- this.toggleClearSearchButtonWrapper = this.toggleClearSearchButton.bind(this);
- this.handleInputPlaceholderWrapper = this.handleInputPlaceholder.bind(this);
- this.handleInputVisualTokenWrapper = this.handleInputVisualToken.bind(this);
- 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.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.filteredSearchInputForm.addEventListener('submit', this.handleFormSubmit);
- this.filteredSearchInput.addEventListener('input', this.setDropdownWrapper);
- this.filteredSearchInput.addEventListener('input', this.toggleClearSearchButtonWrapper);
- this.filteredSearchInput.addEventListener('input', this.handleInputPlaceholderWrapper);
- this.filteredSearchInput.addEventListener('input', this.handleInputVisualTokenWrapper);
- this.filteredSearchInput.addEventListener('keydown', this.checkForEnterWrapper);
- this.filteredSearchInput.addEventListener('keyup', this.checkForBackspaceWrapper);
- this.filteredSearchInput.addEventListener('click', this.tokenChange);
- this.filteredSearchInput.addEventListener('keyup', this.tokenChange);
- this.filteredSearchInput.addEventListener('focus', this.addInputContainerFocusWrapper);
- this.tokensContainer.addEventListener('click', FilteredSearchManager.selectToken);
- 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);
- eventHub.$on('recentSearchesItemSelected', this.onrecentSearchesItemSelectedWrapper);
- }
+ cleanup() {
+ this.unbindEvents();
+ document.removeEventListener('beforeunload', this.cleanupWrapper);
- unbindEvents() {
- this.filteredSearchInputForm.removeEventListener('submit', this.handleFormSubmit);
- this.filteredSearchInput.removeEventListener('input', this.setDropdownWrapper);
- this.filteredSearchInput.removeEventListener('input', this.toggleClearSearchButtonWrapper);
- this.filteredSearchInput.removeEventListener('input', this.handleInputPlaceholderWrapper);
- this.filteredSearchInput.removeEventListener('input', this.handleInputVisualTokenWrapper);
- this.filteredSearchInput.removeEventListener('keydown', this.checkForEnterWrapper);
- this.filteredSearchInput.removeEventListener('keyup', this.checkForBackspaceWrapper);
- this.filteredSearchInput.removeEventListener('click', this.tokenChange);
- this.filteredSearchInput.removeEventListener('keyup', this.tokenChange);
- this.filteredSearchInput.removeEventListener('focus', this.addInputContainerFocusWrapper);
- this.tokensContainer.removeEventListener('click', FilteredSearchManager.selectToken);
- 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);
- eventHub.$off('recentSearchesItemSelected', this.onrecentSearchesItemSelectedWrapper);
+ if (this.recentSearchesRoot) {
+ this.recentSearchesRoot.destroy();
}
+ }
- checkForBackspace(e) {
- // 8 = Backspace Key
- // 46 = Delete Key
- if (e.keyCode === 8 || e.keyCode === 46) {
- const { lastVisualToken } = gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
+ bindEvents() {
+ this.handleFormSubmit = this.handleFormSubmit.bind(this);
+ this.setDropdownWrapper = this.dropdownManager.setDropdown.bind(this.dropdownManager);
+ this.toggleClearSearchButtonWrapper = this.toggleClearSearchButton.bind(this);
+ this.handleInputPlaceholderWrapper = this.handleInputPlaceholder.bind(this);
+ this.handleInputVisualTokenWrapper = this.handleInputVisualToken.bind(this);
+ 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.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.filteredSearchInputForm.addEventListener('submit', this.handleFormSubmit);
+ this.filteredSearchInput.addEventListener('input', this.setDropdownWrapper);
+ this.filteredSearchInput.addEventListener('input', this.toggleClearSearchButtonWrapper);
+ this.filteredSearchInput.addEventListener('input', this.handleInputPlaceholderWrapper);
+ this.filteredSearchInput.addEventListener('input', this.handleInputVisualTokenWrapper);
+ this.filteredSearchInput.addEventListener('keydown', this.checkForEnterWrapper);
+ this.filteredSearchInput.addEventListener('keyup', this.checkForBackspaceWrapper);
+ this.filteredSearchInput.addEventListener('click', this.tokenChange);
+ this.filteredSearchInput.addEventListener('keyup', this.tokenChange);
+ this.filteredSearchInput.addEventListener('focus', this.addInputContainerFocusWrapper);
+ this.tokensContainer.addEventListener('click', FilteredSearchManager.selectToken);
+ 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);
+ eventHub.$on('recentSearchesItemSelected', this.onrecentSearchesItemSelectedWrapper);
+ }
- if (this.filteredSearchInput.value === '' && lastVisualToken) {
- this.filteredSearchInput.value = gl.FilteredSearchVisualTokens.getLastTokenPartial();
- gl.FilteredSearchVisualTokens.removeLastTokenPartial();
- }
+ unbindEvents() {
+ this.filteredSearchInputForm.removeEventListener('submit', this.handleFormSubmit);
+ this.filteredSearchInput.removeEventListener('input', this.setDropdownWrapper);
+ this.filteredSearchInput.removeEventListener('input', this.toggleClearSearchButtonWrapper);
+ this.filteredSearchInput.removeEventListener('input', this.handleInputPlaceholderWrapper);
+ this.filteredSearchInput.removeEventListener('input', this.handleInputVisualTokenWrapper);
+ this.filteredSearchInput.removeEventListener('keydown', this.checkForEnterWrapper);
+ this.filteredSearchInput.removeEventListener('keyup', this.checkForBackspaceWrapper);
+ this.filteredSearchInput.removeEventListener('click', this.tokenChange);
+ this.filteredSearchInput.removeEventListener('keyup', this.tokenChange);
+ this.filteredSearchInput.removeEventListener('focus', this.addInputContainerFocusWrapper);
+ this.tokensContainer.removeEventListener('click', FilteredSearchManager.selectToken);
+ 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);
+ eventHub.$off('recentSearchesItemSelected', this.onrecentSearchesItemSelectedWrapper);
+ }
+
+ checkForBackspace(e) {
+ // 8 = Backspace Key
+ // 46 = Delete Key
+ if (e.keyCode === 8 || e.keyCode === 46) {
+ const { lastVisualToken } = gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
- // Reposition dropdown so that it is aligned with cursor
- this.dropdownManager.updateCurrentDropdownOffset();
+ if (this.filteredSearchInput.value === '' && lastVisualToken) {
+ this.filteredSearchInput.value = gl.FilteredSearchVisualTokens.getLastTokenPartial();
+ gl.FilteredSearchVisualTokens.removeLastTokenPartial();
}
- }
- checkForEnter(e) {
- if (e.keyCode === 38 || e.keyCode === 40) {
- const selectionStart = this.filteredSearchInput.selectionStart;
+ // Reposition dropdown so that it is aligned with cursor
+ this.dropdownManager.updateCurrentDropdownOffset();
+ }
+ }
- e.preventDefault();
- this.filteredSearchInput.setSelectionRange(selectionStart, selectionStart);
- }
+ checkForEnter(e) {
+ if (e.keyCode === 38 || e.keyCode === 40) {
+ const selectionStart = this.filteredSearchInput.selectionStart;
- if (e.keyCode === 13) {
- const dropdown = this.dropdownManager.mapping[this.dropdownManager.currentDropdown];
- const dropdownEl = dropdown.element;
- const activeElements = dropdownEl.querySelectorAll('.droplab-item-active');
+ e.preventDefault();
+ this.filteredSearchInput.setSelectionRange(selectionStart, selectionStart);
+ }
- e.preventDefault();
+ if (e.keyCode === 13) {
+ const dropdown = this.dropdownManager.mapping[this.dropdownManager.currentDropdown];
+ const dropdownEl = dropdown.element;
+ const activeElements = dropdownEl.querySelectorAll('.droplab-item-active');
- if (!activeElements.length) {
- if (this.isHandledAsync) {
- e.stopImmediatePropagation();
+ e.preventDefault();
- this.filteredSearchInput.blur();
- this.dropdownManager.resetDropdowns();
- } else {
- // Prevent droplab from opening dropdown
- this.dropdownManager.destroyDroplab();
- }
+ if (!activeElements.length) {
+ if (this.isHandledAsync) {
+ e.stopImmediatePropagation();
- this.search();
+ this.filteredSearchInput.blur();
+ this.dropdownManager.resetDropdowns();
+ } else {
+ // Prevent droplab from opening dropdown
+ this.dropdownManager.destroyDroplab();
}
+
+ this.search();
}
}
+ }
- addInputContainerFocus() {
- const inputContainer = this.filteredSearchInput.closest('.filtered-search-box');
+ addInputContainerFocus() {
+ const inputContainer = this.filteredSearchInput.closest('.filtered-search-box');
- if (inputContainer) {
- inputContainer.classList.add('focus');
- }
+ if (inputContainer) {
+ inputContainer.classList.add('focus');
}
+ }
- removeInputContainerFocus(e) {
- const inputContainer = this.filteredSearchInput.closest('.filtered-search-box');
- const isElementInFilteredSearch = inputContainer && inputContainer.contains(e.target);
- const isElementInDynamicFilterDropdown = e.target.closest('.filter-dropdown') !== null;
- const isElementInStaticFilterDropdown = e.target.closest('ul[data-dropdown]') !== null;
+ removeInputContainerFocus(e) {
+ const inputContainer = this.filteredSearchInput.closest('.filtered-search-box');
+ const isElementInFilteredSearch = inputContainer && inputContainer.contains(e.target);
+ const isElementInDynamicFilterDropdown = e.target.closest('.filter-dropdown') !== null;
+ const isElementInStaticFilterDropdown = e.target.closest('ul[data-dropdown]') !== null;
- if (!isElementInFilteredSearch && !isElementInDynamicFilterDropdown &&
- !isElementInStaticFilterDropdown && inputContainer) {
- inputContainer.classList.remove('focus');
- }
+ if (!isElementInFilteredSearch && !isElementInDynamicFilterDropdown &&
+ !isElementInStaticFilterDropdown && inputContainer) {
+ inputContainer.classList.remove('focus');
}
+ }
- static selectToken(e) {
- const button = e.target.closest('.selectable');
+ static selectToken(e) {
+ const button = e.target.closest('.selectable');
- if (button) {
- e.preventDefault();
- e.stopPropagation();
- gl.FilteredSearchVisualTokens.selectToken(button);
- }
+ if (button) {
+ e.preventDefault();
+ e.stopPropagation();
+ gl.FilteredSearchVisualTokens.selectToken(button);
}
+ }
- unselectEditTokens(e) {
- const inputContainer = this.container.querySelector('.filtered-search-box');
- const isElementInFilteredSearch = inputContainer && inputContainer.contains(e.target);
- const isElementInFilterDropdown = e.target.closest('.filter-dropdown') !== null;
- const isElementTokensContainer = e.target.classList.contains('tokens-container');
+ unselectEditTokens(e) {
+ const inputContainer = this.container.querySelector('.filtered-search-box');
+ const isElementInFilteredSearch = inputContainer && inputContainer.contains(e.target);
+ const isElementInFilterDropdown = e.target.closest('.filter-dropdown') !== null;
+ const isElementTokensContainer = e.target.classList.contains('tokens-container');
- if ((!isElementInFilteredSearch && !isElementInFilterDropdown) || isElementTokensContainer) {
- gl.FilteredSearchVisualTokens.moveInputToTheRight();
- this.dropdownManager.resetDropdowns();
- }
+ if ((!isElementInFilteredSearch && !isElementInFilterDropdown) || isElementTokensContainer) {
+ gl.FilteredSearchVisualTokens.moveInputToTheRight();
+ this.dropdownManager.resetDropdowns();
}
+ }
- editToken(e) {
- const token = e.target.closest('.js-visual-token');
+ editToken(e) {
+ const token = e.target.closest('.js-visual-token');
- if (token) {
- gl.FilteredSearchVisualTokens.editToken(token);
- this.tokenChange();
- }
+ if (token) {
+ gl.FilteredSearchVisualTokens.editToken(token);
+ this.tokenChange();
}
+ }
- toggleClearSearchButton() {
- const query = gl.DropdownUtils.getSearchQuery();
- const hidden = 'hidden';
- const hasHidden = this.clearSearchButton.classList.contains(hidden);
+ toggleClearSearchButton() {
+ const query = gl.DropdownUtils.getSearchQuery();
+ const hidden = 'hidden';
+ const hasHidden = this.clearSearchButton.classList.contains(hidden);
- if (query.length === 0 && !hasHidden) {
- this.clearSearchButton.classList.add(hidden);
- } else if (query.length && hasHidden) {
- this.clearSearchButton.classList.remove(hidden);
- }
+ if (query.length === 0 && !hasHidden) {
+ this.clearSearchButton.classList.add(hidden);
+ } else if (query.length && hasHidden) {
+ this.clearSearchButton.classList.remove(hidden);
}
+ }
- handleInputPlaceholder() {
- const query = gl.DropdownUtils.getSearchQuery();
- const placeholder = 'Search or filter results...';
- const currentPlaceholder = this.filteredSearchInput.placeholder;
+ handleInputPlaceholder() {
+ const query = gl.DropdownUtils.getSearchQuery();
+ const placeholder = 'Search or filter results...';
+ const currentPlaceholder = this.filteredSearchInput.placeholder;
- if (query.length === 0 && currentPlaceholder !== placeholder) {
- this.filteredSearchInput.placeholder = placeholder;
- } else if (query.length > 0 && currentPlaceholder !== '') {
- this.filteredSearchInput.placeholder = '';
- }
+ if (query.length === 0 && currentPlaceholder !== placeholder) {
+ this.filteredSearchInput.placeholder = placeholder;
+ } else if (query.length > 0 && currentPlaceholder !== '') {
+ this.filteredSearchInput.placeholder = '';
}
+ }
- removeSelectedToken(e) {
- // 8 = Backspace Key
- // 46 = Delete Key
- if (e.keyCode === 8 || e.keyCode === 46) {
- gl.FilteredSearchVisualTokens.removeSelectedToken();
- this.handleInputPlaceholder();
- this.toggleClearSearchButton();
- }
+ removeSelectedToken(e) {
+ // 8 = Backspace Key
+ // 46 = Delete Key
+ if (e.keyCode === 8 || e.keyCode === 46) {
+ gl.FilteredSearchVisualTokens.removeSelectedToken();
+ this.handleInputPlaceholder();
+ this.toggleClearSearchButton();
}
+ }
- onClearSearch(e) {
- e.preventDefault();
- this.clearSearch();
- }
+ onClearSearch(e) {
+ e.preventDefault();
+ this.clearSearch();
+ }
- clearSearch() {
- this.filteredSearchInput.value = '';
+ clearSearch() {
+ this.filteredSearchInput.value = '';
- const removeElements = [];
+ const removeElements = [];
- [].forEach.call(this.tokensContainer.children, (t) => {
- if (t.classList.contains('js-visual-token')) {
- removeElements.push(t);
- }
- });
+ [].forEach.call(this.tokensContainer.children, (t) => {
+ if (t.classList.contains('js-visual-token')) {
+ removeElements.push(t);
+ }
+ });
- removeElements.forEach((el) => {
- el.parentElement.removeChild(el);
- });
+ removeElements.forEach((el) => {
+ el.parentElement.removeChild(el);
+ });
- this.clearSearchButton.classList.add('hidden');
- this.handleInputPlaceholder();
+ this.clearSearchButton.classList.add('hidden');
+ this.handleInputPlaceholder();
- this.dropdownManager.resetDropdowns();
+ this.dropdownManager.resetDropdowns();
- if (this.isHandledAsync) {
- this.search();
- }
+ if (this.isHandledAsync) {
+ this.search();
}
+ }
- handleInputVisualToken() {
- const input = this.filteredSearchInput;
- const { tokens, searchToken }
- = gl.FilteredSearchTokenizer.processTokens(input.value);
- const { isLastVisualTokenValid }
- = gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
-
- if (isLastVisualTokenValid) {
- tokens.forEach((t) => {
- input.value = input.value.replace(`${t.key}:${t.symbol}${t.value}`, '');
- gl.FilteredSearchVisualTokens.addFilterVisualToken(t.key, `${t.symbol}${t.value}`);
- });
-
- const fragments = searchToken.split(':');
- if (fragments.length > 1) {
- const inputValues = fragments[0].split(' ');
- const tokenKey = inputValues.last();
-
- if (inputValues.length > 1) {
- inputValues.pop();
- const searchTerms = inputValues.join(' ');
-
- input.value = input.value.replace(searchTerms, '');
- gl.FilteredSearchVisualTokens.addSearchVisualToken(searchTerms);
- }
+ handleInputVisualToken() {
+ const input = this.filteredSearchInput;
+ const { tokens, searchToken }
+ = gl.FilteredSearchTokenizer.processTokens(input.value);
+ const { isLastVisualTokenValid }
+ = gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
+
+ if (isLastVisualTokenValid) {
+ tokens.forEach((t) => {
+ input.value = input.value.replace(`${t.key}:${t.symbol}${t.value}`, '');
+ gl.FilteredSearchVisualTokens.addFilterVisualToken(t.key, `${t.symbol}${t.value}`);
+ });
- gl.FilteredSearchVisualTokens.addFilterVisualToken(tokenKey);
- input.value = input.value.replace(`${tokenKey}:`, '');
- }
- } else {
- // Keep listening to token until we determine that the user is done typing the token value
- const valueCompletedRegex = /([~%@]{0,1}".+")|([~%@]{0,1}'.+')|^((?![~%@]')(?![~%@]")(?!')(?!")).*/g;
+ const fragments = searchToken.split(':');
+ if (fragments.length > 1) {
+ const inputValues = fragments[0].split(' ');
+ const tokenKey = inputValues.last();
- if (searchToken.match(valueCompletedRegex) && input.value[input.value.length - 1] === ' ') {
- gl.FilteredSearchVisualTokens.addFilterVisualToken(searchToken);
+ if (inputValues.length > 1) {
+ inputValues.pop();
+ const searchTerms = inputValues.join(' ');
- // Trim the last space as seen in the if statement above
- input.value = input.value.replace(searchToken, '').trim();
+ input.value = input.value.replace(searchTerms, '');
+ gl.FilteredSearchVisualTokens.addSearchVisualToken(searchTerms);
}
+
+ gl.FilteredSearchVisualTokens.addFilterVisualToken(tokenKey);
+ input.value = input.value.replace(`${tokenKey}:`, '');
}
- }
+ } else {
+ // Keep listening to token until we determine that the user is done typing the token value
+ const valueCompletedRegex = /([~%@]{0,1}".+")|([~%@]{0,1}'.+')|^((?![~%@]')(?![~%@]")(?!')(?!")).*/g;
- handleFormSubmit(e) {
- e.preventDefault();
- this.search();
- }
+ if (searchToken.match(valueCompletedRegex) && input.value[input.value.length - 1] === ' ') {
+ gl.FilteredSearchVisualTokens.addFilterVisualToken(searchToken);
- saveCurrentSearchQuery() {
- // Don't save before we have fetched the already saved searches
- this.fetchingRecentSearchesPromise.then(() => {
- const searchQuery = gl.DropdownUtils.getSearchQuery();
- if (searchQuery.length > 0) {
- const resultantSearches = this.recentSearchesStore.addRecentSearch(searchQuery);
- this.recentSearchesService.save(resultantSearches);
- }
- });
+ // Trim the last space as seen in the if statement above
+ input.value = input.value.replace(searchToken, '').trim();
+ }
}
+ }
- loadSearchParamsFromURL() {
- const params = gl.utils.getUrlParamsArray();
- const usernameParams = this.getUsernameParams();
- let hasFilteredSearch = false;
+ handleFormSubmit(e) {
+ e.preventDefault();
+ this.search();
+ }
- params.forEach((p) => {
- const split = p.split('=');
- const keyParam = decodeURIComponent(split[0]);
- const value = split[1];
+ saveCurrentSearchQuery() {
+ // Don't save before we have fetched the already saved searches
+ this.fetchingRecentSearchesPromise.then(() => {
+ const searchQuery = gl.DropdownUtils.getSearchQuery();
+ if (searchQuery.length > 0) {
+ const resultantSearches = this.recentSearchesStore.addRecentSearch(searchQuery);
+ this.recentSearchesService.save(resultantSearches);
+ }
+ });
+ }
- // Check if it matches edge conditions listed in this.filteredSearchTokenKeys
- const condition = this.filteredSearchTokenKeys.searchByConditionUrl(p);
+ loadSearchParamsFromURL() {
+ const params = gl.utils.getUrlParamsArray();
+ const usernameParams = this.getUsernameParams();
+ let hasFilteredSearch = false;
- if (condition) {
- hasFilteredSearch = true;
- gl.FilteredSearchVisualTokens.addFilterVisualToken(condition.tokenKey, condition.value);
- } else {
- // Sanitize value since URL converts spaces into +
- // Replace before decode so that we know what was originally + versus the encoded +
- const sanitizedValue = value ? decodeURIComponent(value.replace(/\+/g, ' ')) : value;
- const match = this.filteredSearchTokenKeys.searchByKeyParam(keyParam);
-
- if (match) {
- const indexOf = keyParam.indexOf('_');
- const sanitizedKey = indexOf !== -1 ? keyParam.slice(0, keyParam.indexOf('_')) : keyParam;
- const symbol = match.symbol;
- let quotationsToUse = '';
-
- if (sanitizedValue.indexOf(' ') !== -1) {
- // Prefer ", but use ' if required
- quotationsToUse = sanitizedValue.indexOf('"') === -1 ? '"' : '\'';
- }
+ params.forEach((p) => {
+ const split = p.split('=');
+ const keyParam = decodeURIComponent(split[0]);
+ const value = split[1];
+
+ // Check if it matches edge conditions listed in this.filteredSearchTokenKeys
+ const condition = this.filteredSearchTokenKeys.searchByConditionUrl(p);
+ if (condition) {
+ hasFilteredSearch = true;
+ gl.FilteredSearchVisualTokens.addFilterVisualToken(condition.tokenKey, condition.value);
+ } else {
+ // Sanitize value since URL converts spaces into +
+ // Replace before decode so that we know what was originally + versus the encoded +
+ const sanitizedValue = value ? decodeURIComponent(value.replace(/\+/g, ' ')) : value;
+ const match = this.filteredSearchTokenKeys.searchByKeyParam(keyParam);
+
+ if (match) {
+ const indexOf = keyParam.indexOf('_');
+ const sanitizedKey = indexOf !== -1 ? keyParam.slice(0, keyParam.indexOf('_')) : keyParam;
+ const symbol = match.symbol;
+ let quotationsToUse = '';
+
+ if (sanitizedValue.indexOf(' ') !== -1) {
+ // Prefer ", but use ' if required
+ quotationsToUse = sanitizedValue.indexOf('"') === -1 ? '"' : '\'';
+ }
+
+ hasFilteredSearch = true;
+ gl.FilteredSearchVisualTokens.addFilterVisualToken(sanitizedKey, `${symbol}${quotationsToUse}${sanitizedValue}${quotationsToUse}`);
+ } else if (!match && keyParam === 'assignee_id') {
+ const id = parseInt(value, 10);
+ if (usernameParams[id]) {
hasFilteredSearch = true;
- gl.FilteredSearchVisualTokens.addFilterVisualToken(sanitizedKey, `${symbol}${quotationsToUse}${sanitizedValue}${quotationsToUse}`);
- } else if (!match && keyParam === 'assignee_id') {
- const id = parseInt(value, 10);
- if (usernameParams[id]) {
- hasFilteredSearch = true;
- gl.FilteredSearchVisualTokens.addFilterVisualToken('assignee', `@${usernameParams[id]}`);
- }
- } else if (!match && keyParam === 'author_id') {
- const id = parseInt(value, 10);
- if (usernameParams[id]) {
- hasFilteredSearch = true;
- gl.FilteredSearchVisualTokens.addFilterVisualToken('author', `@${usernameParams[id]}`);
- }
- } else if (!match && keyParam === 'search') {
+ gl.FilteredSearchVisualTokens.addFilterVisualToken('assignee', `@${usernameParams[id]}`);
+ }
+ } else if (!match && keyParam === 'author_id') {
+ const id = parseInt(value, 10);
+ if (usernameParams[id]) {
hasFilteredSearch = true;
- this.filteredSearchInput.value = sanitizedValue;
+ gl.FilteredSearchVisualTokens.addFilterVisualToken('author', `@${usernameParams[id]}`);
}
+ } else if (!match && keyParam === 'search') {
+ hasFilteredSearch = true;
+ this.filteredSearchInput.value = sanitizedValue;
}
- });
+ }
+ });
- this.saveCurrentSearchQuery();
+ this.saveCurrentSearchQuery();
- if (hasFilteredSearch) {
- this.clearSearchButton.classList.remove('hidden');
- this.handleInputPlaceholder();
- }
+ if (hasFilteredSearch) {
+ this.clearSearchButton.classList.remove('hidden');
+ this.handleInputPlaceholder();
}
+ }
- search() {
- const paths = [];
- const searchQuery = gl.DropdownUtils.getSearchQuery();
-
- this.saveCurrentSearchQuery();
+ search() {
+ const paths = [];
+ const searchQuery = gl.DropdownUtils.getSearchQuery();
- const { tokens, searchToken }
- = this.tokenizer.processTokens(searchQuery);
- const currentState = gl.utils.getParameterByName('state') || 'opened';
- paths.push(`state=${currentState}`);
+ this.saveCurrentSearchQuery();
- tokens.forEach((token) => {
- const condition = this.filteredSearchTokenKeys
- .searchByConditionKeyValue(token.key, token.value.toLowerCase());
- const { param } = this.filteredSearchTokenKeys.searchByKey(token.key) || {};
- const keyParam = param ? `${token.key}_${param}` : token.key;
- let tokenPath = '';
+ const { tokens, searchToken }
+ = this.tokenizer.processTokens(searchQuery);
+ const currentState = gl.utils.getParameterByName('state') || 'opened';
+ paths.push(`state=${currentState}`);
- if (condition) {
- tokenPath = condition.url;
- } else {
- let tokenValue = token.value;
+ tokens.forEach((token) => {
+ const condition = this.filteredSearchTokenKeys
+ .searchByConditionKeyValue(token.key, token.value.toLowerCase());
+ const { param } = this.filteredSearchTokenKeys.searchByKey(token.key) || {};
+ const keyParam = param ? `${token.key}_${param}` : token.key;
+ let tokenPath = '';
- if ((tokenValue[0] === '\'' && tokenValue[tokenValue.length - 1] === '\'') ||
- (tokenValue[0] === '"' && tokenValue[tokenValue.length - 1] === '"')) {
- tokenValue = tokenValue.slice(1, tokenValue.length - 1);
- }
+ if (condition) {
+ tokenPath = condition.url;
+ } else {
+ let tokenValue = token.value;
- tokenPath = `${keyParam}=${encodeURIComponent(tokenValue)}`;
+ if ((tokenValue[0] === '\'' && tokenValue[tokenValue.length - 1] === '\'') ||
+ (tokenValue[0] === '"' && tokenValue[tokenValue.length - 1] === '"')) {
+ tokenValue = tokenValue.slice(1, tokenValue.length - 1);
}
- paths.push(tokenPath);
- });
-
- if (searchToken) {
- const sanitized = searchToken.split(' ').map(t => encodeURIComponent(t)).join('+');
- paths.push(`search=${sanitized}`);
+ tokenPath = `${keyParam}=${encodeURIComponent(tokenValue)}`;
}
- const parameterizedUrl = `?scope=all&utf8=%E2%9C%93&${paths.join('&')}`;
+ paths.push(tokenPath);
+ });
- if (this.updateObject) {
- this.updateObject(parameterizedUrl);
- } else {
- gl.utils.visitUrl(parameterizedUrl);
- }
+ if (searchToken) {
+ const sanitized = searchToken.split(' ').map(t => encodeURIComponent(t)).join('+');
+ paths.push(`search=${sanitized}`);
}
- getUsernameParams() {
- const usernamesById = {};
- try {
- const attribute = this.filteredSearchInput.getAttribute('data-username-params');
- JSON.parse(attribute).forEach((user) => {
- usernamesById[user.id] = user.username;
- });
- } catch (e) {
- // do nothing
- }
- return usernamesById;
+ const parameterizedUrl = `?scope=all&utf8=%E2%9C%93&${paths.join('&')}`;
+
+ if (this.updateObject) {
+ this.updateObject(parameterizedUrl);
+ } else {
+ gl.utils.visitUrl(parameterizedUrl);
}
+ }
- tokenChange() {
- const dropdown = this.dropdownManager.mapping[this.dropdownManager.currentDropdown];
+ getUsernameParams() {
+ const usernamesById = {};
+ try {
+ const attribute = this.filteredSearchInput.getAttribute('data-username-params');
+ JSON.parse(attribute).forEach((user) => {
+ usernamesById[user.id] = user.username;
+ });
+ } catch (e) {
+ // do nothing
+ }
+ return usernamesById;
+ }
- if (dropdown) {
- const currentDropdownRef = dropdown.reference;
+ tokenChange() {
+ const dropdown = this.dropdownManager.mapping[this.dropdownManager.currentDropdown];
- this.setDropdownWrapper();
- currentDropdownRef.dispatchInputEvent();
- }
- }
+ if (dropdown) {
+ const currentDropdownRef = dropdown.reference;
- onrecentSearchesItemSelected(text) {
- this.clearSearch();
- this.filteredSearchInput.value = text;
- this.filteredSearchInput.dispatchEvent(new CustomEvent('input'));
- this.search();
+ this.setDropdownWrapper();
+ currentDropdownRef.dispatchInputEvent();
}
}
- window.gl = window.gl || {};
- gl.FilteredSearchManager = FilteredSearchManager;
-})();
+ onrecentSearchesItemSelected(text) {
+ this.clearSearch();
+ this.filteredSearchInput.value = text;
+ this.filteredSearchInput.dispatchEvent(new CustomEvent('input'));
+ this.search();
+ }
+}
+
+window.gl = window.gl || {};
+gl.FilteredSearchManager = FilteredSearchManager;
diff --git a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
index 6d5df86f2a5..1abad9d1b73 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
@@ -1,100 +1,98 @@
-(() => {
- const tokenKeys = [{
- key: 'author',
- type: 'string',
- param: 'username',
- symbol: '@',
- }, {
- key: 'assignee',
- type: 'string',
- param: 'username',
- symbol: '@',
- }, {
- key: 'milestone',
- type: 'string',
- param: 'title',
- symbol: '%',
- }, {
- key: 'label',
- type: 'array',
- param: 'name[]',
- symbol: '~',
- }];
+const tokenKeys = [{
+ key: 'author',
+ type: 'string',
+ param: 'username',
+ symbol: '@',
+}, {
+ key: 'assignee',
+ type: 'string',
+ param: 'username',
+ symbol: '@',
+}, {
+ key: 'milestone',
+ type: 'string',
+ param: 'title',
+ symbol: '%',
+}, {
+ key: 'label',
+ type: 'array',
+ param: 'name[]',
+ symbol: '~',
+}];
- const alternativeTokenKeys = [{
- key: 'label',
- type: 'string',
- param: 'name',
- symbol: '~',
- }];
+const alternativeTokenKeys = [{
+ key: 'label',
+ type: 'string',
+ param: 'name',
+ symbol: '~',
+}];
- const tokenKeysWithAlternative = tokenKeys.concat(alternativeTokenKeys);
+const tokenKeysWithAlternative = tokenKeys.concat(alternativeTokenKeys);
- const conditions = [{
- url: 'assignee_id=0',
- tokenKey: 'assignee',
- value: 'none',
- }, {
- url: 'milestone_title=No+Milestone',
- tokenKey: 'milestone',
- value: 'none',
- }, {
- url: 'milestone_title=%23upcoming',
- tokenKey: 'milestone',
- value: 'upcoming',
- }, {
- url: 'milestone_title=%23started',
- tokenKey: 'milestone',
- value: 'started',
- }, {
- url: 'label_name[]=No+Label',
- tokenKey: 'label',
- value: 'none',
- }];
+const conditions = [{
+ url: 'assignee_id=0',
+ tokenKey: 'assignee',
+ value: 'none',
+}, {
+ url: 'milestone_title=No+Milestone',
+ tokenKey: 'milestone',
+ value: 'none',
+}, {
+ url: 'milestone_title=%23upcoming',
+ tokenKey: 'milestone',
+ value: 'upcoming',
+}, {
+ url: 'milestone_title=%23started',
+ tokenKey: 'milestone',
+ value: 'started',
+}, {
+ url: 'label_name[]=No+Label',
+ tokenKey: 'label',
+ value: 'none',
+}];
- class FilteredSearchTokenKeys {
- static get() {
- return tokenKeys;
- }
+class FilteredSearchTokenKeys {
+ static get() {
+ return tokenKeys;
+ }
- static getAlternatives() {
- return alternativeTokenKeys;
- }
+ static getAlternatives() {
+ return alternativeTokenKeys;
+ }
- static getConditions() {
- return conditions;
- }
+ static getConditions() {
+ return conditions;
+ }
- static searchByKey(key) {
- return tokenKeys.find(tokenKey => tokenKey.key === key) || null;
- }
+ static searchByKey(key) {
+ return tokenKeys.find(tokenKey => tokenKey.key === key) || null;
+ }
- static searchBySymbol(symbol) {
- return tokenKeys.find(tokenKey => tokenKey.symbol === symbol) || null;
- }
+ static searchBySymbol(symbol) {
+ return tokenKeys.find(tokenKey => tokenKey.symbol === symbol) || null;
+ }
- static searchByKeyParam(keyParam) {
- return tokenKeysWithAlternative.find((tokenKey) => {
- let tokenKeyParam = tokenKey.key;
+ static searchByKeyParam(keyParam) {
+ return tokenKeysWithAlternative.find((tokenKey) => {
+ let tokenKeyParam = tokenKey.key;
- if (tokenKey.param) {
- tokenKeyParam += `_${tokenKey.param}`;
- }
+ if (tokenKey.param) {
+ tokenKeyParam += `_${tokenKey.param}`;
+ }
- return keyParam === tokenKeyParam;
- }) || null;
- }
+ return keyParam === tokenKeyParam;
+ }) || null;
+ }
- static searchByConditionUrl(url) {
- return conditions.find(condition => condition.url === url) || null;
- }
+ static searchByConditionUrl(url) {
+ return conditions.find(condition => condition.url === url) || null;
+ }
- static searchByConditionKeyValue(key, value) {
- return conditions
- .find(condition => condition.tokenKey === key && condition.value === value) || null;
- }
+ static searchByConditionKeyValue(key, value) {
+ return conditions
+ .find(condition => condition.tokenKey === key && condition.value === value) || null;
}
+}
- window.gl = window.gl || {};
- gl.FilteredSearchTokenKeys = FilteredSearchTokenKeys;
-})();
+window.gl = window.gl || {};
+gl.FilteredSearchTokenKeys = FilteredSearchTokenKeys;
diff --git a/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js
index a2729dc0e95..2808e4b238a 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js
@@ -1,58 +1,56 @@
require('./filtered_search_token_keys');
-(() => {
- class FilteredSearchTokenizer {
- static processTokens(input) {
- const allowedKeys = gl.FilteredSearchTokenKeys.get().map(i => i.key);
- // Regex extracts `(token):(symbol)(value)`
- // Values that start with a double quote must end in a double quote (same for single)
- const tokenRegex = new RegExp(`(${allowedKeys.join('|')}):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\\S+))`, 'g');
- const tokens = [];
- const tokenIndexes = []; // stores key+value for simple search
- let lastToken = null;
- const searchToken = input.replace(tokenRegex, (match, key, symbol, v1, v2, v3) => {
- let tokenValue = v1 || v2 || v3;
- let tokenSymbol = symbol;
- let tokenIndex = '';
-
- if (tokenValue === '~' || tokenValue === '%' || tokenValue === '@') {
- tokenSymbol = tokenValue;
- tokenValue = '';
- }
-
- tokenIndex = `${key}:${tokenValue}`;
-
- // Prevent adding duplicates
- if (tokenIndexes.indexOf(tokenIndex) === -1) {
- tokenIndexes.push(tokenIndex);
-
- tokens.push({
- key,
- value: tokenValue || '',
- symbol: tokenSymbol || '',
- });
- }
-
- return '';
- }).replace(/\s{2,}/g, ' ').trim() || '';
-
- if (tokens.length > 0) {
- const last = tokens[tokens.length - 1];
- const lastString = `${last.key}:${last.symbol}${last.value}`;
- lastToken = input.lastIndexOf(lastString) ===
- input.length - lastString.length ? last : searchToken;
- } else {
- lastToken = searchToken;
+class FilteredSearchTokenizer {
+ static processTokens(input) {
+ const allowedKeys = gl.FilteredSearchTokenKeys.get().map(i => i.key);
+ // Regex extracts `(token):(symbol)(value)`
+ // Values that start with a double quote must end in a double quote (same for single)
+ const tokenRegex = new RegExp(`(${allowedKeys.join('|')}):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\\S+))`, 'g');
+ const tokens = [];
+ const tokenIndexes = []; // stores key+value for simple search
+ let lastToken = null;
+ const searchToken = input.replace(tokenRegex, (match, key, symbol, v1, v2, v3) => {
+ let tokenValue = v1 || v2 || v3;
+ let tokenSymbol = symbol;
+ let tokenIndex = '';
+
+ if (tokenValue === '~' || tokenValue === '%' || tokenValue === '@') {
+ tokenSymbol = tokenValue;
+ tokenValue = '';
}
- return {
- tokens,
- lastToken,
- searchToken,
- };
+ tokenIndex = `${key}:${tokenValue}`;
+
+ // Prevent adding duplicates
+ if (tokenIndexes.indexOf(tokenIndex) === -1) {
+ tokenIndexes.push(tokenIndex);
+
+ tokens.push({
+ key,
+ value: tokenValue || '',
+ symbol: tokenSymbol || '',
+ });
+ }
+
+ return '';
+ }).replace(/\s{2,}/g, ' ').trim() || '';
+
+ if (tokens.length > 0) {
+ const last = tokens[tokens.length - 1];
+ const lastString = `${last.key}:${last.symbol}${last.value}`;
+ lastToken = input.lastIndexOf(lastString) ===
+ input.length - lastString.length ? last : searchToken;
+ } else {
+ lastToken = searchToken;
}
+
+ return {
+ tokens,
+ lastToken,
+ searchToken,
+ };
}
+}
- window.gl = window.gl || {};
- gl.FilteredSearchTokenizer = FilteredSearchTokenizer;
-})();
+window.gl = window.gl || {};
+gl.FilteredSearchTokenizer = FilteredSearchTokenizer;
diff --git a/spec/javascripts/filtered_search/dropdown_user_spec.js b/spec/javascripts/filtered_search/dropdown_user_spec.js
index 2b1fe5e3eef..3f92fe4701e 100644
--- a/spec/javascripts/filtered_search/dropdown_user_spec.js
+++ b/spec/javascripts/filtered_search/dropdown_user_spec.js
@@ -3,69 +3,67 @@ require('~/filtered_search/filtered_search_tokenizer');
require('~/filtered_search/filtered_search_dropdown');
require('~/filtered_search/dropdown_user');
-(() => {
- describe('Dropdown User', () => {
- describe('getSearchInput', () => {
- let dropdownUser;
+describe('Dropdown User', () => {
+ describe('getSearchInput', () => {
+ let dropdownUser;
- beforeEach(() => {
- spyOn(gl.DropdownUser.prototype, 'bindEvents').and.callFake(() => {});
- spyOn(gl.DropdownUser.prototype, 'getProjectId').and.callFake(() => {});
- spyOn(gl.DropdownUtils, 'getSearchInput').and.callFake(() => {});
+ beforeEach(() => {
+ spyOn(gl.DropdownUser.prototype, 'bindEvents').and.callFake(() => {});
+ spyOn(gl.DropdownUser.prototype, 'getProjectId').and.callFake(() => {});
+ spyOn(gl.DropdownUtils, 'getSearchInput').and.callFake(() => {});
- dropdownUser = new gl.DropdownUser();
- });
-
- it('should not return the double quote found in value', () => {
- spyOn(gl.FilteredSearchTokenizer, 'processTokens').and.returnValue({
- lastToken: '"johnny appleseed',
- });
+ dropdownUser = new gl.DropdownUser();
+ });
- expect(dropdownUser.getSearchInput()).toBe('johnny appleseed');
+ it('should not return the double quote found in value', () => {
+ spyOn(gl.FilteredSearchTokenizer, 'processTokens').and.returnValue({
+ lastToken: '"johnny appleseed',
});
- it('should not return the single quote found in value', () => {
- spyOn(gl.FilteredSearchTokenizer, 'processTokens').and.returnValue({
- lastToken: '\'larry boy',
- });
+ expect(dropdownUser.getSearchInput()).toBe('johnny appleseed');
+ });
- expect(dropdownUser.getSearchInput()).toBe('larry boy');
+ it('should not return the single quote found in value', () => {
+ spyOn(gl.FilteredSearchTokenizer, 'processTokens').and.returnValue({
+ lastToken: '\'larry boy',
});
+
+ expect(dropdownUser.getSearchInput()).toBe('larry boy');
});
+ });
- describe('config AjaxFilter\'s endpoint', () => {
- beforeEach(() => {
- spyOn(gl.DropdownUser.prototype, 'bindEvents').and.callFake(() => {});
- spyOn(gl.DropdownUser.prototype, 'getProjectId').and.callFake(() => {});
- });
+ describe('config AjaxFilter\'s endpoint', () => {
+ beforeEach(() => {
+ spyOn(gl.DropdownUser.prototype, 'bindEvents').and.callFake(() => {});
+ spyOn(gl.DropdownUser.prototype, 'getProjectId').and.callFake(() => {});
+ });
- it('should return endpoint', () => {
- window.gon = {
- relative_url_root: '',
- };
- const dropdown = new gl.DropdownUser();
+ it('should return endpoint', () => {
+ window.gon = {
+ relative_url_root: '',
+ };
+ const dropdown = new gl.DropdownUser();
- expect(dropdown.config.AjaxFilter.endpoint).toBe('/autocomplete/users.json');
- });
+ expect(dropdown.config.AjaxFilter.endpoint).toBe('/autocomplete/users.json');
+ });
- it('should return endpoint when relative_url_root is undefined', () => {
- const dropdown = new gl.DropdownUser();
+ it('should return endpoint when relative_url_root is undefined', () => {
+ const dropdown = new gl.DropdownUser();
- expect(dropdown.config.AjaxFilter.endpoint).toBe('/autocomplete/users.json');
- });
+ expect(dropdown.config.AjaxFilter.endpoint).toBe('/autocomplete/users.json');
+ });
- it('should return endpoint with relative url when available', () => {
- window.gon = {
- relative_url_root: '/gitlab_directory',
- };
- const dropdown = new gl.DropdownUser();
+ it('should return endpoint with relative url when available', () => {
+ window.gon = {
+ relative_url_root: '/gitlab_directory',
+ };
+ const dropdown = new gl.DropdownUser();
- expect(dropdown.config.AjaxFilter.endpoint).toBe('/gitlab_directory/autocomplete/users.json');
- });
+ expect(dropdown.config.AjaxFilter.endpoint).toBe('/gitlab_directory/autocomplete/users.json');
+ });
- afterEach(() => {
- window.gon = {};
- });
+ afterEach(() => {
+ window.gon = {};
});
});
-})();
+});
diff --git a/spec/javascripts/filtered_search/dropdown_utils_spec.js b/spec/javascripts/filtered_search/dropdown_utils_spec.js
index e6538020896..c820c955172 100644
--- a/spec/javascripts/filtered_search/dropdown_utils_spec.js
+++ b/spec/javascripts/filtered_search/dropdown_utils_spec.js
@@ -3,308 +3,306 @@ require('~/filtered_search/dropdown_utils');
require('~/filtered_search/filtered_search_tokenizer');
require('~/filtered_search/filtered_search_dropdown_manager');
-(() => {
- describe('Dropdown Utils', () => {
- describe('getEscapedText', () => {
- it('should return same word when it has no space', () => {
- const escaped = gl.DropdownUtils.getEscapedText('textWithoutSpace');
- expect(escaped).toBe('textWithoutSpace');
- });
+describe('Dropdown Utils', () => {
+ describe('getEscapedText', () => {
+ it('should return same word when it has no space', () => {
+ const escaped = gl.DropdownUtils.getEscapedText('textWithoutSpace');
+ expect(escaped).toBe('textWithoutSpace');
+ });
- it('should escape with double quotes', () => {
- let escaped = gl.DropdownUtils.getEscapedText('text with space');
- expect(escaped).toBe('"text with space"');
+ it('should escape with double quotes', () => {
+ let escaped = gl.DropdownUtils.getEscapedText('text with space');
+ expect(escaped).toBe('"text with space"');
- escaped = gl.DropdownUtils.getEscapedText('won\'t fix');
- expect(escaped).toBe('"won\'t fix"');
- });
+ escaped = gl.DropdownUtils.getEscapedText('won\'t fix');
+ expect(escaped).toBe('"won\'t fix"');
+ });
- it('should escape with single quotes', () => {
- const escaped = gl.DropdownUtils.getEscapedText('won"t fix');
- expect(escaped).toBe('\'won"t fix\'');
- });
+ it('should escape with single quotes', () => {
+ const escaped = gl.DropdownUtils.getEscapedText('won"t fix');
+ expect(escaped).toBe('\'won"t fix\'');
+ });
- it('should escape with single quotes by default', () => {
- const escaped = gl.DropdownUtils.getEscapedText('won"t\' fix');
- expect(escaped).toBe('\'won"t\' fix\'');
- });
+ it('should escape with single quotes by default', () => {
+ const escaped = gl.DropdownUtils.getEscapedText('won"t\' fix');
+ expect(escaped).toBe('\'won"t\' fix\'');
});
+ });
- describe('filterWithSymbol', () => {
- let input;
- const item = {
- title: '@root',
- };
+ describe('filterWithSymbol', () => {
+ let input;
+ const item = {
+ title: '@root',
+ };
- beforeEach(() => {
- setFixtures(`
- <input type="text" id="test" />
- `);
+ beforeEach(() => {
+ setFixtures(`
+ <input type="text" id="test" />
+ `);
- input = document.getElementById('test');
- });
+ input = document.getElementById('test');
+ });
- it('should filter without symbol', () => {
- input.value = 'roo';
+ it('should filter without symbol', () => {
+ input.value = 'roo';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('@', input, item);
- expect(updatedItem.droplab_hidden).toBe(false);
- });
+ const updatedItem = gl.DropdownUtils.filterWithSymbol('@', input, item);
+ expect(updatedItem.droplab_hidden).toBe(false);
+ });
- it('should filter with symbol', () => {
- input.value = '@roo';
+ it('should filter with symbol', () => {
+ input.value = '@roo';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('@', input, item);
- expect(updatedItem.droplab_hidden).toBe(false);
- });
+ const updatedItem = gl.DropdownUtils.filterWithSymbol('@', input, item);
+ expect(updatedItem.droplab_hidden).toBe(false);
+ });
- describe('filters multiple word title', () => {
- const multipleWordItem = {
- title: 'Community Contributions',
- };
+ describe('filters multiple word title', () => {
+ const multipleWordItem = {
+ title: 'Community Contributions',
+ };
- it('should filter with double quote', () => {
- input.value = '"';
+ it('should filter with double quote', () => {
+ input.value = '"';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
- expect(updatedItem.droplab_hidden).toBe(false);
- });
+ const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+ expect(updatedItem.droplab_hidden).toBe(false);
+ });
- it('should filter with double quote and symbol', () => {
- input.value = '~"';
+ it('should filter with double quote and symbol', () => {
+ input.value = '~"';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
- expect(updatedItem.droplab_hidden).toBe(false);
- });
+ const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+ expect(updatedItem.droplab_hidden).toBe(false);
+ });
- it('should filter with double quote and multiple words', () => {
- input.value = '"community con';
+ it('should filter with double quote and multiple words', () => {
+ input.value = '"community con';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
- expect(updatedItem.droplab_hidden).toBe(false);
- });
+ const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+ expect(updatedItem.droplab_hidden).toBe(false);
+ });
- it('should filter with double quote, symbol and multiple words', () => {
- input.value = '~"community con';
+ it('should filter with double quote, symbol and multiple words', () => {
+ input.value = '~"community con';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
- expect(updatedItem.droplab_hidden).toBe(false);
- });
+ const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+ expect(updatedItem.droplab_hidden).toBe(false);
+ });
- it('should filter with single quote', () => {
- input.value = '\'';
+ it('should filter with single quote', () => {
+ input.value = '\'';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
- expect(updatedItem.droplab_hidden).toBe(false);
- });
+ const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+ expect(updatedItem.droplab_hidden).toBe(false);
+ });
- it('should filter with single quote and symbol', () => {
- input.value = '~\'';
+ it('should filter with single quote and symbol', () => {
+ input.value = '~\'';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
- expect(updatedItem.droplab_hidden).toBe(false);
- });
+ const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+ expect(updatedItem.droplab_hidden).toBe(false);
+ });
- it('should filter with single quote and multiple words', () => {
- input.value = '\'community con';
+ it('should filter with single quote and multiple words', () => {
+ input.value = '\'community con';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
- expect(updatedItem.droplab_hidden).toBe(false);
- });
+ const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+ expect(updatedItem.droplab_hidden).toBe(false);
+ });
- it('should filter with single quote, symbol and multiple words', () => {
- input.value = '~\'community con';
+ it('should filter with single quote, symbol and multiple words', () => {
+ input.value = '~\'community con';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
- expect(updatedItem.droplab_hidden).toBe(false);
- });
+ const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+ expect(updatedItem.droplab_hidden).toBe(false);
});
});
+ });
- describe('filterHint', () => {
- let input;
-
- beforeEach(() => {
- setFixtures(`
- <ul class="tokens-container">
- <li class="input-token">
- <input class="filtered-search" type="text" id="test" />
- </li>
- </ul>
- `);
-
- input = document.getElementById('test');
- });
+ describe('filterHint', () => {
+ let input;
- it('should filter', () => {
- input.value = 'l';
- let updatedItem = gl.DropdownUtils.filterHint(input, {
- hint: 'label',
- });
- expect(updatedItem.droplab_hidden).toBe(false);
+ beforeEach(() => {
+ setFixtures(`
+ <ul class="tokens-container">
+ <li class="input-token">
+ <input class="filtered-search" type="text" id="test" />
+ </li>
+ </ul>
+ `);
- input.value = 'o';
- updatedItem = gl.DropdownUtils.filterHint(input, {
- hint: 'label',
- });
- expect(updatedItem.droplab_hidden).toBe(true);
- });
+ input = document.getElementById('test');
+ });
- it('should return droplab_hidden false when item has no hint', () => {
- const updatedItem = gl.DropdownUtils.filterHint(input, {}, '');
- expect(updatedItem.droplab_hidden).toBe(false);
+ it('should filter', () => {
+ input.value = 'l';
+ let updatedItem = gl.DropdownUtils.filterHint(input, {
+ hint: 'label',
});
+ expect(updatedItem.droplab_hidden).toBe(false);
- it('should allow multiple if item.type is array', () => {
- input.value = 'label:~first la';
- const updatedItem = gl.DropdownUtils.filterHint(input, {
- hint: 'label',
- type: 'array',
- });
- expect(updatedItem.droplab_hidden).toBe(false);
+ input.value = 'o';
+ updatedItem = gl.DropdownUtils.filterHint(input, {
+ hint: 'label',
});
+ expect(updatedItem.droplab_hidden).toBe(true);
+ });
- it('should prevent multiple if item.type is not array', () => {
- input.value = 'milestone:~first mile';
- let updatedItem = gl.DropdownUtils.filterHint(input, {
- hint: 'milestone',
- });
- expect(updatedItem.droplab_hidden).toBe(true);
+ it('should return droplab_hidden false when item has no hint', () => {
+ const updatedItem = gl.DropdownUtils.filterHint(input, {}, '');
+ expect(updatedItem.droplab_hidden).toBe(false);
+ });
- updatedItem = gl.DropdownUtils.filterHint(input, {
- hint: 'milestone',
- type: 'string',
- });
- expect(updatedItem.droplab_hidden).toBe(true);
+ it('should allow multiple if item.type is array', () => {
+ input.value = 'label:~first la';
+ const updatedItem = gl.DropdownUtils.filterHint(input, {
+ hint: 'label',
+ type: 'array',
});
+ expect(updatedItem.droplab_hidden).toBe(false);
});
- describe('setDataValueIfSelected', () => {
- beforeEach(() => {
- spyOn(gl.FilteredSearchDropdownManager, 'addWordToInput')
- .and.callFake(() => {});
+ it('should prevent multiple if item.type is not array', () => {
+ input.value = 'milestone:~first mile';
+ let updatedItem = gl.DropdownUtils.filterHint(input, {
+ hint: 'milestone',
});
+ expect(updatedItem.droplab_hidden).toBe(true);
- it('calls addWordToInput when dataValue exists', () => {
- const selected = {
- getAttribute: () => 'value',
- };
-
- gl.DropdownUtils.setDataValueIfSelected(null, selected);
- expect(gl.FilteredSearchDropdownManager.addWordToInput.calls.count()).toEqual(1);
+ updatedItem = gl.DropdownUtils.filterHint(input, {
+ hint: 'milestone',
+ type: 'string',
});
+ expect(updatedItem.droplab_hidden).toBe(true);
+ });
+ });
- it('returns true when dataValue exists', () => {
- const selected = {
- getAttribute: () => 'value',
- };
+ describe('setDataValueIfSelected', () => {
+ beforeEach(() => {
+ spyOn(gl.FilteredSearchDropdownManager, 'addWordToInput')
+ .and.callFake(() => {});
+ });
- const result = gl.DropdownUtils.setDataValueIfSelected(null, selected);
- expect(result).toBe(true);
- });
+ it('calls addWordToInput when dataValue exists', () => {
+ const selected = {
+ getAttribute: () => 'value',
+ };
- it('returns false when dataValue does not exist', () => {
- const selected = {
- getAttribute: () => null,
- };
+ gl.DropdownUtils.setDataValueIfSelected(null, selected);
+ expect(gl.FilteredSearchDropdownManager.addWordToInput.calls.count()).toEqual(1);
+ });
- const result = gl.DropdownUtils.setDataValueIfSelected(null, selected);
- expect(result).toBe(false);
- });
+ it('returns true when dataValue exists', () => {
+ const selected = {
+ getAttribute: () => 'value',
+ };
+
+ const result = gl.DropdownUtils.setDataValueIfSelected(null, selected);
+ expect(result).toBe(true);
});
- describe('getInputSelectionPosition', () => {
- describe('word with trailing spaces', () => {
- const value = 'label:none ';
+ it('returns false when dataValue does not exist', () => {
+ const selected = {
+ getAttribute: () => null,
+ };
+
+ const result = gl.DropdownUtils.setDataValueIfSelected(null, selected);
+ expect(result).toBe(false);
+ });
+ });
- it('should return selectionStart when cursor is at the trailing space', () => {
- const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
- selectionStart: 11,
- value,
- });
+ describe('getInputSelectionPosition', () => {
+ describe('word with trailing spaces', () => {
+ const value = 'label:none ';
- expect(left).toBe(11);
- expect(right).toBe(11);
+ it('should return selectionStart when cursor is at the trailing space', () => {
+ const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
+ selectionStart: 11,
+ value,
});
- it('should return input when cursor is at the start of input', () => {
- const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
- selectionStart: 0,
- value,
- });
+ expect(left).toBe(11);
+ expect(right).toBe(11);
+ });
- expect(left).toBe(0);
- expect(right).toBe(10);
+ it('should return input when cursor is at the start of input', () => {
+ const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
+ selectionStart: 0,
+ value,
});
- it('should return input when cursor is at the middle of input', () => {
- const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
- selectionStart: 7,
- value,
- });
+ expect(left).toBe(0);
+ expect(right).toBe(10);
+ });
- expect(left).toBe(0);
- expect(right).toBe(10);
+ it('should return input when cursor is at the middle of input', () => {
+ const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
+ selectionStart: 7,
+ value,
});
- it('should return input when cursor is at the end of input', () => {
- const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
- selectionStart: 10,
- value,
- });
+ expect(left).toBe(0);
+ expect(right).toBe(10);
+ });
- expect(left).toBe(0);
- expect(right).toBe(10);
+ it('should return input when cursor is at the end of input', () => {
+ const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
+ selectionStart: 10,
+ value,
});
- });
- describe('multiple words', () => {
- const value = 'label:~"Community Contribution"';
+ expect(left).toBe(0);
+ expect(right).toBe(10);
+ });
+ });
- it('should return input when cursor is after the first word', () => {
- const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
- selectionStart: 17,
- value,
- });
+ describe('multiple words', () => {
+ const value = 'label:~"Community Contribution"';
- expect(left).toBe(0);
- expect(right).toBe(31);
+ it('should return input when cursor is after the first word', () => {
+ const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
+ selectionStart: 17,
+ value,
});
- it('should return input when cursor is before the second word', () => {
- const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
- selectionStart: 18,
- value,
- });
+ expect(left).toBe(0);
+ expect(right).toBe(31);
+ });
- expect(left).toBe(0);
- expect(right).toBe(31);
+ it('should return input when cursor is before the second word', () => {
+ const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
+ selectionStart: 18,
+ value,
});
- });
- describe('incomplete multiple words', () => {
- const value = 'label:~"Community Contribution';
+ expect(left).toBe(0);
+ expect(right).toBe(31);
+ });
+ });
- it('should return entire input when cursor is at the start of input', () => {
- const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
- selectionStart: 0,
- value,
- });
+ describe('incomplete multiple words', () => {
+ const value = 'label:~"Community Contribution';
- expect(left).toBe(0);
- expect(right).toBe(30);
+ it('should return entire input when cursor is at the start of input', () => {
+ const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
+ selectionStart: 0,
+ value,
});
- it('should return entire input when cursor is at the end of input', () => {
- const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
- selectionStart: 30,
- value,
- });
+ expect(left).toBe(0);
+ expect(right).toBe(30);
+ });
- expect(left).toBe(0);
- expect(right).toBe(30);
+ it('should return entire input when cursor is at the end of input', () => {
+ const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
+ selectionStart: 30,
+ value,
});
+
+ expect(left).toBe(0);
+ expect(right).toBe(30);
});
});
});
-})();
+});
diff --git a/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js
index a1da3396d7b..17bf8932489 100644
--- a/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js
@@ -3,99 +3,97 @@ require('~/filtered_search/filtered_search_visual_tokens');
require('~/filtered_search/filtered_search_tokenizer');
require('~/filtered_search/filtered_search_dropdown_manager');
-(() => {
- describe('Filtered Search Dropdown Manager', () => {
- describe('addWordToInput', () => {
- function getInputValue() {
- return document.querySelector('.filtered-search').value;
- }
-
- function setInputValue(value) {
- document.querySelector('.filtered-search').value = value;
- }
-
- beforeEach(() => {
- setFixtures(`
- <ul class="tokens-container">
- <li class="input-token">
- <input class="filtered-search">
- </li>
- </ul>
- `);
- });
+describe('Filtered Search Dropdown Manager', () => {
+ describe('addWordToInput', () => {
+ function getInputValue() {
+ return document.querySelector('.filtered-search').value;
+ }
+
+ function setInputValue(value) {
+ document.querySelector('.filtered-search').value = value;
+ }
+
+ beforeEach(() => {
+ setFixtures(`
+ <ul class="tokens-container">
+ <li class="input-token">
+ <input class="filtered-search">
+ </li>
+ </ul>
+ `);
+ });
- describe('input has no existing value', () => {
- it('should add just tokenName', () => {
- gl.FilteredSearchDropdownManager.addWordToInput('milestone');
+ describe('input has no existing value', () => {
+ it('should add just tokenName', () => {
+ gl.FilteredSearchDropdownManager.addWordToInput('milestone');
- const token = document.querySelector('.tokens-container .js-visual-token');
+ const token = document.querySelector('.tokens-container .js-visual-token');
- expect(token.classList.contains('filtered-search-token')).toEqual(true);
- expect(token.querySelector('.name').innerText).toBe('milestone');
- expect(getInputValue()).toBe('');
- });
+ expect(token.classList.contains('filtered-search-token')).toEqual(true);
+ expect(token.querySelector('.name').innerText).toBe('milestone');
+ expect(getInputValue()).toBe('');
+ });
- it('should add tokenName and tokenValue', () => {
- gl.FilteredSearchDropdownManager.addWordToInput('label');
+ it('should add tokenName and tokenValue', () => {
+ gl.FilteredSearchDropdownManager.addWordToInput('label');
- let token = document.querySelector('.tokens-container .js-visual-token');
+ let token = document.querySelector('.tokens-container .js-visual-token');
- expect(token.classList.contains('filtered-search-token')).toEqual(true);
- expect(token.querySelector('.name').innerText).toBe('label');
- expect(getInputValue()).toBe('');
+ expect(token.classList.contains('filtered-search-token')).toEqual(true);
+ expect(token.querySelector('.name').innerText).toBe('label');
+ expect(getInputValue()).toBe('');
- gl.FilteredSearchDropdownManager.addWordToInput('label', 'none');
- // We have to get that reference again
- // Because gl.FilteredSearchDropdownManager deletes the previous token
- token = document.querySelector('.tokens-container .js-visual-token');
+ gl.FilteredSearchDropdownManager.addWordToInput('label', 'none');
+ // We have to get that reference again
+ // Because gl.FilteredSearchDropdownManager deletes the previous token
+ token = document.querySelector('.tokens-container .js-visual-token');
- expect(token.classList.contains('filtered-search-token')).toEqual(true);
- expect(token.querySelector('.name').innerText).toBe('label');
- expect(token.querySelector('.value').innerText).toBe('none');
- expect(getInputValue()).toBe('');
- });
+ expect(token.classList.contains('filtered-search-token')).toEqual(true);
+ expect(token.querySelector('.name').innerText).toBe('label');
+ expect(token.querySelector('.value').innerText).toBe('none');
+ expect(getInputValue()).toBe('');
});
+ });
- describe('input has existing value', () => {
- it('should be able to just add tokenName', () => {
- setInputValue('a');
- gl.FilteredSearchDropdownManager.addWordToInput('author');
+ describe('input has existing value', () => {
+ it('should be able to just add tokenName', () => {
+ setInputValue('a');
+ gl.FilteredSearchDropdownManager.addWordToInput('author');
- const token = document.querySelector('.tokens-container .js-visual-token');
+ const token = document.querySelector('.tokens-container .js-visual-token');
- expect(token.classList.contains('filtered-search-token')).toEqual(true);
- expect(token.querySelector('.name').innerText).toBe('author');
- expect(getInputValue()).toBe('');
- });
+ expect(token.classList.contains('filtered-search-token')).toEqual(true);
+ expect(token.querySelector('.name').innerText).toBe('author');
+ expect(getInputValue()).toBe('');
+ });
- it('should replace tokenValue', () => {
- gl.FilteredSearchDropdownManager.addWordToInput('author');
+ it('should replace tokenValue', () => {
+ gl.FilteredSearchDropdownManager.addWordToInput('author');
- setInputValue('roo');
- gl.FilteredSearchDropdownManager.addWordToInput(null, '@root');
+ setInputValue('roo');
+ gl.FilteredSearchDropdownManager.addWordToInput(null, '@root');
- const token = document.querySelector('.tokens-container .js-visual-token');
+ const token = document.querySelector('.tokens-container .js-visual-token');
- expect(token.classList.contains('filtered-search-token')).toEqual(true);
- expect(token.querySelector('.name').innerText).toBe('author');
- expect(token.querySelector('.value').innerText).toBe('@root');
- expect(getInputValue()).toBe('');
- });
+ expect(token.classList.contains('filtered-search-token')).toEqual(true);
+ expect(token.querySelector('.name').innerText).toBe('author');
+ expect(token.querySelector('.value').innerText).toBe('@root');
+ expect(getInputValue()).toBe('');
+ });
- it('should add tokenValues containing spaces', () => {
- gl.FilteredSearchDropdownManager.addWordToInput('label');
+ it('should add tokenValues containing spaces', () => {
+ gl.FilteredSearchDropdownManager.addWordToInput('label');
- setInputValue('"test ');
- gl.FilteredSearchDropdownManager.addWordToInput('label', '~\'"test me"\'');
+ setInputValue('"test ');
+ gl.FilteredSearchDropdownManager.addWordToInput('label', '~\'"test me"\'');
- const token = document.querySelector('.tokens-container .js-visual-token');
+ const token = document.querySelector('.tokens-container .js-visual-token');
- expect(token.classList.contains('filtered-search-token')).toEqual(true);
- expect(token.querySelector('.name').innerText).toBe('label');
- expect(token.querySelector('.value').innerText).toBe('~\'"test me"\'');
- expect(getInputValue()).toBe('');
- });
+ expect(token.classList.contains('filtered-search-token')).toEqual(true);
+ expect(token.querySelector('.name').innerText).toBe('label');
+ expect(token.querySelector('.value').innerText).toBe('~\'"test me"\'');
+ expect(getInputValue()).toBe('');
});
});
});
-})();
+});
diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
index 97af681429b..6683489f63c 100644
--- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
@@ -6,271 +6,269 @@ 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', () => {
- let input;
- let manager;
- let tokensContainer;
- const placeholder = 'Search or filter results...';
-
- function dispatchBackspaceEvent(element, eventType) {
- const backspaceKey = 8;
- const event = new Event(eventType);
- event.keyCode = backspaceKey;
- element.dispatchEvent(event);
- }
+describe('Filtered Search Manager', () => {
+ let input;
+ let manager;
+ let tokensContainer;
+ const placeholder = 'Search or filter results...';
+
+ function dispatchBackspaceEvent(element, eventType) {
+ const backspaceKey = 8;
+ const event = new Event(eventType);
+ event.keyCode = backspaceKey;
+ element.dispatchEvent(event);
+ }
+
+ function dispatchDeleteEvent(element, eventType) {
+ const deleteKey = 46;
+ const event = new Event(eventType);
+ event.keyCode = deleteKey;
+ element.dispatchEvent(event);
+ }
+
+ beforeEach(() => {
+ setFixtures(`
+ <div class="filtered-search-box">
+ <form>
+ <ul class="tokens-container list-unstyled">
+ ${FilteredSearchSpecHelper.createInputHTML(placeholder)}
+ </ul>
+ <button class="clear-search" type="button">
+ <i class="fa fa-times"></i>
+ </button>
+ </form>
+ </div>
+ `);
+
+ 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);
+ spyOn(gl.FilteredSearchVisualTokens, 'unselectTokens').and.callThrough();
+
+ input = document.querySelector('.filtered-search');
+ tokensContainer = document.querySelector('.tokens-container');
+ manager = new gl.FilteredSearchManager();
+ });
- function dispatchDeleteEvent(element, eventType) {
- const deleteKey = 46;
- const event = new Event(eventType);
- event.keyCode = deleteKey;
- element.dispatchEvent(event);
- }
+ afterEach(() => {
+ manager.cleanup();
+ });
- beforeEach(() => {
- setFixtures(`
- <div class="filtered-search-box">
- <form>
- <ul class="tokens-container list-unstyled">
- ${FilteredSearchSpecHelper.createInputHTML(placeholder)}
- </ul>
- <button class="clear-search" type="button">
- <i class="fa fa-times"></i>
- </button>
- </form>
- </div>
- `);
+ describe('search', () => {
+ const defaultParams = '?scope=all&utf8=%E2%9C%93&state=opened';
- 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);
- spyOn(gl.FilteredSearchVisualTokens, 'unselectTokens').and.callThrough();
+ it('should search with a single word', (done) => {
+ input.value = 'searchTerm';
- input = document.querySelector('.filtered-search');
- tokensContainer = document.querySelector('.tokens-container');
- manager = new gl.FilteredSearchManager();
- });
+ spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
+ expect(url).toEqual(`${defaultParams}&search=searchTerm`);
+ done();
+ });
- afterEach(() => {
- manager.cleanup();
+ manager.search();
});
- describe('search', () => {
- const defaultParams = '?scope=all&utf8=%E2%9C%93&state=opened';
-
- it('should search with a single word', (done) => {
- input.value = 'searchTerm';
+ it('should search with multiple words', (done) => {
+ input.value = 'awesome search terms';
- spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
- expect(url).toEqual(`${defaultParams}&search=searchTerm`);
- done();
- });
-
- manager.search();
+ spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
+ expect(url).toEqual(`${defaultParams}&search=awesome+search+terms`);
+ done();
});
- it('should search with multiple words', (done) => {
- input.value = 'awesome search terms';
+ manager.search();
+ });
- spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
- expect(url).toEqual(`${defaultParams}&search=awesome+search+terms`);
- done();
- });
+ it('should search with special characters', (done) => {
+ input.value = '~!@#$%^&*()_+{}:<>,.?/';
- manager.search();
+ spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
+ expect(url).toEqual(`${defaultParams}&search=~!%40%23%24%25%5E%26*()_%2B%7B%7D%3A%3C%3E%2C.%3F%2F`);
+ done();
});
- it('should search with special characters', (done) => {
- input.value = '~!@#$%^&*()_+{}:<>,.?/';
+ manager.search();
+ });
- spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
- expect(url).toEqual(`${defaultParams}&search=~!%40%23%24%25%5E%26*()_%2B%7B%7D%3A%3C%3E%2C.%3F%2F`);
- done();
- });
+ it('removes duplicated tokens', (done) => {
+ tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(`
+ ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug')}
+ ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug')}
+ `);
- manager.search();
+ spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
+ expect(url).toEqual(`${defaultParams}&label_name[]=bug`);
+ done();
});
- it('removes duplicated tokens', (done) => {
- tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(`
- ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug')}
- ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug')}
- `);
-
- spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
- expect(url).toEqual(`${defaultParams}&label_name[]=bug`);
- done();
- });
+ manager.search();
+ });
+ });
- manager.search();
- });
+ describe('handleInputPlaceholder', () => {
+ it('should render placeholder when there is no input', () => {
+ expect(input.placeholder).toEqual(placeholder);
});
- describe('handleInputPlaceholder', () => {
- it('should render placeholder when there is no input', () => {
- expect(input.placeholder).toEqual(placeholder);
- });
+ it('should not render placeholder when there is input', () => {
+ input.value = 'test words';
+
+ const event = new Event('input');
+ input.dispatchEvent(event);
- it('should not render placeholder when there is input', () => {
- input.value = 'test words';
+ expect(input.placeholder).toEqual('');
+ });
- const event = new Event('input');
- input.dispatchEvent(event);
+ it('should not render placeholder when there are tokens and no input', () => {
+ tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
+ FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug'),
+ );
- expect(input.placeholder).toEqual('');
- });
+ const event = new Event('input');
+ input.dispatchEvent(event);
- it('should not render placeholder when there are tokens and no input', () => {
+ expect(input.placeholder).toEqual('');
+ });
+ });
+
+ describe('checkForBackspace', () => {
+ describe('tokens and no input', () => {
+ beforeEach(() => {
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug'),
);
-
- const event = new Event('input');
- input.dispatchEvent(event);
-
- expect(input.placeholder).toEqual('');
});
- });
-
- describe('checkForBackspace', () => {
- describe('tokens and no input', () => {
- beforeEach(() => {
- tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
- FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug'),
- );
- });
- it('removes last token', () => {
- spyOn(gl.FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough();
- dispatchBackspaceEvent(input, 'keyup');
-
- expect(gl.FilteredSearchVisualTokens.removeLastTokenPartial).toHaveBeenCalled();
- });
-
- it('sets the input', () => {
- spyOn(gl.FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough();
- dispatchDeleteEvent(input, 'keyup');
+ it('removes last token', () => {
+ spyOn(gl.FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough();
+ dispatchBackspaceEvent(input, 'keyup');
- expect(gl.FilteredSearchVisualTokens.getLastTokenPartial).toHaveBeenCalled();
- expect(input.value).toEqual('~bug');
- });
+ expect(gl.FilteredSearchVisualTokens.removeLastTokenPartial).toHaveBeenCalled();
});
- it('does not remove token or change input when there is existing input', () => {
- spyOn(gl.FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough();
+ it('sets the input', () => {
spyOn(gl.FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough();
-
- input.value = 'text';
dispatchDeleteEvent(input, 'keyup');
- expect(gl.FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled();
- expect(gl.FilteredSearchVisualTokens.getLastTokenPartial).not.toHaveBeenCalled();
- expect(input.value).toEqual('text');
+ expect(gl.FilteredSearchVisualTokens.getLastTokenPartial).toHaveBeenCalled();
+ expect(input.value).toEqual('~bug');
});
});
- describe('removeSelectedToken', () => {
- function getVisualTokens() {
- return tokensContainer.querySelectorAll('.js-visual-token');
- }
+ 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();
- beforeEach(() => {
- tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
- FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', 'none', true),
- );
- });
+ input.value = 'text';
+ dispatchDeleteEvent(input, 'keyup');
- it('removes selected token when the backspace key is pressed', () => {
- expect(getVisualTokens().length).toEqual(1);
+ expect(gl.FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled();
+ expect(gl.FilteredSearchVisualTokens.getLastTokenPartial).not.toHaveBeenCalled();
+ expect(input.value).toEqual('text');
+ });
+ });
- dispatchBackspaceEvent(document, 'keydown');
+ describe('removeSelectedToken', () => {
+ function getVisualTokens() {
+ return tokensContainer.querySelectorAll('.js-visual-token');
+ }
- expect(getVisualTokens().length).toEqual(0);
- });
+ beforeEach(() => {
+ tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
+ FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', 'none', true),
+ );
+ });
- it('removes selected token when the delete key is pressed', () => {
- expect(getVisualTokens().length).toEqual(1);
+ it('removes selected token when the backspace key is pressed', () => {
+ expect(getVisualTokens().length).toEqual(1);
- dispatchDeleteEvent(document, 'keydown');
+ dispatchBackspaceEvent(document, 'keydown');
- expect(getVisualTokens().length).toEqual(0);
- });
+ expect(getVisualTokens().length).toEqual(0);
+ });
- it('updates the input placeholder after removal', () => {
- manager.handleInputPlaceholder();
+ it('removes selected token when the delete key is pressed', () => {
+ expect(getVisualTokens().length).toEqual(1);
- expect(input.placeholder).toEqual('');
- expect(getVisualTokens().length).toEqual(1);
+ dispatchDeleteEvent(document, 'keydown');
- dispatchBackspaceEvent(document, 'keydown');
+ expect(getVisualTokens().length).toEqual(0);
+ });
- expect(input.placeholder).not.toEqual('');
- expect(getVisualTokens().length).toEqual(0);
- });
+ it('updates the input placeholder after removal', () => {
+ manager.handleInputPlaceholder();
- it('updates the clear button after removal', () => {
- manager.toggleClearSearchButton();
+ expect(input.placeholder).toEqual('');
+ expect(getVisualTokens().length).toEqual(1);
- const clearButton = document.querySelector('.clear-search');
+ dispatchBackspaceEvent(document, 'keydown');
- expect(clearButton.classList.contains('hidden')).toEqual(false);
- expect(getVisualTokens().length).toEqual(1);
+ expect(input.placeholder).not.toEqual('');
+ expect(getVisualTokens().length).toEqual(0);
+ });
- dispatchBackspaceEvent(document, 'keydown');
+ it('updates the clear button after removal', () => {
+ manager.toggleClearSearchButton();
- expect(clearButton.classList.contains('hidden')).toEqual(true);
- expect(getVisualTokens().length).toEqual(0);
- });
+ const clearButton = document.querySelector('.clear-search');
+
+ expect(clearButton.classList.contains('hidden')).toEqual(false);
+ expect(getVisualTokens().length).toEqual(1);
+
+ dispatchBackspaceEvent(document, 'keydown');
+
+ expect(clearButton.classList.contains('hidden')).toEqual(true);
+ expect(getVisualTokens().length).toEqual(0);
});
+ });
- describe('unselects token', () => {
- beforeEach(() => {
- tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(`
- ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug', true)}
- ${FilteredSearchSpecHelper.createSearchVisualTokenHTML('search term')}
- ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~awesome')}
- `);
- });
+ describe('unselects token', () => {
+ beforeEach(() => {
+ tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(`
+ ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug', true)}
+ ${FilteredSearchSpecHelper.createSearchVisualTokenHTML('search term')}
+ ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~awesome')}
+ `);
+ });
- it('unselects token when input is clicked', () => {
- const selectedToken = tokensContainer.querySelector('.js-visual-token .selected');
+ it('unselects token when input is clicked', () => {
+ const selectedToken = tokensContainer.querySelector('.js-visual-token .selected');
- expect(selectedToken.classList.contains('selected')).toEqual(true);
- expect(gl.FilteredSearchVisualTokens.unselectTokens).not.toHaveBeenCalled();
+ expect(selectedToken.classList.contains('selected')).toEqual(true);
+ expect(gl.FilteredSearchVisualTokens.unselectTokens).not.toHaveBeenCalled();
- // Click directly on input attached to document
- // so that the click event will propagate properly
- document.querySelector('.filtered-search').click();
+ // Click directly on input attached to document
+ // so that the click event will propagate properly
+ document.querySelector('.filtered-search').click();
- expect(gl.FilteredSearchVisualTokens.unselectTokens).toHaveBeenCalled();
- expect(selectedToken.classList.contains('selected')).toEqual(false);
- });
+ expect(gl.FilteredSearchVisualTokens.unselectTokens).toHaveBeenCalled();
+ expect(selectedToken.classList.contains('selected')).toEqual(false);
+ });
- it('unselects token when document.body is clicked', () => {
- const selectedToken = tokensContainer.querySelector('.js-visual-token .selected');
+ it('unselects token when document.body is clicked', () => {
+ const selectedToken = tokensContainer.querySelector('.js-visual-token .selected');
- expect(selectedToken.classList.contains('selected')).toEqual(true);
- expect(gl.FilteredSearchVisualTokens.unselectTokens).not.toHaveBeenCalled();
+ expect(selectedToken.classList.contains('selected')).toEqual(true);
+ expect(gl.FilteredSearchVisualTokens.unselectTokens).not.toHaveBeenCalled();
- document.body.click();
+ document.body.click();
- expect(selectedToken.classList.contains('selected')).toEqual(false);
- expect(gl.FilteredSearchVisualTokens.unselectTokens).toHaveBeenCalled();
- });
+ expect(selectedToken.classList.contains('selected')).toEqual(false);
+ expect(gl.FilteredSearchVisualTokens.unselectTokens).toHaveBeenCalled();
});
+ });
- describe('toggleInputContainerFocus', () => {
- it('toggles on focus', () => {
- input.focus();
- expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual(true);
- });
+ describe('toggleInputContainerFocus', () => {
+ it('toggles on focus', () => {
+ input.focus();
+ expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual(true);
+ });
- it('toggles on blur', () => {
- input.blur();
- expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual(false);
- });
+ it('toggles on blur', () => {
+ input.blur();
+ expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual(false);
});
});
-})();
+});
diff --git a/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js b/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js
index cf409a7e509..6f9fa434c35 100644
--- a/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js
@@ -1,110 +1,108 @@
require('~/extensions/array');
require('~/filtered_search/filtered_search_token_keys');
-(() => {
- describe('Filtered Search Token Keys', () => {
- describe('get', () => {
- let tokenKeys;
-
- beforeEach(() => {
- tokenKeys = gl.FilteredSearchTokenKeys.get();
- });
-
- it('should return tokenKeys', () => {
- expect(tokenKeys !== null).toBe(true);
- });
-
- it('should return tokenKeys as an array', () => {
- expect(tokenKeys instanceof Array).toBe(true);
- });
- });
-
- describe('getConditions', () => {
- let conditions;
-
- beforeEach(() => {
- conditions = gl.FilteredSearchTokenKeys.getConditions();
- });
-
- it('should return conditions', () => {
- expect(conditions !== null).toBe(true);
- });
-
- it('should return conditions as an array', () => {
- expect(conditions instanceof Array).toBe(true);
- });
- });
-
- describe('searchByKey', () => {
- it('should return null when key not found', () => {
- const tokenKey = gl.FilteredSearchTokenKeys.searchByKey('notakey');
- expect(tokenKey === null).toBe(true);
- });
-
- it('should return tokenKey when found by key', () => {
- const tokenKeys = gl.FilteredSearchTokenKeys.get();
- const result = gl.FilteredSearchTokenKeys.searchByKey(tokenKeys[0].key);
- expect(result).toEqual(tokenKeys[0]);
- });
- });
-
- describe('searchBySymbol', () => {
- it('should return null when symbol not found', () => {
- const tokenKey = gl.FilteredSearchTokenKeys.searchBySymbol('notasymbol');
- expect(tokenKey === null).toBe(true);
- });
-
- it('should return tokenKey when found by symbol', () => {
- const tokenKeys = gl.FilteredSearchTokenKeys.get();
- const result = gl.FilteredSearchTokenKeys.searchBySymbol(tokenKeys[0].symbol);
- expect(result).toEqual(tokenKeys[0]);
- });
- });
-
- describe('searchByKeyParam', () => {
- it('should return null when key param not found', () => {
- const tokenKey = gl.FilteredSearchTokenKeys.searchByKeyParam('notakeyparam');
- expect(tokenKey === null).toBe(true);
- });
-
- it('should return tokenKey when found by key param', () => {
- const tokenKeys = gl.FilteredSearchTokenKeys.get();
- const result = gl.FilteredSearchTokenKeys.searchByKeyParam(`${tokenKeys[0].key}_${tokenKeys[0].param}`);
- expect(result).toEqual(tokenKeys[0]);
- });
-
- it('should return alternative tokenKey when found by key param', () => {
- const tokenKeys = gl.FilteredSearchTokenKeys.getAlternatives();
- const result = gl.FilteredSearchTokenKeys.searchByKeyParam(`${tokenKeys[0].key}_${tokenKeys[0].param}`);
- expect(result).toEqual(tokenKeys[0]);
- });
- });
-
- describe('searchByConditionUrl', () => {
- it('should return null when condition url not found', () => {
- const condition = gl.FilteredSearchTokenKeys.searchByConditionUrl(null);
- expect(condition === null).toBe(true);
- });
-
- it('should return condition when found by url', () => {
- const conditions = gl.FilteredSearchTokenKeys.getConditions();
- const result = gl.FilteredSearchTokenKeys.searchByConditionUrl(conditions[0].url);
- expect(result).toBe(conditions[0]);
- });
- });
-
- describe('searchByConditionKeyValue', () => {
- it('should return null when condition tokenKey and value not found', () => {
- const condition = gl.FilteredSearchTokenKeys.searchByConditionKeyValue(null, null);
- expect(condition === null).toBe(true);
- });
-
- it('should return condition when found by tokenKey and value', () => {
- const conditions = gl.FilteredSearchTokenKeys.getConditions();
- const result = gl.FilteredSearchTokenKeys
- .searchByConditionKeyValue(conditions[0].tokenKey, conditions[0].value);
- expect(result).toEqual(conditions[0]);
- });
+describe('Filtered Search Token Keys', () => {
+ describe('get', () => {
+ let tokenKeys;
+
+ beforeEach(() => {
+ tokenKeys = gl.FilteredSearchTokenKeys.get();
+ });
+
+ it('should return tokenKeys', () => {
+ expect(tokenKeys !== null).toBe(true);
+ });
+
+ it('should return tokenKeys as an array', () => {
+ expect(tokenKeys instanceof Array).toBe(true);
+ });
+ });
+
+ describe('getConditions', () => {
+ let conditions;
+
+ beforeEach(() => {
+ conditions = gl.FilteredSearchTokenKeys.getConditions();
+ });
+
+ it('should return conditions', () => {
+ expect(conditions !== null).toBe(true);
+ });
+
+ it('should return conditions as an array', () => {
+ expect(conditions instanceof Array).toBe(true);
+ });
+ });
+
+ describe('searchByKey', () => {
+ it('should return null when key not found', () => {
+ const tokenKey = gl.FilteredSearchTokenKeys.searchByKey('notakey');
+ expect(tokenKey === null).toBe(true);
+ });
+
+ it('should return tokenKey when found by key', () => {
+ const tokenKeys = gl.FilteredSearchTokenKeys.get();
+ const result = gl.FilteredSearchTokenKeys.searchByKey(tokenKeys[0].key);
+ expect(result).toEqual(tokenKeys[0]);
+ });
+ });
+
+ describe('searchBySymbol', () => {
+ it('should return null when symbol not found', () => {
+ const tokenKey = gl.FilteredSearchTokenKeys.searchBySymbol('notasymbol');
+ expect(tokenKey === null).toBe(true);
+ });
+
+ it('should return tokenKey when found by symbol', () => {
+ const tokenKeys = gl.FilteredSearchTokenKeys.get();
+ const result = gl.FilteredSearchTokenKeys.searchBySymbol(tokenKeys[0].symbol);
+ expect(result).toEqual(tokenKeys[0]);
+ });
+ });
+
+ describe('searchByKeyParam', () => {
+ it('should return null when key param not found', () => {
+ const tokenKey = gl.FilteredSearchTokenKeys.searchByKeyParam('notakeyparam');
+ expect(tokenKey === null).toBe(true);
+ });
+
+ it('should return tokenKey when found by key param', () => {
+ const tokenKeys = gl.FilteredSearchTokenKeys.get();
+ const result = gl.FilteredSearchTokenKeys.searchByKeyParam(`${tokenKeys[0].key}_${tokenKeys[0].param}`);
+ expect(result).toEqual(tokenKeys[0]);
+ });
+
+ it('should return alternative tokenKey when found by key param', () => {
+ const tokenKeys = gl.FilteredSearchTokenKeys.getAlternatives();
+ const result = gl.FilteredSearchTokenKeys.searchByKeyParam(`${tokenKeys[0].key}_${tokenKeys[0].param}`);
+ expect(result).toEqual(tokenKeys[0]);
+ });
+ });
+
+ describe('searchByConditionUrl', () => {
+ it('should return null when condition url not found', () => {
+ const condition = gl.FilteredSearchTokenKeys.searchByConditionUrl(null);
+ expect(condition === null).toBe(true);
+ });
+
+ it('should return condition when found by url', () => {
+ const conditions = gl.FilteredSearchTokenKeys.getConditions();
+ const result = gl.FilteredSearchTokenKeys.searchByConditionUrl(conditions[0].url);
+ expect(result).toBe(conditions[0]);
+ });
+ });
+
+ describe('searchByConditionKeyValue', () => {
+ it('should return null when condition tokenKey and value not found', () => {
+ const condition = gl.FilteredSearchTokenKeys.searchByConditionKeyValue(null, null);
+ expect(condition === null).toBe(true);
+ });
+
+ it('should return condition when found by tokenKey and value', () => {
+ const conditions = gl.FilteredSearchTokenKeys.getConditions();
+ const result = gl.FilteredSearchTokenKeys
+ .searchByConditionKeyValue(conditions[0].tokenKey, conditions[0].value);
+ expect(result).toEqual(conditions[0]);
});
});
-})();
+});
diff --git a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js b/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js
index cabbc694ec4..3e2e577f115 100644
--- a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js
@@ -2,134 +2,132 @@ require('~/extensions/array');
require('~/filtered_search/filtered_search_token_keys');
require('~/filtered_search/filtered_search_tokenizer');
-(() => {
- describe('Filtered Search Tokenizer', () => {
- describe('processTokens', () => {
- it('returns for input containing only search value', () => {
- const results = gl.FilteredSearchTokenizer.processTokens('searchTerm');
- expect(results.searchToken).toBe('searchTerm');
- expect(results.tokens.length).toBe(0);
- expect(results.lastToken).toBe(results.searchToken);
- });
-
- it('returns for input containing only tokens', () => {
- const results = gl.FilteredSearchTokenizer
- .processTokens('author:@root label:~"Very Important" milestone:%v1.0 assignee:none');
- expect(results.searchToken).toBe('');
- expect(results.tokens.length).toBe(4);
- expect(results.tokens[3]).toBe(results.lastToken);
-
- expect(results.tokens[0].key).toBe('author');
- expect(results.tokens[0].value).toBe('root');
- expect(results.tokens[0].symbol).toBe('@');
-
- expect(results.tokens[1].key).toBe('label');
- expect(results.tokens[1].value).toBe('"Very Important"');
- expect(results.tokens[1].symbol).toBe('~');
-
- expect(results.tokens[2].key).toBe('milestone');
- expect(results.tokens[2].value).toBe('v1.0');
- expect(results.tokens[2].symbol).toBe('%');
-
- expect(results.tokens[3].key).toBe('assignee');
- expect(results.tokens[3].value).toBe('none');
- expect(results.tokens[3].symbol).toBe('');
- });
-
- it('returns for input starting with search value and ending with tokens', () => {
- const results = gl.FilteredSearchTokenizer
- .processTokens('searchTerm anotherSearchTerm milestone:none');
- expect(results.searchToken).toBe('searchTerm anotherSearchTerm');
- expect(results.tokens.length).toBe(1);
- expect(results.tokens[0]).toBe(results.lastToken);
- expect(results.tokens[0].key).toBe('milestone');
- expect(results.tokens[0].value).toBe('none');
- expect(results.tokens[0].symbol).toBe('');
- });
-
- it('returns for input starting with tokens and ending with search value', () => {
- const results = gl.FilteredSearchTokenizer
- .processTokens('assignee:@user searchTerm');
-
- expect(results.searchToken).toBe('searchTerm');
- expect(results.tokens.length).toBe(1);
- expect(results.tokens[0].key).toBe('assignee');
- expect(results.tokens[0].value).toBe('user');
- expect(results.tokens[0].symbol).toBe('@');
- expect(results.lastToken).toBe(results.searchToken);
- });
-
- it('returns for input containing search value wrapped between tokens', () => {
- const results = gl.FilteredSearchTokenizer
- .processTokens('author:@root label:~"Won\'t fix" searchTerm anotherSearchTerm milestone:none');
-
- expect(results.searchToken).toBe('searchTerm anotherSearchTerm');
- expect(results.tokens.length).toBe(3);
- expect(results.tokens[2]).toBe(results.lastToken);
-
- expect(results.tokens[0].key).toBe('author');
- expect(results.tokens[0].value).toBe('root');
- expect(results.tokens[0].symbol).toBe('@');
-
- expect(results.tokens[1].key).toBe('label');
- expect(results.tokens[1].value).toBe('"Won\'t fix"');
- expect(results.tokens[1].symbol).toBe('~');
-
- expect(results.tokens[2].key).toBe('milestone');
- expect(results.tokens[2].value).toBe('none');
- expect(results.tokens[2].symbol).toBe('');
- });
-
- it('returns for input containing search value in between tokens', () => {
- const results = gl.FilteredSearchTokenizer
- .processTokens('author:@root searchTerm assignee:none anotherSearchTerm label:~Doing');
- expect(results.searchToken).toBe('searchTerm anotherSearchTerm');
- expect(results.tokens.length).toBe(3);
- expect(results.tokens[2]).toBe(results.lastToken);
-
- expect(results.tokens[0].key).toBe('author');
- expect(results.tokens[0].value).toBe('root');
- expect(results.tokens[0].symbol).toBe('@');
-
- expect(results.tokens[1].key).toBe('assignee');
- expect(results.tokens[1].value).toBe('none');
- expect(results.tokens[1].symbol).toBe('');
-
- expect(results.tokens[2].key).toBe('label');
- expect(results.tokens[2].value).toBe('Doing');
- expect(results.tokens[2].symbol).toBe('~');
- });
-
- it('returns search value for invalid tokens', () => {
- const results = gl.FilteredSearchTokenizer.processTokens('fake:token');
- expect(results.lastToken).toBe('fake:token');
- expect(results.searchToken).toBe('fake:token');
- expect(results.tokens.length).toEqual(0);
- });
-
- it('returns search value and token for mix of valid and invalid tokens', () => {
- const results = gl.FilteredSearchTokenizer.processTokens('label:real fake:token');
- expect(results.tokens.length).toEqual(1);
- expect(results.tokens[0].key).toBe('label');
- expect(results.tokens[0].value).toBe('real');
- expect(results.tokens[0].symbol).toBe('');
- expect(results.lastToken).toBe('fake:token');
- expect(results.searchToken).toBe('fake:token');
- });
-
- it('returns search value for invalid symbols', () => {
- const results = gl.FilteredSearchTokenizer.processTokens('std::includes');
- expect(results.lastToken).toBe('std::includes');
- expect(results.searchToken).toBe('std::includes');
- });
-
- it('removes duplicated values', () => {
- const results = gl.FilteredSearchTokenizer.processTokens('label:~foo label:~foo');
- expect(results.tokens.length).toBe(1);
- expect(results.tokens[0].key).toBe('label');
- expect(results.tokens[0].value).toBe('foo');
- expect(results.tokens[0].symbol).toBe('~');
- });
+describe('Filtered Search Tokenizer', () => {
+ describe('processTokens', () => {
+ it('returns for input containing only search value', () => {
+ const results = gl.FilteredSearchTokenizer.processTokens('searchTerm');
+ expect(results.searchToken).toBe('searchTerm');
+ expect(results.tokens.length).toBe(0);
+ expect(results.lastToken).toBe(results.searchToken);
+ });
+
+ it('returns for input containing only tokens', () => {
+ const results = gl.FilteredSearchTokenizer
+ .processTokens('author:@root label:~"Very Important" milestone:%v1.0 assignee:none');
+ expect(results.searchToken).toBe('');
+ expect(results.tokens.length).toBe(4);
+ expect(results.tokens[3]).toBe(results.lastToken);
+
+ expect(results.tokens[0].key).toBe('author');
+ expect(results.tokens[0].value).toBe('root');
+ expect(results.tokens[0].symbol).toBe('@');
+
+ expect(results.tokens[1].key).toBe('label');
+ expect(results.tokens[1].value).toBe('"Very Important"');
+ expect(results.tokens[1].symbol).toBe('~');
+
+ expect(results.tokens[2].key).toBe('milestone');
+ expect(results.tokens[2].value).toBe('v1.0');
+ expect(results.tokens[2].symbol).toBe('%');
+
+ expect(results.tokens[3].key).toBe('assignee');
+ expect(results.tokens[3].value).toBe('none');
+ expect(results.tokens[3].symbol).toBe('');
+ });
+
+ it('returns for input starting with search value and ending with tokens', () => {
+ const results = gl.FilteredSearchTokenizer
+ .processTokens('searchTerm anotherSearchTerm milestone:none');
+ expect(results.searchToken).toBe('searchTerm anotherSearchTerm');
+ expect(results.tokens.length).toBe(1);
+ expect(results.tokens[0]).toBe(results.lastToken);
+ expect(results.tokens[0].key).toBe('milestone');
+ expect(results.tokens[0].value).toBe('none');
+ expect(results.tokens[0].symbol).toBe('');
+ });
+
+ it('returns for input starting with tokens and ending with search value', () => {
+ const results = gl.FilteredSearchTokenizer
+ .processTokens('assignee:@user searchTerm');
+
+ expect(results.searchToken).toBe('searchTerm');
+ expect(results.tokens.length).toBe(1);
+ expect(results.tokens[0].key).toBe('assignee');
+ expect(results.tokens[0].value).toBe('user');
+ expect(results.tokens[0].symbol).toBe('@');
+ expect(results.lastToken).toBe(results.searchToken);
+ });
+
+ it('returns for input containing search value wrapped between tokens', () => {
+ const results = gl.FilteredSearchTokenizer
+ .processTokens('author:@root label:~"Won\'t fix" searchTerm anotherSearchTerm milestone:none');
+
+ expect(results.searchToken).toBe('searchTerm anotherSearchTerm');
+ expect(results.tokens.length).toBe(3);
+ expect(results.tokens[2]).toBe(results.lastToken);
+
+ expect(results.tokens[0].key).toBe('author');
+ expect(results.tokens[0].value).toBe('root');
+ expect(results.tokens[0].symbol).toBe('@');
+
+ expect(results.tokens[1].key).toBe('label');
+ expect(results.tokens[1].value).toBe('"Won\'t fix"');
+ expect(results.tokens[1].symbol).toBe('~');
+
+ expect(results.tokens[2].key).toBe('milestone');
+ expect(results.tokens[2].value).toBe('none');
+ expect(results.tokens[2].symbol).toBe('');
+ });
+
+ it('returns for input containing search value in between tokens', () => {
+ const results = gl.FilteredSearchTokenizer
+ .processTokens('author:@root searchTerm assignee:none anotherSearchTerm label:~Doing');
+ expect(results.searchToken).toBe('searchTerm anotherSearchTerm');
+ expect(results.tokens.length).toBe(3);
+ expect(results.tokens[2]).toBe(results.lastToken);
+
+ expect(results.tokens[0].key).toBe('author');
+ expect(results.tokens[0].value).toBe('root');
+ expect(results.tokens[0].symbol).toBe('@');
+
+ expect(results.tokens[1].key).toBe('assignee');
+ expect(results.tokens[1].value).toBe('none');
+ expect(results.tokens[1].symbol).toBe('');
+
+ expect(results.tokens[2].key).toBe('label');
+ expect(results.tokens[2].value).toBe('Doing');
+ expect(results.tokens[2].symbol).toBe('~');
+ });
+
+ it('returns search value for invalid tokens', () => {
+ const results = gl.FilteredSearchTokenizer.processTokens('fake:token');
+ expect(results.lastToken).toBe('fake:token');
+ expect(results.searchToken).toBe('fake:token');
+ expect(results.tokens.length).toEqual(0);
+ });
+
+ it('returns search value and token for mix of valid and invalid tokens', () => {
+ const results = gl.FilteredSearchTokenizer.processTokens('label:real fake:token');
+ expect(results.tokens.length).toEqual(1);
+ expect(results.tokens[0].key).toBe('label');
+ expect(results.tokens[0].value).toBe('real');
+ expect(results.tokens[0].symbol).toBe('');
+ expect(results.lastToken).toBe('fake:token');
+ expect(results.searchToken).toBe('fake:token');
+ });
+
+ it('returns search value for invalid symbols', () => {
+ const results = gl.FilteredSearchTokenizer.processTokens('std::includes');
+ expect(results.lastToken).toBe('std::includes');
+ expect(results.searchToken).toBe('std::includes');
+ });
+
+ it('removes duplicated values', () => {
+ const results = gl.FilteredSearchTokenizer.processTokens('label:~foo label:~foo');
+ expect(results.tokens.length).toBe(1);
+ expect(results.tokens[0].key).toBe('label');
+ expect(results.tokens[0].value).toBe('foo');
+ expect(results.tokens[0].symbol).toBe('~');
});
});
-})();
+});