From 6804003425393f290977d5ab5ee26969ebcabb16 Mon Sep 17 00:00:00 2001 From: R Fultz Date: Tue, 12 Oct 2021 13:31:18 -0400 Subject: [PATCH 01/67] WIP --- fec/fec/static/js/init.js | 11 +- fec/fec/static/js/modules/typeahead.js | 123 +- fec/fec/templates/home_base.html | 12 +- package-lock.json | 18463 +---------------------- package.json | 4 + 5 files changed, 186 insertions(+), 18427 deletions(-) diff --git a/fec/fec/static/js/init.js b/fec/fec/static/js/init.js index 388dadf6b9..9a31292ad0 100644 --- a/fec/fec/static/js/init.js +++ b/fec/fec/static/js/init.js @@ -16,9 +16,12 @@ var skipNav = require('./modules/skip-nav'); var siteNav = require('./modules/site-nav'); var dropdown = require('./modules/dropdowns'); var toc = require('./modules/toc'); -var typeahead = require('./modules/typeahead'); +// var typeahead = require('./modules/typeahead'); var helpers = require('./modules/helpers'); +import filterTypeahead from './modules/filters/filter-typeahead'; +import { AutoComplete } from './modules/typeahead'; + // Hack: Append jQuery to `window` for use by legacy libraries window.$ = window.jQuery = $; @@ -94,6 +97,12 @@ $(document).ready(function() { new typeahead.Typeahead($(this), 'all', '/data/'); }); + let siteSearchObjects = document.querySelectorAll('.js-site-search-new'); + siteSearchObjects.forEach((val, key) => { + // console.log('forEach val, key: ', val, key); + new AutoComplete(val, 'all', '/data/'); + }); + // For any link that should scroll to a section on the page apply .js-scroll to $('.js-scroll').on('click', function(e) { e.preventDefault(); diff --git a/fec/fec/static/js/modules/typeahead.js b/fec/fec/static/js/modules/typeahead.js index f67835f529..b6d2370901 100644 --- a/fec/fec/static/js/modules/typeahead.js +++ b/fec/fec/static/js/modules/typeahead.js @@ -6,19 +6,23 @@ var $ = require('jquery'); var URI = require('urijs'); -var _ = require('underscore'); -var Handlebars = require('handlebars'); -var helpers = require('./helpers'); +// var _ = require('underscore'); +let Handlebars = require('handlebars'); +import { sanitizeValue } from './helpers'; // Hack: Append jQuery to `window` for use by typeahead.js window.$ = window.jQuery = $; -require('corejs-typeahead/dist/typeahead.jquery'); +// require('corejs-typeahead/dist/typeahead.jquery'); var Bloodhound = require('corejs-typeahead/dist/bloodhound'); -var events = require('./events'); +import autoComplete from '@tarekraafat/autocomplete.js'; +import events from './events'; -var officeMap = { + +// var events = require('./events'); + +const officeMap = { H: 'House', S: 'Senate', P: 'President' @@ -68,17 +72,17 @@ function getUrl(resource) { .readable(); } -var engineOpts = { +const engineOpts = { datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), queryTokenizer: Bloodhound.tokenizers.whitespace, limit: 10 }; function createEngine(opts) { - return new Bloodhound(_.extend({}, engineOpts, opts)); + return new Bloodhound(Object.assign({}, engineOpts, opts)); } -var candidateEngine = createEngine({ +const candidateEngine = createEngine({ remote: { url: getUrl('candidates'), wildcard: '%QUERY', @@ -88,7 +92,7 @@ var candidateEngine = createEngine({ } }); -var committeeEngine = createEngine({ +const committeeEngine = createEngine({ remote: { url: getUrl('committees'), wildcard: '%QUERY', @@ -98,7 +102,7 @@ var committeeEngine = createEngine({ } }); -var auditCommitteeEngine = createEngine({ +const auditCommitteeEngine = createEngine({ remote: { url: getUrl('audit_committees'), wildcard: '%QUERY', @@ -108,7 +112,7 @@ var auditCommitteeEngine = createEngine({ } }); -var auditCandidateEngine = createEngine({ +const auditCandidateEngine = createEngine({ remote: { url: getUrl('audit_candidates'), wildcard: '%QUERY', @@ -118,7 +122,7 @@ var auditCandidateEngine = createEngine({ } }); -var candidateDataset = { +const candidateDataset = { name: 'candidate', display: 'name', limit: 5, @@ -137,7 +141,7 @@ var candidateDataset = { } }; -var committeeDataset = { +const committeeDataset = { name: 'committee', display: 'name', limit: 10, @@ -155,7 +159,7 @@ var committeeDataset = { } }; -var auditCommitteeDataset = { +const auditCommitteeDataset = { name: 'auditCommittees', display: 'name', limit: 10, @@ -171,7 +175,7 @@ var auditCommitteeDataset = { } }; -var auditCandidateDataset = { +const auditCandidateDataset = { name: 'auditCandidates', display: 'name', limit: 10, @@ -196,7 +200,7 @@ var individualDataset = { source: function(query, syncResults) { syncResults([ { - id: helpers.sanitizeValue(query), + id: sanitizeValue(query), type: 'individual' } ]); @@ -220,7 +224,7 @@ var siteDataset = { source: function(query, syncResults) { syncResults([ { - id: helpers.sanitizeValue(query), + id: sanitizeValue(query), type: 'site' } ]); @@ -249,6 +253,35 @@ var typeaheadOpts = { hint: false }; +let autoCompleteOpts = { + selector: ".js-site-search-new", + placeHolder: "The opts worked!", + data: { + src: ["Sauce - Thousand Island", "Wild Boar - Tenderloin", "Goat - Whole Cut"], + cache: true, + }, + resultsList: { + element: (list, data) => { + if (!data.results.length) { + // Create "No Results" message element + const message = document.createElement("div"); + // Add class to the created element + message.setAttribute("class", "no_result"); + // Add message text content + message.innerHTML = `Found No Results for "${data.query}"`; + // Append message element to the results list + list.prepend(message); + } + }, + noResults: true, + }, + resultItem: { + highlight: { + render: true + } + } +}; + /** * @class * @param {String} selector - A string to be used to find the element in the page. @@ -264,17 +297,32 @@ var typeaheadOpts = { * @property {Object} - null if no results. Otherwise we get back an {Object} for each item in the menu */ function Typeahead(selector, type, url) { - this.$input = $(selector); + // this.$input = $(selector); + // this.url = url || '/'; + // this.typeahead = null; + + // this.dataset = datasets[type]; + + // this.init(); + + // events.on('searchTypeChanged', this.handleChangeEvent.bind(this)); + + // this.$input.on('keyup', this.setAria.bind(this)); +} +function AutoComplete(element, type, url) { + events.on('searchTypeChanged', this.handleChangeEvent.bind(this)); + + this.$input = element; this.url = url || '/'; - this.typeahead = null; + this.autoComplete = null; this.dataset = datasets[type]; this.init(); - events.on('searchTypeChanged', this.handleChangeEvent.bind(this)); + console.log('new AutoComplete!'); - this.$input.on('keyup', this.setAria.bind(this)); + this.$input.addEventListener('keyup', this.setAria.bind(this)); } Typeahead.prototype.init = function() { @@ -290,10 +338,28 @@ Typeahead.prototype.init = function() { this.$input.on('typeahead:select', this.select.bind(this)); }; +AutoComplete.prototype.init = function() { + console.log('AutoComplete.init()'); + // if (this.autoComplete) this.$input.typeahead('destroy'); + + // this.autoComplete = this.$input.typeahead(typeaheadOpts, this.dataset); + // this.$element = this.$input.parent('.twitter-typeahead'); + // this.$element.css('display', 'block'); + // this.$element.find('.tt-menu').attr('aria-live', 'polite'); + // this.$element.find('.tt-input').removeAttr('aria-readonly'); + // this.$element.find('.tt-input').attr('aria-expanded', 'false'); + // this.$input.on('typeahead:select', this.select.bind(this)); +}; + Typeahead.prototype.handleChangeEvent = function(data) { this.init(data.type); }; +AutoComplete.prototype.handleChangeEvent = function(data) { + console.log('AutoComplete.handleChangeEvent'); + this.init(data.type); +} + Typeahead.prototype.setAria = function() { if (this.$element.find('.tt-menu').attr('aria-expanded') == 'false') { this.$element.find('.tt-input').attr('aria-expanded', 'false'); @@ -303,6 +369,18 @@ Typeahead.prototype.setAria = function() { //alert('closed') }; +AutoComplete.prototype.setAria = function() { + console.log('AutoComplete.setAria()'); + + let thisMenu = this.$input.querySelector('.tt-menu[aria-expanded]'); + let thisInput = this.$input.querySelector('.tt-input'); + console.log(' thisMenu: ', thisMenu); + console.log(' thisInput: ', thisInput); + console.log(' this.$input: ', this.$input); + console.log(' !!thisMenu: ', !!thisMenu); + if (thisInput && thisMenu) thisInput.setAttribute('aria-expanded', !!thisMenu); +}; + Typeahead.prototype.select = function(event, datum) { if (datum.type === 'individual') { window.location = @@ -329,6 +407,7 @@ Typeahead.prototype.searchSite = function(query) { }; module.exports = { + AutoComplete: AutoComplete, Typeahead: Typeahead, datasets: datasets }; diff --git a/fec/fec/templates/home_base.html b/fec/fec/templates/home_base.html index ef15cc4340..f750e493a6 100644 --- a/fec/fec/templates/home_base.html +++ b/fec/fec/templates/home_base.html @@ -52,14 +52,22 @@

Your web browser is not supported

`; + this.$input.parentNode.insertBefore(this.$element, this.$input); + this.$element.prepend(this.$input); + + /* FINAL LOOK: + + */ + + let theMenus = this.$element.querySelectorAll('.tt-menu'); + theMenus.forEach(el => { + el.setAttribute('aria-live', 'polite'); + }); + + let theInputs = this.$element.querySelectorAll('.tt-input'); + theInputs.forEach(el => { + el.setAttribute('aria-expanded', 'false').removeAttribute('aria-readonly'); + }); + + this.$input.addEventListener('typeahead:select', this.select.bind(this)); }; Typeahead.prototype.handleChangeEvent = function(data) { @@ -358,7 +449,7 @@ Typeahead.prototype.handleChangeEvent = function(data) { AutoComplete.prototype.handleChangeEvent = function(data) { console.log('AutoComplete.handleChangeEvent'); this.init(data.type); -} +}; Typeahead.prototype.setAria = function() { if (this.$element.find('.tt-menu').attr('aria-expanded') == 'false') { @@ -378,7 +469,8 @@ AutoComplete.prototype.setAria = function() { console.log(' thisInput: ', thisInput); console.log(' this.$input: ', this.$input); console.log(' !!thisMenu: ', !!thisMenu); - if (thisInput && thisMenu) thisInput.setAttribute('aria-expanded', !!thisMenu); + if (thisInput && thisMenu) + thisInput.setAttribute('aria-expanded', !!thisMenu); }; Typeahead.prototype.select = function(event, datum) { @@ -393,6 +485,19 @@ Typeahead.prototype.select = function(event, datum) { window.location = this.url + datum.type + '/' + datum.id; } }; +AutoComplete.prototype.select = function(event, datum) { + console.log('AutoComplete.select(event, datum): ', event, datum); + // if (datum.type === 'individual') { + // window.location = + // this.url + + // 'receipts/individual-contributions/?contributor_name=' + + // datum.id; + // } else if (datum.type === 'site') { + // this.searchSite(datum.id); + // } else { + // window.location = this.url + datum.type + '/' + datum.id; + // } +}; Typeahead.prototype.searchSite = function(query) { /* If the site search option is selected, this function handles submitting From 1b942f97a4cfb849fc222a92610ec9f1658b4194 Mon Sep 17 00:00:00 2001 From: R Fultz Date: Thu, 18 Nov 2021 13:51:11 -0500 Subject: [PATCH 03/67] Progress! --- .eslintrc | 11 +- fec/fec/static/js/init.js | 8 +- fec/fec/static/js/modules/typeahead.js | 166 +- fec/fec/templates/home_base.html | 129 +- package-lock.json | 19980 +++++++++++++++++++++-- 5 files changed, 18447 insertions(+), 1847 deletions(-) diff --git a/.eslintrc b/.eslintrc index 5a2485e5ad..5abb6ed8d2 100644 --- a/.eslintrc +++ b/.eslintrc @@ -24,7 +24,15 @@ "navigator": true }, "rules": { + "array-bracket-newline": ["error", "consistent"], "array-bracket-spacing": ["error", "never", { "objectsInArrays": false }], + // "array-element-newline": ["error", + // { + // "multiline": true, + // "minItems": 2 + // } + // ], + "comma-spacing": ["error", { "before": false, "after": true }], "comma-dangle": ["error", "never"], "eol-last": ["error", "always"], // "eqeqeq": ["warn", "smart"], @@ -36,8 +44,9 @@ "max-len": ["error", { "code": 200, "comments": 250, - "ignoreStrings": true, + // "ignoreStrings": true, "ignoreTemplateLiterals": true + // "ignorePattern": false }], "no-console": "error", "no-empty-function": "error", diff --git a/fec/fec/static/js/init.js b/fec/fec/static/js/init.js index 9a31292ad0..145adddc36 100644 --- a/fec/fec/static/js/init.js +++ b/fec/fec/static/js/init.js @@ -19,7 +19,7 @@ var toc = require('./modules/toc'); // var typeahead = require('./modules/typeahead'); var helpers = require('./modules/helpers'); -import filterTypeahead from './modules/filters/filter-typeahead'; +// import filterTypeahead from './modules/filters/filter-typeahead'; import { AutoComplete } from './modules/typeahead'; // Hack: Append jQuery to `window` for use by legacy libraries @@ -94,10 +94,10 @@ $(document).ready(function() { // Initialize header typeaheads (mobile and desktop) $('.js-site-search').each(function() { - new typeahead.Typeahead($(this), 'all', '/data/'); + // new typeahead.Typeahead($(this), 'all', '/data/'); }); - let siteSearchObjects = document.querySelectorAll('.js-site-search-new'); + let siteSearchObjects = document.querySelectorAll('.js-site-search'); siteSearchObjects.forEach((val, key) => { // console.log('forEach val, key: ', val, key); new AutoComplete(val, 'all', '/data/'); @@ -124,6 +124,6 @@ $(document).ready(function() { } else { $link.remove(); } - $p.nextAll().remove() + $p.nextAll().remove(); }); }); diff --git a/fec/fec/static/js/modules/typeahead.js b/fec/fec/static/js/modules/typeahead.js index eeef4bccf2..6078a44f85 100644 --- a/fec/fec/static/js/modules/typeahead.js +++ b/fec/fec/static/js/modules/typeahead.js @@ -5,7 +5,7 @@ */ var $ = require('jquery'); -var URI = require('urijs'); +// var URI = require('urijs'); var _ = require('underscore'); let Handlebars = require('handlebars'); import { sanitizeValue } from './helpers'; @@ -28,6 +28,7 @@ const officeMap = { }; function formatCandidate(result) { + console.log('formatCandidate(result): ', result); return { name: result.name, id: result.id, @@ -37,6 +38,7 @@ function formatCandidate(result) { } function formatCommittee(result) { + console.log('formatCommittee(result): ', result); return { name: result.name, id: result.id, @@ -46,6 +48,7 @@ function formatCommittee(result) { } function formatAuditCommittee(result) { + console.log('formatAuditCommittee(result): ', result); return { name: result.name, id: result.id, @@ -54,6 +57,7 @@ function formatAuditCommittee(result) { } function formatAuditCandidate(result) { + console.log('formatAuditCandidate(result): ', result); return { name: result.name, id: result.id, @@ -64,27 +68,14 @@ function formatAuditCandidate(result) { function getUrl(resource) { console.log('getURL(resource): ', resource); - let toReturn = URI(window.API_LOCATION) - .path([window.API_VERSION, 'names', resource, ''].join('/')) - .query({ - q: '%QUERY', - api_key: window.API_KEY_PUBLIC - }) - .readable(); - - let toReturn2 = [ + let toReturn = [ window.API_LOCATION, window.API_VERSION, 'names', resource, '' ].join('/'); - toReturn2 += `?=%QUERY&api_key=${window.API_KEY_PUBLIC}`; - - console.log('getURL()'); - console.log(' toReturn: ', toReturn); - console.log(' toReturn2: ', toReturn2); - console.log(' toReturn2.href: ', toReturn2.href); + toReturn += `?q=%QUERY&api_key=${window.API_KEY_PUBLIC}`; return toReturn; } @@ -103,7 +94,7 @@ const candidateEngine = createEngine({ url: getUrl('candidates'), wildcard: '%QUERY', transform: function(response) { - console.log('committeeEngine'); + console.log('committeeEngine transform'); let toReturn = _.map(response.results, formatCandidate); // return _.map(response.results, formatCommittee); let toReturn2 = response['results'].map(n => formatCandidate(n)); @@ -290,39 +281,31 @@ var typeaheadOpts = { hint: false }; -let autoCompleteOpts = { - selector: '.js-site-search-new', - placeHolder: 'The opts worked!', +let autoCompleteOpts = window.tempOpts; +/*{ + selector: '.js-site-search', + // placeHolder: 'The opts worked!', data: { - // src: [ - // 'Sauce - Thousand Island', - // 'Wild Boar - Tenderloin', - // 'Goat - Whole Cut' - // ], - src: '', - cache: true - }, - resultsList: { - element: (list, data) => { - if (!data.results.length) { - // Create "No Results" message element - const message = document.createElement('div'); - // Add class to the created element - message.setAttribute('class', 'no_result'); - // Add message text content - message.innerHTML = `Found No Results for "${data.query}"`; - // Append message element to the results list - list.prepend(message); - } + src: async q => { + // console.log('src! q: ', q); + let toReturn; + toReturn = [ + { food: 'Sauce - Thousand Island', cities: 'Soanindrariny', animals: 'Common boubou shrike' }, + { food: 'Wild Boar - Tenderloin', cities: 'Luthu', animals: 'Eastern diamondback rattlesnake' }, + { food: 'Goat - Whole Cut', cities: 'Kargowa', animals: 'Sheep, red' } + ]; + console.log('typeof toReturn: ', typeof toReturn); + return toReturn; + // src: [{ name: 'OBAMA, BARACK \/ JOSEPH R. BIDEN', office_sought: 'P', id: 'P80003338' }, { name: 'BIDEN, JOSEPH R JR', office_sought: 'P', id: 'P80000722' }, { name: 'BIDEN, JOSEPH R JR', office_sought: 'S', id: 'S8DE00012' }, { name: 'BIDEN, JOE R', office_sought: 'P', id: 'P60012143' }, { name: 'BIDEN, JOSEPH ROBINETTEE', office_sought: 'P', id: 'P60012135' }, { name: 'BIDEN, JR., JOSEPH R.', office_sought: 'P', id: 'P60012465' }], + // keys: ['name', 'id'], }, - noResults: true - }, - resultItem: { - highlight: { - render: true + keys: ['food', 'cities', 'animals'] + }, + resultsList: { + tag: 'div', + id: '', } - } -}; +};*/ /** * @class @@ -364,16 +347,16 @@ function AutoComplete(element, type, url) { } Typeahead.prototype.init = function() { - if (this.typeahead) { - this.$input.typeahead('destroy'); - } - this.typeahead = this.$input.typeahead(typeaheadOpts, this.dataset); - this.$element = this.$input.parent('.twitter-typeahead'); - this.$element.css('display', 'block'); - this.$element.find('.tt-menu').attr('aria-live', 'polite'); - this.$element.find('.tt-input').removeAttr('aria-readonly'); - this.$element.find('.tt-input').attr('aria-expanded', 'false'); - this.$input.on('typeahead:select', this.select.bind(this)); + // if (this.typeahead) { + // this.$input.typeahead('destroy'); + // } + // this.typeahead = this.$input.typeahead(typeaheadOpts, this.dataset); + // this.$element = this.$input.parent('.twitter-typeahead'); + // this.$element.css('display', 'block'); + // this.$element.find('.tt-menu').attr('aria-live', 'polite'); + // this.$element.find('.tt-input').removeAttr('aria-readonly'); + // this.$element.find('.tt-input').attr('aria-expanded', 'false'); + // this.$input.on('typeahead:select', this.select.bind(this)); }; AutoComplete.prototype.init = function() { @@ -381,9 +364,7 @@ AutoComplete.prototype.init = function() { // TODO: do we need to destroy/reset one if it already exists? // if (this.autoComplete) this.$input.typeahead('destroy'); let theseOpts = autoCompleteOpts; - theseOpts.data = Object.assign(theseOpts.data, { src: getUrl() }); - - this.autoComplete = new autoComplete(autoCompleteOpts, this.dataset); + theseOpts.data = Object.assign(theseOpts.data, theseOpts ); // Create a new span to wrap the input, // add the span to the page before the input @@ -405,16 +386,15 @@ AutoComplete.prototype.init = function() { role="listbox"\ class="tt-menu"\ aria-live="polite"\ - style="position: absolute; top: 100%; left: 0px; z-index: 100; display: none;"\ + style="position: absolute; top: 100%; left: 0px; z-index: 100;"\ aria-expanded="false">\ - \ - \ - \ - \ + \ `; this.$input.parentNode.insertBefore(this.$element, this.$input); this.$element.prepend(this.$input); + this.autoComplete = new autoComplete(autoCompleteOpts); + /* FINAL LOOK:
- - - - - + +
diff --git a/fec/home/templates/purgecss-homepage/navs.html b/fec/home/templates/purgecss-homepage/navs.html index cf21755d69..18769f311a 100644 --- a/fec/home/templates/purgecss-homepage/navs.html +++ b/fec/home/templates/purgecss-homepage/navs.html @@ -74,7 +74,7 @@

Your web browser is not supported

`; + // Create an element to hold the results + this.resultsHolder = document.createElement('div'); + this.resultsHolder.setAttribute('role', 'listbox'); + this.resultsHolder.setAttribute('class', 'tt-menu'); + this.resultsHolder.setAttribute('aria-live', 'polite'); + this.resultsHolder.setAttribute('style', 'position: absolute; top: 100%; left: 0px; z-index: 100;'); + this.resultsHolder.setAttribute('aria-expanded', 'false'); + this.resultsHolder.innerHTML = ''; + this.$element.appendChild(this.resultsHolder); + this.$input.parentNode.insertBefore(this.$element, this.$input); this.$element.prepend(this.$input); this.autoComplete = new autoComplete(autoCompleteOpts); - /* FINAL LOOK: - - */ - let theMenus = this.$element.querySelectorAll('.tt-menu'); theMenus.forEach(el => { el.setAttribute('aria-live', 'polite'); @@ -419,23 +412,16 @@ AutoComplete.prototype.init = function() { el.setAttribute('aria-expanded', 'false').removeAttribute('aria-readonly'); }); - this.$input.addEventListener('typeahead:select', this.select.bind(this)); - - this.$input.addEventListener('results', this.handleResultsEvent.bind(this)); - this.$input.addEventListener('open', this.handleOpenEvent.bind(this)); + this.$input.addEventListener('results', this.handleResults.bind(this)); + this.$input.addEventListener('selection', this.handleSelect.bind(this)); + this.$input.addEventListener('navigate', this.handleNavigate.bind(this)); }; -AutoComplete.prototype.handleResultsEvent = function(e) { - console.log('handleResultsEvent e: ', e); - console.log(' target next sibling: ', e.srcElement.nextElementSibling); - let resultsHolder = e.srcElement.parentElement.querySelector('.tt-dataset'); - console.log(' resultsHolder: ', resultsHolder); +AutoComplete.prototype.handleFocus = function(e) { + // Only opens if the resultsList is not empty + this.autoComplete.open(); }; -AutoComplete.prototype.handleOpenEvent = function(e) { - //Select a candidate: - console.log('handleOpenEvent e: ', e); -}; Typeahead.prototype.handleChangeEvent = function(data) { // this.init(data.type); }; @@ -467,34 +453,57 @@ AutoComplete.prototype.setAria = function() { thisInput.setAttribute('aria-expanded', !!thisMenu); }; -Typeahead.prototype.select = function(event, datum) { - // if (datum.type === 'individual') { - // window.location = - // this.url + - // 'receipts/individual-contributions/?contributor_name=' + - // datum.id; - // } else if (datum.type === 'site') { - // this.searchSite(datum.id); - // } else { - // window.location = this.url + datum.type + '/' + datum.id; - // } +AutoComplete.prototype.handleResults = function(e) { + console.log('handleResults()'); + // Reset the 'last selected' marker for arrow/keyboard navigation + this.formerSelectionIndex = 0; }; -AutoComplete.prototype.select = function(event, datum) { - console.log('AutoComplete.select(event, datum): ', event, datum); - // if (datum.type === 'individual') { - // window.location = - // this.url + - // 'receipts/individual-contributions/?contributor_name=' + - // datum.id; - // } else if (datum.type === 'site') { - // this.searchSite(datum.id); - // } else { - // window.location = this.url + datum.type + '/' + datum.id; - // } + +AutoComplete.prototype.handleSelect = function(e) { + console.log('handleSelect(e): ', e); + let val = e.detail.selection.value; + + // If it's a header, ignore the selection/click/tap + if (val.is_header) return; + + // Find the element + if (val.type == 'individual') { + window.location = `${this.url}receipts/individual-contributions/?contributor_name=${val.id}`; + + } else if (val.type == 'candidate' || val.type == 'committee') { + window.location = `${this.url}${val.type}/${val.id}`; + + } else if (val.type == 'site') { + this.searchSite(e.detail.selection.match); + } + + // if (theURL) window.location = theURL; +}; + +AutoComplete.prototype.handleNavigate = function(e) { + console.log('handleNavigate(e): ', e); + // If we've just focused on a header object, we want to nav off of it + if (e.detail.selection.value.is_header === true) { + // If we were previously higher on the list, let's go to the next + if (this.formerSelectionIndex <= e.detail.selection.index) this.autoComplete.next(); + // If we were previously lower on the list, let's jump up another + else this.autoComplete.previous(); + + // Save the former selection for next time + this.formerSelectionIndex = e.detail.selection.index; + + } else if (this.formerSelectionIndex == e.detail.selection.index && this.formerSelectionIndex >= e.detail.results.length - 1) { + this.formerSelectionIndex = -1; + this.autoComplete.goTo(0); + + } else { + this.formerSelectionIndex = e.detail.selection.index; + this.$input.value = e.detail.selection.match; + } }; Typeahead.prototype.searchSite = function(query) { - /* If the site search option is selected, this function handles submitting + /** If the site search option is selected, this function handles submitting * a new search on /search */ @@ -505,6 +514,18 @@ Typeahead.prototype.searchSite = function(query) { // $form.submit(); }; +AutoComplete.prototype.searchSite = function(q) { + console.log('searchSite(q): ', q); + /** If the site search option is selected, this function handles submitting + * a new search on /search + */ + let form = this.$input.closest('form'); + let action = form.getAttribute('action'); + this.$input.value = q; + form.setAttribute('action', action); + form.submit(); +}; + module.exports = { AutoComplete: AutoComplete, Typeahead: Typeahead, diff --git a/fec/fec/static/scss/components/_search-bar.scss b/fec/fec/static/scss/components/_search-bar.scss index 9e7529cea3..a7f554994b 100644 --- a/fec/fec/static/scss/components/_search-bar.scss +++ b/fec/fec/static/scss/components/_search-bar.scss @@ -278,6 +278,9 @@ $search-button-width: u(5.6rem); padding: u(1rem .5rem); } +.tt-dataset { + margin-left: 0; +} .tt-dataset-0 { border-bottom: 1px solid $base; } diff --git a/fec/fec/templates/home_base.html b/fec/fec/templates/home_base.html index f564cc002d..0857ea7b23 100644 --- a/fec/fec/templates/home_base.html +++ b/fec/fec/templates/home_base.html @@ -161,6 +161,8 @@

Your web browser is not supported

window.API_KEY_PUBLIC_CALENDAR = '{{ settings.FEC_API_KEY_PUBLIC_CALENDAR }}'; window.CANONICAL_BASE = '{{ settings.CANONICAL_BASE }}'; + if (window.FEC_APP_URL == 'None') window.FEC_APP_URL = 'http://127.0.0.1:8000/'; + const officeMap = { H: 'House', S: 'Senate', @@ -188,40 +190,56 @@

Your web browser is not supported

// placeHolder: 'The opts worked!', data: { src: async q => { - let fetchedResults; + let fetchedResults = []; window.queryText = q; - await fetch(`https://fec-dev-api.app.cloud.gov/v1/names/candidates/?q=${q}&api_key=${window.API_KEY_PUBLIC}`, + await fetch( + `https://fec-dev-api.app.cloud.gov/v1/names/candidates/?q=${q}&api_key=${window.API_KEY_PUBLIC}`, fetchInit ) .then(response => response.json()) .then(data => { - console.log('yay! first fetch data: ', data); - if (data.results.length > 0) fetchedResults = [{is_header: true, name: 'Select a candidate:', id: window.queryText}]; + let results = data.results; + + if (results.length > 0) fetchedResults.push({is_header: true, id: window.queryText, name: 'Select a candidate:', type: 'none'}); // console.log('data.results.length: ', data.results.length); // if (data.results.length > 0) console.log(' first data.results: ', data.results[0]); - fetchedResults.push(...data.results.slice(0, 5)); + results.forEach(element => { + element.type = 'candidate'; + }); + fetchedResults.push(...results.slice(0, 5)); // if (response.status !== 200) throw new Error('The network rejected the grand total request.'); // else if (response.type == 'cors') throw new Error('CORS error'); // response.json().then(data => { // instance.displayUpdatedData_grandTotal(data); // }); - return fetch(`https://fec-dev-api.app.cloud.gov/v1/names/committees/?q=${q}&api_key=${window.API_KEY_PUBLIC}`, - fetchInit - ) + return fetch( + `https://fec-dev-api.app.cloud.gov/v1/names/committees/?q=${q}&api_key=${window.API_KEY_PUBLIC}`, + fetchInit + ) }) .then(response => response.json()) .then(data => { - console.log('second fetch data: ', data); - if (data.results.length > 0) fetchedResults.push({is_header: true, name: 'Select a committee:', id: window.queryText}); - fetchedResults.push(...data.results.slice(0, 10)); + let results = data.results; + + if (!fetchedResults) fetchedResults = []; + if (results.length > 0) fetchedResults.push({is_header: true, id: window.queryText, name: 'Select a committee:', type: 'none'}); + + results.forEach(element => { + element.type = 'committee'; + }); + fetchedResults.push(...results.slice(0, 10)); + }) + .then((data) => { + fetchedResults.push({is_suggestion: true, id: window.queryText, name: 'Search individual contributions from:', type: 'individual'}); + fetchedResults.push({is_suggestion: true, id: window.queryText, name: 'Search other pages:', type: 'site'}); + // suggestion for individual contribs }); - console.log('big fetchedResults: ', fetchedResults); return fetchedResults; }, keys: searchedAttribs() }, resultsList: { - tag: 'div', + // tag: 'div', class: 'tt-dataset tt-dataset-candidate', maxResults: 20, tabSelect: true, @@ -239,13 +257,23 @@

Your web browser is not supported

// Need header: Select a candidate: }, resultItem: { - tag: 'span', + // tag: 'span', class: 'tt-suggestion tt-selectable', + selected: 'tt-cursor', element: (item, data) => { - console.log('resultItem.element() data: ', item, data); + // console.log('resultItem.element() data: ', item, data); + if (data.value.is_header) { item.setAttribute('class', 'tt-suggestion__header'); + item.setAttribute('tabindex', '-1'); item.innerHTML = data.value.name; + } else if (data.value.is_suggestion) { + item.setAttribute('class', 'tt-suggestion tt-select'); + item.setAttribute('tabindex', '-1'); + item.innerHTML = `${data.value.name} "${data.value.id}"`; + // Search individual contributions from: "window.queryText" + // Search other pages: "john" + } else { // console.log('q: ', q); let dName = data.value.name; @@ -267,13 +295,15 @@

Your web browser is not supported

dID = `${baseStr.slice(0, startPos)}${baseStr.slice(startPos, endPos)}${baseStr.slice(endPos)}`; } - item.innerHTML = `${dName} (${dID})`; + item.innerHTML = `${dName} (${dID})`; - if (dOffice) item.innerHTML += `${dOffice}`; + if (dOffice) item.innerHTML += `${dOffice}`; if (data.value.is_active === true) item.innerHTML += ''; else if (data.value.is_active === false) item.innerHTML += ''; } + + // } // // TESTER, R. JON (S6MT00162) From 8b75b554818dd77b63eef82dc1e5a4074a4e1144 Mon Sep 17 00:00:00 2001 From: R Fultz Date: Mon, 13 Dec 2021 14:39:59 -0500 Subject: [PATCH 06/67] Revert typeahead.js file to develop --- fec/fec/static/js/modules/typeahead.js | 325 +++++-------------------- 1 file changed, 63 insertions(+), 262 deletions(-) diff --git a/fec/fec/static/js/modules/typeahead.js b/fec/fec/static/js/modules/typeahead.js index 2be3f641c3..f67835f529 100644 --- a/fec/fec/static/js/modules/typeahead.js +++ b/fec/fec/static/js/modules/typeahead.js @@ -5,30 +5,26 @@ */ var $ = require('jquery'); -// var URI = require('urijs'); +var URI = require('urijs'); var _ = require('underscore'); -let Handlebars = require('handlebars'); -import { sanitizeValue } from './helpers'; +var Handlebars = require('handlebars'); +var helpers = require('./helpers'); // Hack: Append jQuery to `window` for use by typeahead.js window.$ = window.jQuery = $; -// require('corejs-typeahead/dist/typeahead.jquery'); +require('corejs-typeahead/dist/typeahead.jquery'); var Bloodhound = require('corejs-typeahead/dist/bloodhound'); -import autoComplete from '@tarekraafat/autocomplete.js'; -import events from './events'; +var events = require('./events'); -// var events = require('./events'); - -const officeMap = { +var officeMap = { H: 'House', S: 'Senate', P: 'President' }; function formatCandidate(result) { - console.log('formatCandidate(result): ', result); return { name: result.name, id: result.id, @@ -38,7 +34,6 @@ function formatCandidate(result) { } function formatCommittee(result) { - console.log('formatCommittee(result): ', result); return { name: result.name, id: result.id, @@ -48,7 +43,6 @@ function formatCommittee(result) { } function formatAuditCommittee(result) { - console.log('formatAuditCommittee(result): ', result); return { name: result.name, id: result.id, @@ -57,7 +51,6 @@ function formatAuditCommittee(result) { } function formatAuditCandidate(result) { - console.log('formatAuditCandidate(result): ', result); return { name: result.name, id: result.id, @@ -66,91 +59,66 @@ function formatAuditCandidate(result) { } function getUrl(resource) { - console.log('getURL(resource): ', resource); - - let toReturn = [ - window.API_LOCATION, - window.API_VERSION, - 'names', - resource, - '' - ].join('/'); - toReturn += `?q=%QUERY&api_key=${window.API_KEY_PUBLIC}`; - return toReturn; + return URI(window.API_LOCATION) + .path([window.API_VERSION, 'names', resource, ''].join('/')) + .query({ + q: '%QUERY', + api_key: window.API_KEY_PUBLIC + }) + .readable(); } -const engineOpts = { +var engineOpts = { datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), queryTokenizer: Bloodhound.tokenizers.whitespace, limit: 10 }; function createEngine(opts) { - return new Bloodhound(Object.assign({}, engineOpts, opts)); + return new Bloodhound(_.extend({}, engineOpts, opts)); } -const candidateEngine = createEngine({ +var candidateEngine = createEngine({ remote: { url: getUrl('candidates'), wildcard: '%QUERY', transform: function(response) { - console.log('committeeEngine transform'); - let toReturn = _.map(response.results, formatCandidate); - // return _.map(response.results, formatCommittee); - let toReturn2 = response['results'].map(n => formatCandidate(n)); - console.log(' toReturn: ', toReturn); - console.log(' toReturn2: ', toReturn2); + return _.map(response.results, formatCandidate); } } }); -const committeeEngine = createEngine({ +var committeeEngine = createEngine({ remote: { url: getUrl('committees'), wildcard: '%QUERY', transform: function(response) { - console.log('committeeEngine'); - let toReturn = _.map(response.results, formatCommittee); - // return _.map(response.results, formatCommittee); - let toReturn2 = response['results'].map(n => formatCommittee(n)); - console.log(' toReturn: ', toReturn); - console.log(' toReturn2: ', toReturn2); - return toReturn; + return _.map(response.results, formatCommittee); } } }); -const auditCommitteeEngine = createEngine({ +var auditCommitteeEngine = createEngine({ remote: { url: getUrl('audit_committees'), wildcard: '%QUERY', transform: function(response) { - console.log('auditCommitteeEngine'); - let toReturn = _.map(response.results, formatAuditCommittee); - // return _.map(response.results, formatCommittee); - let toReturn2 = response['results'].map(n => formatAuditCommittee(n)); - console.log(' toReturn: ', toReturn); - console.log(' toReturn2: ', toReturn2); + return _.map(response.results, formatAuditCommittee); } } }); -const auditCandidateEngine = createEngine({ +var auditCandidateEngine = createEngine({ remote: { url: getUrl('audit_candidates'), wildcard: '%QUERY', transform: function(response) { - console.log('auditCandidateEngine'); - let toReturn = _.map(response.results, formatAuditCandidate); - // return _.map(response.results, formatCommittee); - let toReturn2 = response['results'].map(n => formatAuditCandidate(n)); - console.log(' toReturn: ', toReturn); - console.log(' toReturn2: ', toReturn2); + return _.map(response.results, formatAuditCandidate); } } }); -const candidateDataset = { +var candidateDataset = { name: 'candidate', display: 'name', limit: 5, @@ -169,7 +137,7 @@ const candidateDataset = { } }; -const committeeDataset = { +var committeeDataset = { name: 'committee', display: 'name', limit: 10, @@ -187,7 +155,7 @@ const committeeDataset = { } }; -const auditCommitteeDataset = { +var auditCommitteeDataset = { name: 'auditCommittees', display: 'name', limit: 10, @@ -203,7 +171,7 @@ const auditCommitteeDataset = { } }; -const auditCandidateDataset = { +var auditCandidateDataset = { name: 'auditCandidates', display: 'name', limit: 10, @@ -228,7 +196,7 @@ var individualDataset = { source: function(query, syncResults) { syncResults([ { - id: sanitizeValue(query), + id: helpers.sanitizeValue(query), type: 'individual' } ]); @@ -244,7 +212,7 @@ var individualDataset = { } }; -/** This is a fake dataset for showing an empty option with the query +/* This is a fake dataset for showing an empty option with the query * when clicked, this will submit the form to the DigitalGov search site */ var siteDataset = { @@ -252,7 +220,7 @@ var siteDataset = { source: function(query, syncResults) { syncResults([ { - id: sanitizeValue(query), + id: helpers.sanitizeValue(query), type: 'site' } ]); @@ -281,32 +249,6 @@ var typeaheadOpts = { hint: false }; -let autoCompleteOpts = window.tempOpts; -/*{ - selector: '.js-site-search', - // placeHolder: 'The opts worked!', - data: { - src: async q => { - // console.log('src! q: ', q); - let toReturn; - toReturn = [ - { food: 'Sauce - Thousand Island', cities: 'Soanindrariny', animals: 'Common boubou shrike' }, - { food: 'Wild Boar - Tenderloin', cities: 'Luthu', animals: 'Eastern diamondback rattlesnake' }, - { food: 'Goat - Whole Cut', cities: 'Kargowa', animals: 'Sheep, red' } - ]; - console.log('typeof toReturn: ', typeof toReturn); - return toReturn; - // src: [{ name: 'OBAMA, BARACK \/ JOSEPH R. BIDEN', office_sought: 'P', id: 'P80003338' }, { name: 'BIDEN, JOSEPH R JR', office_sought: 'P', id: 'P80000722' }, { name: 'BIDEN, JOSEPH R JR', office_sought: 'S', id: 'S8DE00012' }, { name: 'BIDEN, JOE R', office_sought: 'P', id: 'P60012143' }, { name: 'BIDEN, JOSEPH ROBINETTEE', office_sought: 'P', id: 'P60012135' }, { name: 'BIDEN, JR., JOSEPH R.', office_sought: 'P', id: 'P60012465' }], - // keys: ['name', 'id'], - }, - keys: ['food', 'cities', 'animals'] - }, - resultsList: { - tag: 'div', - id: '', - } -};*/ - /** * @class * @param {String} selector - A string to be used to find the element in the page. @@ -322,21 +264,9 @@ let autoCompleteOpts = window.tempOpts; * @property {Object} - null if no results. Otherwise we get back an {Object} for each item in the menu */ function Typeahead(selector, type, url) { - // this.$input = $(selector); - // this.url = url || '/'; - // this.typeahead = null; - // this.dataset = datasets[type]; - // this.init(); - // events.on('searchTypeChanged', this.handleChangeEvent.bind(this)); - // this.$input.on('keyup', this.setAria.bind(this)); -} -function AutoComplete(element, type, url) { - console.log('new AutoComplete!'); - - this.$input = element; + this.$input = $(selector); this.url = url || '/'; - this.autoComplete = null; - this.formerSelectionIndex; + this.typeahead = null; this.dataset = datasets[type]; @@ -344,190 +274,61 @@ function AutoComplete(element, type, url) { events.on('searchTypeChanged', this.handleChangeEvent.bind(this)); - this.$input.addEventListener('keyup', this.setAria.bind(this)); - this.$input.addEventListener('focus', this.handleFocus.bind(this)); + this.$input.on('keyup', this.setAria.bind(this)); } Typeahead.prototype.init = function() { - // if (this.typeahead) { - // this.$input.typeahead('destroy'); - // } - // this.typeahead = this.$input.typeahead(typeaheadOpts, this.dataset); - // this.$element = this.$input.parent('.twitter-typeahead'); - // this.$element.css('display', 'block'); - // this.$element.find('.tt-menu').attr('aria-live', 'polite'); - // this.$element.find('.tt-input').removeAttr('aria-readonly'); - // this.$element.find('.tt-input').attr('aria-expanded', 'false'); - // this.$input.on('typeahead:select', this.select.bind(this)); -}; - -AutoComplete.prototype.init = function() { - console.log('AutoComplete.init()'); - // TODO: do we need to destroy/reset one if it already exists? - // if (this.autoComplete) this.$input.typeahead('destroy'); - this.$input.value = ''; - - let theseOpts = autoCompleteOpts; - theseOpts.data = Object.assign(theseOpts.data, theseOpts ); - - // Create a new span to wrap the input, - // add the span to the page before the input - // and move the input into it - this.$element = document.createElement('span'); - this.$element.setAttribute('class', 'twitter-typeahead'); - this.$element.setAttribute('style', 'position: relative; display: block'); - this.$element.innerHTML = `\ - \ - \ -