From 5589ab1e0be2d682a8be424289d17b4e566caba0 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Mon, 12 Dec 2016 10:28:22 -0600 Subject: [PATCH] Refactor and add comments --- .../filtered_search/dropdown_hint.js.es6 | 3 +- .../filtered_search/dropdown_non_user.js.es6 | 17 ++++---- .../filtered_search/dropdown_user.js.es6 | 3 +- .../filtered_search_dropdown.js.es6 | 39 +++++++++--------- .../filtered_search_dropdown_manager.js.es6 | 41 ++++++++++--------- .../filtered_search_manager.js.es6 | 9 ++-- .../filtered_search_tokenizer.es6 | 12 ------ 7 files changed, 58 insertions(+), 66 deletions(-) diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js.es6 b/app/assets/javascripts/filtered_search/dropdown_hint.js.es6 index 53952e6bc63..43a0b1da0fe 100644 --- a/app/assets/javascripts/filtered_search/dropdown_hint.js.es6 +++ b/app/assets/javascripts/filtered_search/dropdown_hint.js.es6 @@ -23,7 +23,6 @@ class DropdownHint extends gl.FilteredSearchDropdown { constructor(droplab, dropdown, input) { super(droplab, dropdown, input); - this.listId = dropdown.id; this.config = { droplabFilter: { template: 'hint', @@ -66,7 +65,7 @@ return item; } - configure() { + init() { this.droplab.addHook(this.input, this.dropdown, [droplabFilter], this.config).init(); } } diff --git a/app/assets/javascripts/filtered_search/dropdown_non_user.js.es6 b/app/assets/javascripts/filtered_search/dropdown_non_user.js.es6 index 752a9a6e242..0969df65836 100644 --- a/app/assets/javascripts/filtered_search/dropdown_non_user.js.es6 +++ b/app/assets/javascripts/filtered_search/dropdown_non_user.js.es6 @@ -5,7 +5,6 @@ class DropdownNonUser extends gl.FilteredSearchDropdown { constructor(droplab, dropdown, input, endpoint, symbol) { super(droplab, dropdown, input); - this.listId = dropdown.id; this.symbol = symbol; this.config = { droplabAjax: { @@ -28,15 +27,17 @@ getEscapedText(text) { let escapedText = text; + const hasSpace = text.indexOf(' ') !== -1; + const hasDoubleQuote = text.indexOf('"') !== -1; + const hasSingleQuote = text.indexOf('\'') !== -1; // Encapsulate value with quotes if it has spaces - if (text.indexOf(' ') !== -1) { - if (text.indexOf('"') !== -1) { - // Use single quotes if value contains double quotes + // Known side effect: values's with both single and double quotes + // won't escape properly + if (hasSpace) { + if (hasDoubleQuote) { escapedText = `'${text}'`; - } else { - // Known side effect: values's with both single and double quotes - // won't escape properly + } else if (hasSingleQuote) { escapedText = `"${text}"`; } } @@ -65,7 +66,7 @@ super.renderContent(forceShowList); } - configure() { + init() { this.droplab.addHook(this.input, this.dropdown, [droplabAjax, droplabFilter], this.config).init(); } } diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js.es6 b/app/assets/javascripts/filtered_search/dropdown_user.js.es6 index 749fb9d90aa..8bc274e0b12 100644 --- a/app/assets/javascripts/filtered_search/dropdown_user.js.es6 +++ b/app/assets/javascripts/filtered_search/dropdown_user.js.es6 @@ -5,7 +5,6 @@ class DropdownUser extends gl.FilteredSearchDropdown { constructor(droplab, dropdown, input) { super(droplab, dropdown, input); - this.listId = dropdown.id; this.config = { droplabAjaxFilter: { endpoint: '/autocomplete/users.json', @@ -47,7 +46,7 @@ return hasPrefix ? valueWithoutPrefix : valueWithoutColon; } - configure() { + init() { this.droplab.addHook(this.input, this.dropdown, [droplabAjaxFilter], this.config).init(); } } diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es6 index 990d56188cb..85d684e3058 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es6 +++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es6 @@ -46,14 +46,8 @@ this.dismissDropdown(); } - renderContent(forceShowList = false) { - if (forceShowList && this.getCurrentHook().list.hidden) { - this.getCurrentHook().list.show(); - } - } - setAsDropdown() { - this.input.setAttribute(DATA_DROPDOWN_TRIGGER, `#${this.listId}`); + this.input.setAttribute(DATA_DROPDOWN_TRIGGER, `#${this.dropdown.id}`); } setOffset(offset = 0) { @@ -67,17 +61,14 @@ gl.FilteredSearchDropdownManager.addWordToInput(dataValue); } + // Return boolean based on whether it was set return dataValue !== null; } - dismissDropdown() { - this.input.focus(); - } - - dispatchInputEvent() { - // Propogate input change to FilteredSearchManager - // so that it can determine which dropdowns to open - this.input.dispatchEvent(new Event('input')); + renderContent(forceShowList = false) { + if (forceShowList && this.getCurrentHook().list.hidden) { + this.getCurrentHook().list.show(); + } } render(forceRenderContent = false, forceShowList = false) { @@ -88,11 +79,23 @@ if (firstTimeInitialized || forceRenderContent) { this.renderContent(forceShowList); - } else if(currentHook.list.list.id !== this.listId) { + } 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(); + } + + dispatchInputEvent() { + // Propogate input change to FilteredSearchDropdownManager + // so that it can determine which dropdowns to open + this.input.dispatchEvent(new Event('input')); + } + hideDropdown() { this.getCurrentHook().list.hide(); } @@ -100,9 +103,7 @@ resetFilters() { const hook = this.getCurrentHook(); const data = hook.list.data; - const results = data.map(function(o) { - o.droplab_hidden = false; - }); + const results = data.map(o => o.droplab_hidden = false); hook.list.render(results); } } diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es6 index 67a474985c0..a0764c275e5 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es6 +++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es6 @@ -5,6 +5,8 @@ this.tokenizer = gl.FilteredSearchTokenizer; this.filteredSearchInput = document.querySelector('.filtered-search'); + this.setupMapping(); + this.cleanupWrapper = this.cleanup.bind(this); document.addEventListener('page:fetch', this.cleanupWrapper); } @@ -52,21 +54,22 @@ } } - static addWordToInput(word, addSpace) { - const filteredSearchInput = document.querySelector('.filtered-search') - const filteredSearchValue = filteredSearchInput.value; - const hasExistingValue = filteredSearchValue.length !== 0; - const { lastToken } = gl.FilteredSearchTokenizer.processTokens(filteredSearchValue); + static addWordToInput(word, addSpace = false) { + const input = document.querySelector('.filtered-search') + const value = input.value; + const hasExistingValue = value.length !== 0; + const { lastToken } = gl.FilteredSearchTokenizer.processTokens(value); if (lastToken.hasOwnProperty('key')) { - console.log(lastToken); // Spaces inside the token means that the token value will be escaped by quotes const hasQuotes = lastToken.value.indexOf(' ') !== -1; + + // Add 2 length to account for the length of the front and back quotes const lengthToRemove = hasQuotes ? lastToken.value.length + 2 : lastToken.value.length; - filteredSearchInput.value = filteredSearchValue.slice(0, -1 * (lengthToRemove)); + input.value = value.slice(0, -1 * (lengthToRemove)); } - filteredSearchInput.value += hasExistingValue && addSpace ? ` ${word}` : word; + input.value += hasExistingValue && addSpace ? ` ${word}` : word; } updateCurrentDropdownOffset() { @@ -74,6 +77,10 @@ } updateDropdownOffset(key) { + if (!this.font) { + this.font = window.getComputedStyle(this.filteredSearchInput).font; + } + const filterIconPadding = 27; const offset = gl.text.getTextWidth(this.filteredSearchInput.value, this.font) + filterIconPadding; @@ -87,19 +94,20 @@ let forceShowList = false; if (!this.mapping[key].reference) { - var dl = this.droplab; + const dl = this.droplab; const defaultArguments = [null, dl, element, this.filteredSearchInput]; const glArguments = defaultArguments.concat(this.mapping[key].extraArguments || []); + // Passing glArguments to `new gl[glClass]()` this.mapping[key].reference = new (Function.prototype.bind.apply(gl[glClass], glArguments)); } if (firstLoad) { - this.mapping[key].reference.configure(); + this.mapping[key].reference.init(); } if (this.currentDropdown === 'hint') { - // Clicked from hint dropdown + // Force the dropdown to show if it was clicked from the hint dropdown forceShowList = true; } @@ -117,15 +125,12 @@ this.droplab = new DropLab(); } - if (!this.font) { - this.font = window.getComputedStyle(this.filteredSearchInput).font; - } - const match = gl.FilteredSearchTokenKeys.get().filter(value => value.key === dropdownName.toLowerCase())[0]; const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key && this.mapping.hasOwnProperty(match.key); const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint'; if (shouldOpenFilterDropdown || shouldOpenHintDropdown) { + // `hint` is not listed as a tokenKey (since it is not a real `filter`) const key = match && match.hasOwnProperty('key') ? match.key : 'hint'; this.load(key, firstLoad); } @@ -137,14 +142,12 @@ const { lastToken } = this.tokenizer.processTokens(this.filteredSearchInput.value); if (typeof lastToken === 'string') { - // Token is not fully initialized yet - // because it has no value + // Token is not fully initialized yet because it has no value // Eg. token = 'label:' const { tokenKey } = this.tokenizer.parseToken(lastToken); this.loadDropdown(tokenKey); } else if (lastToken.hasOwnProperty('key')) { - // Token has been initialized into an object - // because it has a value + // Token has been initialized into an object because it has a value this.loadDropdown(lastToken.key); } else { this.loadDropdown('hint'); diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6 b/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6 index d9ea44b3a13..d3bccc4b14c 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6 +++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js.es6 @@ -1,5 +1,6 @@ /* eslint-disable no-param-reassign */ ((global) => { + // TODO: Encapsulate inside class? function toggleClearSearchButton(e) { const clearSearchButton = document.querySelector('.clear-search'); @@ -25,6 +26,7 @@ let conditionIndex = 0; const validCondition = gl.FilteredSearchTokenKeys.get() .filter(v => v.conditions && v.conditions.filter((c, index) => { + // TODO: Add comment here if (c.url === p) { conditionIndex = index; } @@ -32,6 +34,7 @@ })[0])[0]; if (validCondition) { + // Parse params based on rules provided in the conditions key of gl.FilteredSearchTokenKeys.get() inputValue += `${validCondition.key}:${validCondition.conditions[conditionIndex].keyword}`; inputValue += ' '; } else { @@ -77,7 +80,6 @@ this.clearSearchButton = document.querySelector('.clear-search'); this.dropdownManager = new gl.FilteredSearchDropdownManager(); - this.dropdownManager.setupMapping(); this.bindEvents(); loadSearchParamsFromURL(); this.dropdownManager.setDropdown(); @@ -130,7 +132,6 @@ } checkForEnter(e) { - // Enter KeyCode if (e.keyCode === 13) { e.stopPropagation(); e.preventDefault(); @@ -143,7 +144,6 @@ } search() { - console.log('search'); let path = '?scope=all&utf8=✓'; // Check current state @@ -152,9 +152,10 @@ const defaultState = 'opened'; let currentState = defaultState; - const { tokens, searchToken } = this.tokenizer.processTokens(document.querySelector('.filtered-search').value); + const { tokens, searchToken } = this.tokenizer.processTokens(this.filteredSearchInput.value); if (stateIndex !== -1) { + // TODO: Add comment here const remaining = currentPath.slice(stateIndex + 6); const separatorIndex = remaining.indexOf('&'); diff --git a/app/assets/javascripts/filtered_search/filtered_search_tokenizer.es6 b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.es6 index 4abb5e94d81..ac45d3b7986 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_tokenizer.es6 +++ b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.es6 @@ -1,15 +1,6 @@ /* eslint-disable no-param-reassign */ ((global) => { class FilteredSearchTokenizer { - // TODO: Remove when going to pro - static printTokens(tokens, searchToken, lastToken) { - console.log('tokens:'); - tokens.forEach(token => console.log(token)); - console.log(`search: ${searchToken}`); - console.log('last token:'); - console.log(lastToken); - } - static parseToken(input) { const colonIndex = input.indexOf(':'); let tokenKey; @@ -163,9 +154,6 @@ searchToken = searchTerms.trim(); - // TODO: Remove when going to PRO - gl.FilteredSearchTokenizer.printTokens(tokens, searchToken, lastToken); - return { tokens, searchToken, -- GitLab