workarea-classic_search_autocomplete 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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