workarea-classic_search_autocomplete 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +20 -0
- data/.eslintrc +35 -0
- data/.eslintrc.json +35 -0
- data/.github/workflows/ci.yml +60 -0
- data/.gitignore +19 -0
- data/.rubocop.yml +2 -0
- data/.stylelintrc +8 -0
- data/.stylelintrc.json +8 -0
- data/CHANGELOG.md +19 -0
- data/Gemfile +17 -0
- data/LICENSE +52 -0
- data/README.md +45 -0
- data/Rakefile +60 -0
- data/app/assets/javascripts/jquery_ui/storefront/categorized_autocomplete.js +53 -0
- data/app/assets/javascripts/workarea/storefront/modules/search_fields.js +69 -0
- data/app/assets/javascripts/workarea/storefront/templates/ui_menu_heading.jst.ejs +1 -0
- data/app/assets/javascripts/workarea/storefront/templates/ui_menu_item.jst.ejs +8 -0
- data/app/assets/stylesheets/jquery_ui/storefront/_ui_autocomplete.scss +17 -0
- data/app/assets/stylesheets/jquery_ui/storefront/_ui_menu.scss +40 -0
- data/app/controllers/.keep +0 -0
- data/app/controllers/workarea/storefront/searches_controller.decorator +14 -0
- data/app/queries/workarea/search/search_suggestions.rb +45 -0
- data/app/view_models/workarea/storefront/search_suggestion_view_model.rb +85 -0
- data/app/views/workarea/storefront/searches/index.json.jbuilder +1 -0
- data/bin/rails +25 -0
- data/config/initializers/appends.rb +26 -0
- data/config/initializers/workarea.rb +2 -0
- data/config/routes.rb +5 -0
- data/lib/workarea/classic_search_autocomplete.rb +11 -0
- data/lib/workarea/classic_search_autocomplete/engine.rb +10 -0
- data/lib/workarea/classic_search_autocomplete/version.rb +5 -0
- data/package.json +9 -0
- data/script/admin_ci +9 -0
- data/script/ci +11 -0
- data/script/core_ci +9 -0
- data/script/plugins_ci +9 -0
- data/script/storefront_ci +9 -0
- data/test/dummy/.ruby-version +1 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/config/manifest.js +3 -0
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/javascripts/application.js +14 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +2 -0
- data/test/dummy/app/controllers/concerns/.keep +0 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/jobs/application_job.rb +2 -0
- data/test/dummy/app/mailers/application_mailer.rb +4 -0
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/views/layouts/application.html.erb +15 -0
- data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +25 -0
- data/test/dummy/bin/update +25 -0
- data/test/dummy/config.ru +5 -0
- data/test/dummy/config/application.rb +34 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +52 -0
- data/test/dummy/config/environments/production.rb +83 -0
- data/test/dummy/config/environments/test.rb +45 -0
- data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
- data/test/dummy/config/initializers/assets.rb +12 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/content_security_policy.rb +25 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/workarea.rb +5 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +9 -0
- data/test/dummy/config/locales/en.yml +33 -0
- data/test/dummy/config/puma.rb +34 -0
- data/test/dummy/config/routes.rb +5 -0
- data/test/dummy/config/spring.rb +6 -0
- data/test/dummy/db/seeds.rb +2 -0
- data/test/dummy/lib/assets/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/integration/workarea/storefront/search_autocomplete_integration_test.rb +43 -0
- data/test/queries/workarea/search/search_suggestions_test.rb +77 -0
- data/test/system/workarea/storefront/search_autocomplete_analytics_system_test.rb +46 -0
- data/test/teaspoon_env.rb +6 -0
- data/test/test_helper.rb +10 -0
- data/test/view_models/workarea/storefront/search_suggestion_view_model_test.rb +42 -0
- data/workarea-classic_search_autocomplete.gemspec +25 -0
- data/yarn.lock +3265 -0
- 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,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
|
+
)
|
data/config/routes.rb
ADDED
@@ -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
|
data/package.json
ADDED
data/script/admin_ci
ADDED
data/script/ci
ADDED
data/script/core_ci
ADDED