workarea-classic_search_autocomplete 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +20 -0
  3. data/.eslintrc +35 -0
  4. data/.eslintrc.json +35 -0
  5. data/.github/workflows/ci.yml +60 -0
  6. data/.gitignore +19 -0
  7. data/.rubocop.yml +2 -0
  8. data/.stylelintrc +8 -0
  9. data/.stylelintrc.json +8 -0
  10. data/CHANGELOG.md +19 -0
  11. data/Gemfile +17 -0
  12. data/LICENSE +52 -0
  13. data/README.md +45 -0
  14. data/Rakefile +60 -0
  15. data/app/assets/javascripts/jquery_ui/storefront/categorized_autocomplete.js +53 -0
  16. data/app/assets/javascripts/workarea/storefront/modules/search_fields.js +69 -0
  17. data/app/assets/javascripts/workarea/storefront/templates/ui_menu_heading.jst.ejs +1 -0
  18. data/app/assets/javascripts/workarea/storefront/templates/ui_menu_item.jst.ejs +8 -0
  19. data/app/assets/stylesheets/jquery_ui/storefront/_ui_autocomplete.scss +17 -0
  20. data/app/assets/stylesheets/jquery_ui/storefront/_ui_menu.scss +40 -0
  21. data/app/controllers/.keep +0 -0
  22. data/app/controllers/workarea/storefront/searches_controller.decorator +14 -0
  23. data/app/queries/workarea/search/search_suggestions.rb +45 -0
  24. data/app/view_models/workarea/storefront/search_suggestion_view_model.rb +85 -0
  25. data/app/views/workarea/storefront/searches/index.json.jbuilder +1 -0
  26. data/bin/rails +25 -0
  27. data/config/initializers/appends.rb +26 -0
  28. data/config/initializers/workarea.rb +2 -0
  29. data/config/routes.rb +5 -0
  30. data/lib/workarea/classic_search_autocomplete.rb +11 -0
  31. data/lib/workarea/classic_search_autocomplete/engine.rb +10 -0
  32. data/lib/workarea/classic_search_autocomplete/version.rb +5 -0
  33. data/package.json +9 -0
  34. data/script/admin_ci +9 -0
  35. data/script/ci +11 -0
  36. data/script/core_ci +9 -0
  37. data/script/plugins_ci +9 -0
  38. data/script/storefront_ci +9 -0
  39. data/test/dummy/.ruby-version +1 -0
  40. data/test/dummy/Rakefile +6 -0
  41. data/test/dummy/app/assets/config/manifest.js +3 -0
  42. data/test/dummy/app/assets/images/.keep +0 -0
  43. data/test/dummy/app/assets/javascripts/application.js +14 -0
  44. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  45. data/test/dummy/app/controllers/application_controller.rb +2 -0
  46. data/test/dummy/app/controllers/concerns/.keep +0 -0
  47. data/test/dummy/app/helpers/application_helper.rb +2 -0
  48. data/test/dummy/app/jobs/application_job.rb +2 -0
  49. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  50. data/test/dummy/app/models/concerns/.keep +0 -0
  51. data/test/dummy/app/views/layouts/application.html.erb +15 -0
  52. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  53. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  54. data/test/dummy/bin/bundle +3 -0
  55. data/test/dummy/bin/rails +4 -0
  56. data/test/dummy/bin/rake +4 -0
  57. data/test/dummy/bin/setup +25 -0
  58. data/test/dummy/bin/update +25 -0
  59. data/test/dummy/config.ru +5 -0
  60. data/test/dummy/config/application.rb +34 -0
  61. data/test/dummy/config/boot.rb +5 -0
  62. data/test/dummy/config/environment.rb +5 -0
  63. data/test/dummy/config/environments/development.rb +52 -0
  64. data/test/dummy/config/environments/production.rb +83 -0
  65. data/test/dummy/config/environments/test.rb +45 -0
  66. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  67. data/test/dummy/config/initializers/assets.rb +12 -0
  68. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  69. data/test/dummy/config/initializers/content_security_policy.rb +25 -0
  70. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  71. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  72. data/test/dummy/config/initializers/inflections.rb +16 -0
  73. data/test/dummy/config/initializers/mime_types.rb +4 -0
  74. data/test/dummy/config/initializers/workarea.rb +5 -0
  75. data/test/dummy/config/initializers/wrap_parameters.rb +9 -0
  76. data/test/dummy/config/locales/en.yml +33 -0
  77. data/test/dummy/config/puma.rb +34 -0
  78. data/test/dummy/config/routes.rb +5 -0
  79. data/test/dummy/config/spring.rb +6 -0
  80. data/test/dummy/db/seeds.rb +2 -0
  81. data/test/dummy/lib/assets/.keep +0 -0
  82. data/test/dummy/log/.keep +0 -0
  83. data/test/integration/workarea/storefront/search_autocomplete_integration_test.rb +43 -0
  84. data/test/queries/workarea/search/search_suggestions_test.rb +77 -0
  85. data/test/system/workarea/storefront/search_autocomplete_analytics_system_test.rb +46 -0
  86. data/test/teaspoon_env.rb +6 -0
  87. data/test/test_helper.rb +10 -0
  88. data/test/view_models/workarea/storefront/search_suggestion_view_model_test.rb +42 -0
  89. data/workarea-classic_search_autocomplete.gemspec +25 -0
  90. data/yarn.lock +3265 -0
  91. metadata +156 -0
@@ -0,0 +1,69 @@
1
+ /**
2
+ * @namespace WORKAREA.searchFields
3
+ */
4
+ WORKAREA.registerModule('searchFields', (function () {
5
+ 'use strict';
6
+
7
+ var getSource = function (request, response) {
8
+ var endpoint = WORKAREA.routes.storefront.searchesPath();
9
+
10
+ $.getJSON(endpoint, { q: request.term }, function (data) {
11
+ response(data.results);
12
+ });
13
+ },
14
+
15
+ openSelected = function (event, ui) {
16
+ if (ui.item.type === "Products") {
17
+ WORKAREA.analytics.fireCallback(
18
+ 'productClick',
19
+ ui.item.analytics
20
+ );
21
+ }
22
+
23
+ if (WORKAREA.analytics.domEventsDisabled()) { return; }
24
+ window.location = ui.item.url;
25
+ },
26
+
27
+ /**
28
+ * iOS touch devices treat touch events as mouseenter unless there is no
29
+ * change in the UI, like a menu-selected state. By unbinding the
30
+ * mouseenter event we force those devices to treat the touch event as a
31
+ * click. This prevents the user having to tap twice to open a search
32
+ * autocomplete result.
33
+ */
34
+ openOnTouchDevices = function () {
35
+ $('.ui-autocomplete').off('mouseenter');
36
+ },
37
+
38
+ getConfig = function () {
39
+ var config = WORKAREA.config.searchFieldsAutocomplete || {
40
+ minLength: 2
41
+ };
42
+
43
+ return _.assign({}, config, {
44
+ source: getSource,
45
+ select: openSelected,
46
+ open: openOnTouchDevices
47
+ });
48
+ },
49
+
50
+ /**
51
+ * Queries the DOM for either a `data-search-field` or
52
+ * `[id=storefront_search] attribute to hook onto, selecting the first
53
+ * that is found, before initializing the customized jQuery UI
54
+ * Autocomplete method `categorizedAutocomplete`.
55
+ *
56
+ * @method
57
+ * @name init
58
+ * @memberof WORKAREA.searchFields
59
+ */
60
+ init = function ($scope) {
61
+ $('[data-search-field], #storefront_search', $scope)
62
+ .first()
63
+ .categorizedAutocomplete(getConfig());
64
+ };
65
+
66
+ return {
67
+ init: init
68
+ };
69
+ }()));
@@ -0,0 +1 @@
1
+ <li class="ui-menu-heading"><%= categoryName %></li>
@@ -0,0 +1,8 @@
1
+ <li class="ui-menu-item">
2
+ <span class="ui-menu-item-wrapper">
3
+ <% if (image) { %>
4
+ <img src="<%= image %>" alt="">
5
+ <% } %>
6
+ <%= label %>
7
+ </span>
8
+ </li>
@@ -0,0 +1,17 @@
1
+ /*------------------------------------*\
2
+ #UI-AUTOCOMPLETE
3
+ \*------------------------------------*/
4
+
5
+ $ui-autocomplete-bg-color: $white !default;
6
+
7
+ $ui-autocomplete-width: 190px !default;
8
+
9
+
10
+ .ui-autocomplete {
11
+ @extend %list-reset;
12
+ position: absolute;
13
+ z-index: index($components, ui-autocomplete);
14
+ padding: $spacing-unit;
15
+ width: $ui-autocomplete-width;
16
+ background: $ui-autocomplete-bg-color;
17
+ }
@@ -0,0 +1,40 @@
1
+ /*------------------------------------*\
2
+ #UI-MENU
3
+ \*------------------------------------*/
4
+
5
+ $ui-menu-item-selected-color: $highlight-color !default;
6
+
7
+
8
+ /**
9
+ * 1. default menu item state
10
+ * 2. hovered menu item state
11
+ * 3. the displayed result image
12
+ * 4. menu headings
13
+ */
14
+
15
+ .ui-menu {
16
+ .ui-menu-item { /* [1] */
17
+ @extend %clearfix;
18
+ padding: ($spacing-unit / 2) 0;
19
+ cursor: pointer;
20
+
21
+ &:hover { /* [2] */
22
+ background: $ui-menu-item-selected-color;
23
+ }
24
+
25
+ img { /* [3] */
26
+ margin: 0 ($spacing-unit / 2) 0 0;
27
+ float: left;
28
+ }
29
+ }
30
+
31
+ .ui-menu-heading { /* [4] */
32
+ margin: ($spacing-unit / 2) 0;
33
+ padding: ($spacing-unit / 2) 0;
34
+ font-weight: bold;
35
+ }
36
+ }
37
+
38
+ .ui-menu-item-wrapper {
39
+ display: block;
40
+ }
File without changes
@@ -0,0 +1,14 @@
1
+ module Workarea
2
+ decorate Storefront::SearchesController, with: :classic_search_autocomplete do
3
+ def index
4
+ render nothing: true and return if search_query.blank?
5
+ autocomplete_params = params.permit(:q)
6
+
7
+ search = Search::SearchSuggestions.new(autocomplete_params)
8
+
9
+ @results = search.results.map do |result|
10
+ Storefront::SearchSuggestionViewModel.new(result).to_h
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,45 @@
1
+ module Workarea
2
+ module Search
3
+ class SearchSuggestions
4
+ include Query
5
+ include ProductDisplayRules
6
+
7
+ document Search::Storefront
8
+
9
+ def results
10
+ response['hits']['hits']
11
+ end
12
+
13
+ def sanitized_string
14
+ @sanitized_query ||= QueryString.new(params[:q]).sanitized
15
+ end
16
+
17
+ def query
18
+ {
19
+ bool: {
20
+ must: [
21
+ {
22
+ match_phrase_prefix: {
23
+ 'content.name': {
24
+ query: sanitized_string,
25
+ max_expansions: 10
26
+ }
27
+ }
28
+ },
29
+ active_for_segments_clause,
30
+ preview_current_release_clause
31
+ ]
32
+ }
33
+ }
34
+ end
35
+
36
+ def sort
37
+ { type: :desc }
38
+ end
39
+
40
+ def size
41
+ Workarea.config.search_suggestions
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,85 @@
1
+ module Workarea
2
+ module Storefront
3
+ class SearchSuggestionViewModel < ApplicationViewModel
4
+ include I18n::DefaultUrlOptions
5
+ include Storefront::Engine.routes.url_helpers
6
+ include Search::LoadProductResults
7
+ include AnalyticsHelper
8
+
9
+ def to_h
10
+ {
11
+ value: name,
12
+ type: type,
13
+ image: image,
14
+ url: url,
15
+ analytics: analytics
16
+ }
17
+ end
18
+
19
+ def source
20
+ model['_source']
21
+ end
22
+
23
+ def name
24
+ source['content']['name']
25
+ end
26
+
27
+ def type
28
+ t("workarea.storefront.searches.#{suggestion_type.pluralize}")
29
+ end
30
+
31
+ def image
32
+ return if source['cache']['image'].blank?
33
+
34
+ image_url = URI.parse(source['cache']['image'])
35
+
36
+ if asset_host.present?
37
+ image_url.scheme = asset_host.scheme
38
+ image_url.host = asset_host.host
39
+ end
40
+
41
+ image_url.to_s
42
+ end
43
+
44
+ def asset_host
45
+ URI.parse(Rails.application.config.action_controller.asset_host)
46
+ rescue URI::InvalidURIError
47
+ nil
48
+ end
49
+
50
+ def suggestion_type
51
+ source['type']
52
+ end
53
+
54
+ def analytics
55
+ return unless suggestion_type == 'product'
56
+
57
+ product_analytics_data(product)
58
+ end
59
+
60
+ def product
61
+ @product ||=
62
+ begin
63
+ loaded = load_model_from(model)
64
+
65
+ Storefront::ProductViewModel.wrap(
66
+ loaded[:model],
67
+ loaded.slice(:inventory, :pricing)
68
+ )
69
+ end
70
+ end
71
+
72
+ def url
73
+ if suggestion_type == 'product'
74
+ product_path(product)
75
+ elsif suggestion_type == 'search'
76
+ search_path(q: name)
77
+ elsif suggestion_type == 'category'
78
+ category_path(source['slug'])
79
+ elsif suggestion_type == 'page'
80
+ page_path(source['slug'])
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1 @@
1
+ json.results @results
data/bin/rails ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails gems
3
+ # installed from the root of your application.
4
+
5
+ ENGINE_ROOT = File.expand_path('..', __dir__)
6
+ ENGINE_PATH = File.expand_path('../lib/workarea/classic_search_autocomplete/engine', __dir__)
7
+ APP_PATH = File.expand_path('../test/dummy/config/application', __dir__)
8
+
9
+ # Set up gems listed in the Gemfile.
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
11
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
12
+
13
+ require "rails"
14
+ # Pick the frameworks you want:
15
+ require "active_model/railtie"
16
+ require "active_job/railtie"
17
+ # require "active_record/railtie"
18
+ # require "active_storage/engine"
19
+ require "action_controller/railtie"
20
+ require "action_mailer/railtie"
21
+ require "action_view/railtie"
22
+ # require "action_cable/engine"
23
+ require "sprockets/railtie"
24
+ require "rails/test_unit/railtie"
25
+ require 'rails/engine/commands'
@@ -0,0 +1,26 @@
1
+ Workarea.append_javascripts(
2
+ 'storefront.dependencies',
3
+ 'jquery-ui/widgets/autocomplete'
4
+ )
5
+
6
+ Workarea.append_javascripts(
7
+ 'storefront.templates',
8
+ 'workarea/storefront/templates/ui_menu_heading',
9
+ 'workarea/storefront/templates/ui_menu_item'
10
+ )
11
+
12
+ Workarea.append_javascripts(
13
+ 'storefront.extensions',
14
+ 'jquery_ui/storefront/categorized_autocomplete'
15
+ )
16
+
17
+ Workarea.append_javascripts(
18
+ 'storefront.modules',
19
+ 'workarea/storefront/modules/search_fields'
20
+ )
21
+
22
+ Workarea.append_stylesheets(
23
+ 'storefront.dependencies',
24
+ 'jquery_ui/storefront/ui_menu',
25
+ 'jquery_ui/storefront/ui_autocomplete'
26
+ )
@@ -0,0 +1,2 @@
1
+ Workarea.configure do |config|
2
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,5 @@
1
+ Workarea::Storefront::Engine.routes.draw do
2
+ scope '(:locale)', constraints: Workarea::I18n.routes_constraint do
3
+ resources :searches, only: :index
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ require 'workarea'
2
+ require 'workarea/storefront'
3
+ require 'workarea/admin'
4
+
5
+ require 'workarea/classic_search_autocomplete/engine'
6
+ require 'workarea/classic_search_autocomplete/version'
7
+
8
+ module Workarea
9
+ module ClassicSearchAutocomplete
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ require 'workarea/classic_search_autocomplete'
2
+
3
+ module Workarea
4
+ module ClassicSearchAutocomplete
5
+ class Engine < ::Rails::Engine
6
+ include Workarea::Plugin
7
+ isolate_namespace Workarea::ClassicSearchAutocomplete
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ module Workarea
2
+ module ClassicSearchAutocomplete
3
+ VERSION = '1.0.0'.freeze
4
+ end
5
+ end
data/package.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "devDependencies": {
3
+ "eslint": "~5.16.0",
4
+ "eslint-config-recommended": "~4.0.0",
5
+ "stylelint": "~10.1.0",
6
+ "stylelint-config-recommended-scss": "~3.3.0",
7
+ "stylelint-scss": "~3.9.2"
8
+ }
9
+ }
data/script/admin_ci ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+
3
+ gem install bundler
4
+ bundle update
5
+
6
+ CI_GEM_PATH="$(bundle show workarea-ci)"
7
+ . ${CI_GEM_PATH}/exe/workarea-set-ci-env
8
+
9
+ bin/rails app:workarea:test:admin
data/script/ci ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env bash
2
+
3
+ gem install bundler
4
+ bundle update
5
+
6
+ CI_GEM_PATH="$(bundle show workarea-ci)"
7
+ . ${CI_GEM_PATH}/exe/workarea-set-ci-env
8
+
9
+ bundle exec workarea-lint
10
+ bundle exec workarea-audit
11
+ bin/rails test test/**/*_test.rb
data/script/core_ci ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+
3
+ gem install bundler
4
+ bundle update
5
+
6
+ CI_GEM_PATH="$(bundle show workarea-ci)"
7
+ . ${CI_GEM_PATH}/exe/workarea-set-ci-env
8
+
9
+ bin/rails app:workarea:test:core
data/script/plugins_ci ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+
3
+ gem install bundler
4
+ bundle update
5
+
6
+ CI_GEM_PATH="$(bundle show workarea-ci)"
7
+ . ${CI_GEM_PATH}/exe/workarea-set-ci-env
8
+
9
+ bin/rails app:workarea:test:plugins