workarea-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 (88) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +20 -0
  3. data/.eslintrc.json +35 -0
  4. data/.github/workflows/ci.yml +60 -0
  5. data/.gitignore +20 -0
  6. data/.rubocop.yml +3 -0
  7. data/.stylelintrc.json +8 -0
  8. data/CHANGELOG.md +36 -0
  9. data/Gemfile +17 -0
  10. data/README.md +17 -0
  11. data/Rakefile +60 -0
  12. data/app/assets/javascripts/workarea/storefront/search_autocomplete/config.js +12 -0
  13. data/app/assets/javascripts/workarea/storefront/search_autocomplete/modules/search_autocomplete.js +76 -0
  14. data/app/assets/stylesheets/workarea/storefront/search_autocomplete/components/_page_header.scss +9 -0
  15. data/app/assets/stylesheets/workarea/storefront/search_autocomplete/components/_search_autocomplete.scss +53 -0
  16. data/app/controllers/workarea/storefront/searches_controller.decorator +10 -0
  17. data/app/models/workarea/metrics/search_by_week.decorator +37 -0
  18. data/app/view_models/workarea/storefront/search_autocomplete_products_view_model.rb +13 -0
  19. data/app/view_models/workarea/storefront/search_autocomplete_view_model.rb +66 -0
  20. data/app/views/workarea/storefront/searches/_autocomplete_placeholder.html.haml +2 -0
  21. data/app/views/workarea/storefront/searches/autocomplete.html.haml +34 -0
  22. data/bin/rails +25 -0
  23. data/config/initializers/appends.rb +20 -0
  24. data/config/initializers/content.rb +3 -0
  25. data/config/initializers/fields.rb +13 -0
  26. data/config/locales/en.yml +8 -0
  27. data/config/routes.rb +7 -0
  28. data/lib/workarea/search_autocomplete.rb +11 -0
  29. data/lib/workarea/search_autocomplete/engine.rb +8 -0
  30. data/lib/workarea/search_autocomplete/version.rb +5 -0
  31. data/package.json +9 -0
  32. data/script/admin_ci +9 -0
  33. data/script/ci +11 -0
  34. data/script/core_ci +9 -0
  35. data/script/plugins_ci +9 -0
  36. data/script/storefront_ci +9 -0
  37. data/test/dummy/.ruby-version +1 -0
  38. data/test/dummy/Rakefile +6 -0
  39. data/test/dummy/app/assets/config/manifest.js +3 -0
  40. data/test/dummy/app/assets/images/.keep +0 -0
  41. data/test/dummy/app/assets/javascripts/application.js +14 -0
  42. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  43. data/test/dummy/app/controllers/application_controller.rb +2 -0
  44. data/test/dummy/app/controllers/concerns/.keep +0 -0
  45. data/test/dummy/app/helpers/application_helper.rb +2 -0
  46. data/test/dummy/app/jobs/application_job.rb +2 -0
  47. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  48. data/test/dummy/app/models/concerns/.keep +0 -0
  49. data/test/dummy/app/views/layouts/application.html.erb +15 -0
  50. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  51. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  52. data/test/dummy/bin/bundle +3 -0
  53. data/test/dummy/bin/rails +4 -0
  54. data/test/dummy/bin/rake +4 -0
  55. data/test/dummy/bin/setup +25 -0
  56. data/test/dummy/bin/update +25 -0
  57. data/test/dummy/config.ru +5 -0
  58. data/test/dummy/config/application.rb +34 -0
  59. data/test/dummy/config/boot.rb +5 -0
  60. data/test/dummy/config/environment.rb +5 -0
  61. data/test/dummy/config/environments/development.rb +52 -0
  62. data/test/dummy/config/environments/production.rb +83 -0
  63. data/test/dummy/config/environments/test.rb +45 -0
  64. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  65. data/test/dummy/config/initializers/assets.rb +12 -0
  66. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  67. data/test/dummy/config/initializers/content_security_policy.rb +25 -0
  68. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  69. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  70. data/test/dummy/config/initializers/inflections.rb +16 -0
  71. data/test/dummy/config/initializers/mime_types.rb +4 -0
  72. data/test/dummy/config/initializers/workarea.rb +5 -0
  73. data/test/dummy/config/initializers/wrap_parameters.rb +9 -0
  74. data/test/dummy/config/locales/en.yml +33 -0
  75. data/test/dummy/config/puma.rb +34 -0
  76. data/test/dummy/config/routes.rb +5 -0
  77. data/test/dummy/config/spring.rb +6 -0
  78. data/test/dummy/db/seeds.rb +2 -0
  79. data/test/dummy/lib/assets/.keep +0 -0
  80. data/test/dummy/log/.keep +0 -0
  81. data/test/integration/workarea/storefront/search_autocomplete_integration_test.rb +29 -0
  82. data/test/models/workarea/metrics/search_by_week_autocomplete_test.rb +20 -0
  83. data/test/system/workarea/storefront/search_autocomplete_system_test.rb +171 -0
  84. data/test/teaspoon_env.rb +6 -0
  85. data/test/test_helper.rb +10 -0
  86. data/test/view_models/workarea/storefront/search_autocomplete_view_model_test.rb +125 -0
  87. data/workarea-search_autocomplete.gemspec +23 -0
  88. metadata +148 -0
@@ -0,0 +1,10 @@
1
+ module Workarea
2
+ decorate Storefront::SearchesController, with: 'search_autocomplete' do
3
+ def autocomplete
4
+ @autocomplete = Storefront::SearchAutocompleteViewModel.wrap(
5
+ QueryString.new(params[:q]).pretty,
6
+ view_model_options
7
+ )
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,37 @@
1
+ module Workarea
2
+ decorate Metrics::SearchByWeek, with: 'search_autocomplete' do
3
+ decorated do
4
+ index(
5
+ { query_string: 1, total_results: 1, searches: 1 },
6
+ { partial_filter_expression: { total_results: { '$gt' => 0 } } }
7
+ )
8
+
9
+ scope :by_query_string, ->(string) { where(query_string: /^#{string}/) }
10
+ end
11
+
12
+ class_methods do
13
+ def autocomplete(query_string, max: 3)
14
+ pipeline = [
15
+ {
16
+ '$match' => {
17
+ total_results: { '$gt' => 0 },
18
+ query_string: { '$regex' => /^#{query_string}/ }
19
+ }
20
+ },
21
+ { '$project' => { 'query_id' => 1, 'searches' => 1, 'query_string' => 1 } },
22
+ {
23
+ '$group' => {
24
+ '_id' => '$query_id',
25
+ 'searches' => { '$sum' => '$searches' },
26
+ 'query_string' => { '$first' => '$query_string' }
27
+ }
28
+ },
29
+ { '$sort' => { 'searches' => -1 } },
30
+ { '$limit' => max }
31
+ ]
32
+
33
+ collection.aggregate(pipeline).map { |r| r['query_string'] }
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,13 @@
1
+ module Workarea
2
+ module Storefront
3
+ class SearchAutocompleteProductsViewModel < RecommendationsViewModel
4
+ def product_ids
5
+ model.results.map { |v| v['product_id'] }
6
+ end
7
+
8
+ def result_count
9
+ Workarea.config.storefront_search_autocomplete_max_products
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,66 @@
1
+ module Workarea
2
+ module Storefront
3
+ class SearchAutocompleteViewModel < ApplicationViewModel
4
+ def trending_products?
5
+ query_string.blank? || full_results.products.blank?
6
+ end
7
+ alias_method :trending_searches?, :trending_products?
8
+
9
+ def products
10
+ @products ||= begin
11
+ results = trending_products? ? trending_products : full_results.products
12
+ results.take(Workarea.config.storefront_search_autocomplete_max_products)
13
+ end
14
+ end
15
+
16
+ def searches
17
+ @searches ||= begin
18
+ results = trending_searches? ? trending_searches : autocomplete_searches
19
+ results.take(Workarea.config.storefront_search_autocomplete_max_searches)
20
+ end
21
+ end
22
+
23
+ def query_string
24
+ autocomplete_searches.first.presence || model
25
+ end
26
+
27
+ def content
28
+ Storefront::SearchViewModel.new(response, options)
29
+ end
30
+
31
+ private
32
+
33
+ def autocomplete_searches
34
+ return [] if model.blank?
35
+
36
+ @autocomplete_searches ||= Metrics::SearchByWeek.autocomplete(
37
+ model,
38
+ max: Workarea.config.storefront_search_autocomplete_max_searches
39
+ )
40
+ end
41
+
42
+ def trending_searches
43
+ @trending_searches ||= begin
44
+ Workarea::Insights::TrendingSearches.current.results.map do |v|
45
+ v['query_string']
46
+ end
47
+ end
48
+ end
49
+
50
+ def trending_products
51
+ @trending_products ||= SearchAutocompleteProductsViewModel.wrap(
52
+ Workarea::Insights::TrendingProducts.current,
53
+ options
54
+ )
55
+ end
56
+
57
+ def full_results
58
+ @full_results ||= SearchViewModel.wrap(response, options)
59
+ end
60
+
61
+ def response
62
+ @response ||= Search::StorefrontSearch.new(q: query_string).response
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,2 @@
1
+ #search_autocomplete.search-autocomplete.visually-hidden{ aria: { live: 'polite' } }
2
+
@@ -0,0 +1,34 @@
1
+ .grid.grid--rev
2
+ .grid__cell.grid__cell--25
3
+ - if @autocomplete.searches.any?
4
+ %span.search-autocomplete__heading
5
+ - if @autocomplete.trending_searches?
6
+ = t('workarea.storefront.search_autocomplete.trending_searches')
7
+ - else
8
+ = t('workarea.storefront.search_autocomplete.popular_searches')
9
+
10
+ %ul.search-autocomplete__searches
11
+ - @autocomplete.searches.each do |search|
12
+ %li.search-autocomplete__searches-item
13
+ = link_to search, search_path(q: search), class: 'search-autocomplete__searches-link'
14
+
15
+ = append_partials('storefront.search_autocomplete_under_searches')
16
+
17
+ .grid__cell.grid__cell--75
18
+ - if @autocomplete.products.any?
19
+ .search-autocomplete__products
20
+ %span.search-autocomplete__heading
21
+ - if @autocomplete.trending_products?
22
+ = t('workarea.storefront.search_autocomplete.trending_products')
23
+ - else
24
+ = t('workarea.storefront.search_autocomplete.products_heading', term: @autocomplete.query_string)
25
+
26
+ .grid
27
+ - @autocomplete.products.each do |product|
28
+ .grid__cell.grid__cell--25
29
+ .product-summary
30
+ = render 'workarea/storefront/products/summary', product: product
31
+
32
+ - if @autocomplete.content.customization_content_blocks_for('autocomplete').present?
33
+ .autocomplete.content-autocomplete__content
34
+ = render_content_blocks(@autocomplete.content.customization_content_blocks_for('autocomplete'))
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/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,20 @@
1
+ Workarea.append_partials(
2
+ 'storefront.page_header_search_value',
3
+ 'workarea/storefront/searches/autocomplete_placeholder'
4
+ )
5
+
6
+ Workarea.append_javascripts(
7
+ 'storefront.config',
8
+ 'workarea/storefront/search_autocomplete/config'
9
+ )
10
+
11
+ Workarea.append_javascripts(
12
+ 'storefront.modules',
13
+ 'workarea/storefront/search_autocomplete/modules/search_autocomplete'
14
+ )
15
+
16
+ Workarea.append_stylesheets(
17
+ 'storefront.components',
18
+ 'workarea/storefront/search_autocomplete/components/page_header',
19
+ 'workarea/storefront/search_autocomplete/components/search_autocomplete'
20
+ )
@@ -0,0 +1,3 @@
1
+ Workarea.configure do |config|
2
+ config.content_areas['customization'].append('autocomplete')
3
+ end
@@ -0,0 +1,13 @@
1
+ Workarea::Configuration.define_fields do
2
+ fieldset 'Search', namespaced: false do
3
+ field 'Storefront Search Autocomplete Max Searches',
4
+ type: :integer,
5
+ default: 5,
6
+ description: 'The max number of search autocomplete suggestions to show in the storefront.'
7
+
8
+ field 'Storefront Search Autocomplete Max Products',
9
+ type: :integer,
10
+ default: 4,
11
+ description: 'The max number of search autocomplete products to show in the storefront.'
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ en:
2
+ workarea:
3
+ storefront:
4
+ search_autocomplete:
5
+ products_heading: "Product Results: %{term}"
6
+ trending_products: Trending Products
7
+ trending_searches: Trending Searches
8
+ popular_searches: Popular Searches
data/config/routes.rb ADDED
@@ -0,0 +1,7 @@
1
+ Workarea::Storefront::Engine.routes.draw do
2
+ scope '(:locale)', constraints: Workarea::I18n.routes_constraint do
3
+ resource :search, only: [] do
4
+ get 'autocomplete'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ require 'workarea'
2
+ require 'workarea/storefront'
3
+ require 'workarea/admin'
4
+
5
+ require 'workarea/search_autocomplete/engine'
6
+ require 'workarea/search_autocomplete/version'
7
+
8
+ module Workarea
9
+ module SearchAutocomplete
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ module Workarea
2
+ module SearchAutocomplete
3
+ class Engine < ::Rails::Engine
4
+ include Workarea::Plugin
5
+ isolate_namespace Workarea::SearchAutocomplete
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ module Workarea
2
+ module SearchAutocomplete
3
+ VERSION = '1.0.0'
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
@@ -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:storefront
@@ -0,0 +1 @@
1
+ 2.6.3
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require_relative 'config/application'
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,3 @@
1
+ //= link_tree ../images
2
+ //= link_directory ../javascripts .js
3
+ //= link_directory ../stylesheets .css
File without changes
@@ -0,0 +1,14 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require rails-ujs
14
+ //= require_tree .
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,2 @@
1
+ class ApplicationController < ActionController::Base
2
+ end
File without changes
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ class ApplicationJob < ActiveJob::Base
2
+ end
@@ -0,0 +1,4 @@
1
+ class ApplicationMailer < ActionMailer::Base
2
+ default from: 'from@example.com'
3
+ layout 'mailer'
4
+ end
File without changes
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Dummy</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= stylesheet_link_tag 'application', media: 'all' %>
9
+ <%= javascript_include_tag 'application' %>
10
+ </head>
11
+
12
+ <body>
13
+ <%= yield %>
14
+ </body>
15
+ </html>