workarea-swatches 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +20 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  4. data/.github/ISSUE_TEMPLATE/documentation-request.md +17 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  6. data/.gitignore +21 -0
  7. data/CHANGELOG.md +121 -0
  8. data/CODE_OF_CONDUCT.md +3 -0
  9. data/CONTRIBUTING.md +3 -0
  10. data/Gemfile +17 -0
  11. data/LICENSE +52 -0
  12. data/README.md +40 -0
  13. data/Rakefile +60 -0
  14. data/app/assets/javascripts/workarea/storefront/swatches/modules/product_summary_swatches.js +63 -0
  15. data/app/assets/stylesheets/workarea/storefront/swatches/components/_option_button.scss +17 -0
  16. data/app/assets/stylesheets/workarea/storefront/swatches/components/_swatch_facet.scss +25 -0
  17. data/app/assets/stylesheets/workarea/storefront/swatches/components/_swatch_options.scss +54 -0
  18. data/app/controllers/workarea/admin/catalog_product_swatches_controller.rb +47 -0
  19. data/app/controllers/workarea/admin/catalog_swatches_controller.rb +48 -0
  20. data/app/controllers/workarea/storefront/products_controller.decorator +12 -0
  21. data/app/helpers/workarea/storefront/swatches_helper.rb +84 -0
  22. data/app/models/workarea/catalog/product.decorator +7 -0
  23. data/app/models/workarea/catalog/product_swatch.rb +10 -0
  24. data/app/models/workarea/catalog/swatch.rb +37 -0
  25. data/app/models/workarea/search/settings.decorator +8 -0
  26. data/app/queries/workarea/search/category_browse.decorator +7 -0
  27. data/app/queries/workarea/search/product_search.decorator +7 -0
  28. data/app/queries/workarea/search/swatched_facets.rb +19 -0
  29. data/app/queries/workarea/search/swatches_facet.rb +13 -0
  30. data/app/seeds/workarea/swatches_seeds.rb +37 -0
  31. data/app/view_models/workarea/storefront/product_templates/swatches_view_model.rb +57 -0
  32. data/app/views/workarea/admin/activities/_catalog_product_swatch_create.html.haml +10 -0
  33. data/app/views/workarea/admin/activities/_catalog_product_swatch_destroy.html.haml +10 -0
  34. data/app/views/workarea/admin/activities/_catalog_swatch_create.html.haml +12 -0
  35. data/app/views/workarea/admin/activities/_catalog_swatch_destroy.html.haml +10 -0
  36. data/app/views/workarea/admin/catalog_product_swatches/edit.html.haml +52 -0
  37. data/app/views/workarea/admin/catalog_product_swatches/index.html.haml +65 -0
  38. data/app/views/workarea/admin/catalog_products/_swatches_card.html.haml +27 -0
  39. data/app/views/workarea/admin/catalog_swatches/_primary_navigation.html.haml +1 -0
  40. data/app/views/workarea/admin/catalog_swatches/index.html.haml +78 -0
  41. data/app/views/workarea/api/storefront/facets/_swatches.json.jbuilder +17 -0
  42. data/app/views/workarea/storefront/facets/_swatches.html.haml +14 -0
  43. data/app/views/workarea/storefront/products/_swatch_summary.html.haml +17 -0
  44. data/app/views/workarea/storefront/products/templates/_swatches.html.haml +79 -0
  45. data/bin/rails +19 -0
  46. data/config/initializers/dragonfly.rb +11 -0
  47. data/config/initializers/workarea.rb +40 -0
  48. data/config/locales/en.yml +52 -0
  49. data/config/routes.rb +15 -0
  50. data/lib/tasks/swatches_tasks.rake +4 -0
  51. data/lib/workarea/swatches.rb +11 -0
  52. data/lib/workarea/swatches/engine.rb +12 -0
  53. data/lib/workarea/swatches/version.rb +5 -0
  54. data/test/dummy/Rakefile +6 -0
  55. data/test/dummy/app/assets/config/manifest.js +4 -0
  56. data/test/dummy/app/assets/javascripts/application.js +13 -0
  57. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  58. data/test/dummy/app/controllers/application_controller.rb +3 -0
  59. data/test/dummy/app/helpers/application_helper.rb +2 -0
  60. data/test/dummy/app/jobs/application_job.rb +2 -0
  61. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  62. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  63. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  64. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  65. data/test/dummy/bin/bundle +3 -0
  66. data/test/dummy/bin/rails +4 -0
  67. data/test/dummy/bin/rake +4 -0
  68. data/test/dummy/bin/setup +30 -0
  69. data/test/dummy/bin/update +26 -0
  70. data/test/dummy/bin/yarn +11 -0
  71. data/test/dummy/config.ru +5 -0
  72. data/test/dummy/config/application.rb +30 -0
  73. data/test/dummy/config/boot.rb +5 -0
  74. data/test/dummy/config/cable.yml +10 -0
  75. data/test/dummy/config/environment.rb +5 -0
  76. data/test/dummy/config/environments/development.rb +51 -0
  77. data/test/dummy/config/environments/production.rb +88 -0
  78. data/test/dummy/config/environments/test.rb +44 -0
  79. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  80. data/test/dummy/config/initializers/assets.rb +14 -0
  81. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  82. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  83. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  84. data/test/dummy/config/initializers/inflections.rb +16 -0
  85. data/test/dummy/config/initializers/mime_types.rb +4 -0
  86. data/test/dummy/config/initializers/workarea.rb +5 -0
  87. data/test/dummy/config/initializers/wrap_parameters.rb +9 -0
  88. data/test/dummy/config/locales/en.yml +33 -0
  89. data/test/dummy/config/puma.rb +56 -0
  90. data/test/dummy/config/routes.rb +5 -0
  91. data/test/dummy/config/secrets.yml +32 -0
  92. data/test/dummy/config/spring.rb +6 -0
  93. data/test/dummy/db/seeds.rb +2 -0
  94. data/test/dummy/log/.keep +0 -0
  95. data/test/factories/workarea/swatches.rb +14 -0
  96. data/test/integration/workarea/admin/product_swatches_integration_test.rb +43 -0
  97. data/test/integration/workarea/admin/swatches_integration_test.rb +37 -0
  98. data/test/integration/workarea/storefront/swatches_integration_test.rb +72 -0
  99. data/test/system/workarea/admin/swatches_system_test.rb +58 -0
  100. data/test/system/workarea/storefront/browse_swatches_system_test.rb +50 -0
  101. data/test/system/workarea/storefront/swatches_system_test.rb +101 -0
  102. data/test/teaspoon_env.rb +6 -0
  103. data/test/test_helper.rb +10 -0
  104. data/test/view_models/workarea/storefront/product_templates/swatches_view_model_test.rb +74 -0
  105. data/workarea-swatches.gemspec +19 -0
  106. metadata +147 -0
@@ -0,0 +1,17 @@
1
+ /*------------------------------------*\
2
+ #OPTION-BUTTON
3
+ \*------------------------------------*/
4
+
5
+ /**
6
+ * Extend the `.option-button` class from Workarea to include a swatch option.
7
+ *
8
+ * 1. These dimensions match the :small_thumb processor as configured out of the
9
+ * box
10
+ */
11
+ $option-button-swatch-height: 60px !default; /* [1] */
12
+ $option-button-swatch-width: 60px !default; /* [1] */
13
+
14
+ .option-button__swatch {
15
+ height: $option-button-swatch-height;
16
+ width: $option-button-swatch-width;
17
+ }
@@ -0,0 +1,25 @@
1
+ /*------------------------------------*\
2
+ #SWATCH-FACET
3
+ \*------------------------------------*/
4
+
5
+ /**
6
+ * Each `.swatch-facet` also receives an ID modifier class, in order to offer
7
+ * more granular control over the component, if needed. Since this is a
8
+ * dynamically generated value, they are not listed here.
9
+ */
10
+
11
+ $swatch-facet-height: 20px !default;
12
+ $swatch-facet-width: 20px !default;
13
+
14
+ .swatch-facet {
15
+ display: inline-block;
16
+ width: $swatch-facet-width;
17
+ height: $swatch-facet-height;
18
+ text-decoration: none;
19
+ vertical-align: middle;
20
+ }
21
+
22
+ .swatch-facet--hex {}
23
+ .swatch-facet--image {}
24
+ .swatch-facet--empty {}
25
+
@@ -0,0 +1,54 @@
1
+ /*------------------------------------*\
2
+ #SWATCH-OPTIONS
3
+ \*------------------------------------*/
4
+
5
+ $swatch-options-outline: 1px solid $light-gray !default;
6
+ $swatch-options-selected-outline: 1px solid $highlight-color !default;
7
+
8
+ .swatch-options {}
9
+
10
+ .swatch-options--small {}
11
+
12
+ .swatch-options__label {
13
+ margin: 0;
14
+ }
15
+
16
+ .swatch-options__group {
17
+ @extend %inline-list;
18
+ }
19
+
20
+ .swatch-options__swatch {
21
+ position: relative;
22
+ margin: 0 0 $spacing-unit;
23
+ width: 44px;
24
+ height: 44px;
25
+ outline: $swatch-options-outline;
26
+
27
+ & + & {
28
+ margin-left: $spacing-unit;
29
+ }
30
+
31
+ .swatch-options--small & {
32
+ margin-top: $spacing-unit;
33
+ margin-bottom: 0;
34
+ width: 22px;
35
+ height: 22px;
36
+ }
37
+ }
38
+
39
+ .swatch-options__swatch--selected {
40
+ outline: $swatch-options-selected-outline;
41
+ }
42
+
43
+ .swatch-options__swatch-button,
44
+ .swatch-options__swatch-button-image,
45
+ .swatch-options__swatch-button-text {
46
+ display: block;
47
+ position: absolute;
48
+ top: 0;
49
+ right: 0;
50
+ bottom: 0;
51
+ left: 0;
52
+ }
53
+
54
+ .swatch-options__swatch-name {}
@@ -0,0 +1,47 @@
1
+ module Workarea
2
+ module Admin
3
+ class CatalogProductSwatchesController < Admin::ApplicationController
4
+ required_permissions :catalog
5
+ before_action :find_product
6
+
7
+ def index
8
+ end
9
+
10
+ def create
11
+ @swatch = @product.swatches.build(params[:swatch])
12
+
13
+ if @swatch.save
14
+ flash[:success] = t('workarea.admin.catalog_product_swatches.flash_messages.saved')
15
+ redirect_to catalog_product_swatches_path(@product)
16
+ else
17
+ render :index, status: :unprocessable_entity
18
+ end
19
+ end
20
+
21
+ def update
22
+ @swatch = @product.swatches.find(params[:id])
23
+
24
+ if @swatch.update_attributes(params[:swatch])
25
+ flash[:success] = t('workarea.admin.catalog_product_swatches.flash_messages.saved')
26
+ redirect_to catalog_product_swatches_path(@product)
27
+ else
28
+ flash[:error] = t('workarea.admin.catalog_product_swatches.flash_messages.error')
29
+ render :index
30
+ end
31
+ end
32
+
33
+ def destroy
34
+ @product.swatches.find(params[:id]).destroy
35
+ flash[:success] = t('workarea.admin.catalog_product_swatches.flash_messages.removed')
36
+ redirect_to catalog_product_swatches_path(@product)
37
+ end
38
+
39
+ private
40
+
41
+ def find_product
42
+ model = Catalog::Product.find_by(slug: params[:catalog_product_id])
43
+ @product = ProductViewModel.wrap(model, view_model_options)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,48 @@
1
+ module Workarea
2
+ module Admin
3
+ class CatalogSwatchesController < Admin::ApplicationController
4
+ required_permissions :catalog
5
+ before_action :find_swatches
6
+
7
+ def index
8
+ end
9
+
10
+ def create
11
+ @swatch = Catalog::Swatch.new(params[:swatch])
12
+
13
+ if @swatch.save
14
+ flash[:success] = t('workarea.admin.catalog_swatches.flash_messages.saved')
15
+ redirect_to catalog_swatches_path
16
+ else
17
+ flash[:error] = @swatch.errors.full_messages
18
+ render :index
19
+ end
20
+ end
21
+
22
+ def update
23
+ @swatch = Catalog::Swatch.find(params[:id])
24
+
25
+ if @swatch.update_attributes(params[:swatch])
26
+ flash[:success] = t('workarea.admin.catalog_swatches.flash_messages.saved')
27
+ redirect_to catalog_swatches_path
28
+ else
29
+ flash[:error] = t('workarea.admin.catalog_swatches.flash_messages.error')
30
+ render :index
31
+ end
32
+ end
33
+
34
+ def destroy
35
+ Catalog::Swatch.find(params[:id]).destroy
36
+ flash[:success] = t('workarea.admin.catalog_swatches.flash_messages.removed')
37
+ redirect_to catalog_swatches_path
38
+ end
39
+
40
+ private
41
+
42
+ def find_swatches
43
+ @swatches = Catalog::Swatch.all
44
+ @search_settings = Search::Settings.current
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,12 @@
1
+ module Workarea
2
+ decorate Storefront::ProductsController, with: :swatches do
3
+ def summary
4
+ @product = Storefront::ProductViewModel.wrap(
5
+ Catalog::Product.find_by(slug: params[:id]),
6
+ view_model_options
7
+ )
8
+
9
+ render partial: 'summary', locals: { product: @product }
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,84 @@
1
+ module Workarea
2
+ module Storefront
3
+ module SwatchesHelper
4
+ def render_swatch_option(selection, swatch)
5
+ id = selection.optionize
6
+
7
+ if swatch&.image?
8
+ image_tag(
9
+ swatch.image.process(:small_thumb).url,
10
+ alt: selection,
11
+ class: "option-button__image option-button__image--#{id}"
12
+ )
13
+ elsif swatch&.hex?
14
+ content_tag(
15
+ :div,
16
+ '',
17
+ class: "option-button__swatch option-button__swatch--#{id}",
18
+ style: "background-color: #{swatch.hex};"
19
+ )
20
+ end
21
+ end
22
+
23
+ def render_browse_swatch(selection, swatch)
24
+ id = selection.optionize
25
+
26
+ if swatch&.image?
27
+ image_tag(
28
+ swatch.image.process(:small_thumb).url,
29
+ alt: selection,
30
+ class: [
31
+ 'swatch-options__swatch-button-image',
32
+ "swatch-options__swatch-button-image--#{id}"
33
+ ].join(' ')
34
+ )
35
+ elsif swatch&.hex?
36
+ content_tag(
37
+ :div,
38
+ '',
39
+ class: [
40
+ 'swatch-options__swatch-button',
41
+ "swatch-options__swatch-button--#{id}"
42
+ ].join(' '),
43
+ style: "background-color: #{swatch.hex};"
44
+ )
45
+ else
46
+ content_tag(
47
+ :div,
48
+ selection,
49
+ class: 'swatch-options__swatch-button-text'
50
+ )
51
+ end
52
+ end
53
+
54
+ def render_swatch_facet(selection)
55
+ id = selection.optionize
56
+
57
+ @swatches_cache ||= Catalog::Swatch.all.to_a
58
+ swatch = @swatches_cache.detect { |s| s.id == id }
59
+ class_list = "swatch-facet swatch-facet--#{id}"
60
+
61
+ if swatch&.image?
62
+ image_tag(
63
+ swatch.image.process(:facet_swatch).url,
64
+ alt: selection,
65
+ class: "#{class_list} swatch-facet--image"
66
+ )
67
+ elsif swatch&.hex?
68
+ content_tag(
69
+ :div,
70
+ '',
71
+ class: "#{class_list} swatch-facet--hex",
72
+ style: "background-color: #{swatch.hex};"
73
+ )
74
+ else
75
+ content_tag(
76
+ :span,
77
+ '',
78
+ class: "#{class_list} swatch-facet--empty"
79
+ )
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,7 @@
1
+ module Workarea
2
+ decorate Catalog::Product, with: 'swatches' do
3
+ decorated do
4
+ embeds_many :swatches, class_name: 'Workarea::Catalog::ProductSwatch'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ module Workarea
2
+ module Catalog
3
+ class ProductSwatch < Swatch
4
+ embedded_in :product,
5
+ class_name: 'Workarea::Catalog::Product',
6
+ inverse_of: :swatches,
7
+ touch: true
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,37 @@
1
+ module Workarea
2
+ module Catalog
3
+ class Swatch
4
+ include ApplicationDocument
5
+ extend Dragonfly::Model
6
+
7
+ field :_id, type: String, default: -> { name.optionize }
8
+ field :name, type: String
9
+ field :hex, type: String
10
+ field :image_name, type: String
11
+ field :image_uid, type: String
12
+
13
+ validates :name, presence: true
14
+ validates :hex, length: { is: 7, allow_blank: true }
15
+ validates :id, uniqueness: true
16
+
17
+ dragonfly_accessor :image, app: :workarea
18
+
19
+ before_validation :normalize_hex
20
+
21
+ def image?
22
+ image.present?
23
+ end
24
+
25
+ def hex?
26
+ hex.present?
27
+ end
28
+
29
+ private
30
+
31
+ def normalize_hex
32
+ return if hex.blank?
33
+ self.hex = hex.starts_with?('#') ? hex : "##{hex}"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,8 @@
1
+ module Workarea
2
+ decorate Search::Settings, with: 'swatches' do
3
+ decorated do
4
+ field :swatch_facets, type: Array, default: []
5
+ list_field :swatch_facets
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ module Workarea
2
+ decorate Search::CategoryBrowse, with: 'swatches' do
3
+ decorated do
4
+ include Search::SwatchedFacets
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Workarea
2
+ decorate Search::ProductSearch, with: 'swatches' do
3
+ decorated do
4
+ include Search::SwatchedFacets
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,19 @@
1
+ module Workarea
2
+ module Search
3
+ module SwatchedFacets
4
+ def facets
5
+ @swatched_facets ||= begin
6
+ swatch_facet_names = swatched_facets.map(&:name)
7
+ without_swatches = super.reject { |f| f.name.in?(swatch_facet_names) }
8
+ swatched_facets + without_swatches
9
+ end
10
+ end
11
+
12
+ def swatched_facets
13
+ Search::Settings.current.swatch_facets.map do |swatch_facet|
14
+ SwatchesFacet.new(self, swatch_facet)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ module Workarea
2
+ module Search
3
+ class SwatchesFacet < TermsFacet
4
+ # This class exists so we have a specific facet type to allow rendering
5
+ # a different partial in the storefront to give us the opportunity to show
6
+ # the swatches
7
+
8
+ def type
9
+ :swatches
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,37 @@
1
+ module Workarea
2
+ class SwatchesSeeds
3
+ COLORS = {
4
+ red: '#f44336',
5
+ pink: '#e91e63',
6
+ purple: '#9c27b0',
7
+ deep_purple: '#673ab7',
8
+ indigo: '#3f51b5',
9
+ blue: '#2196f3',
10
+ light_blue: '#03a9f4',
11
+ cyan: '#00bcd4',
12
+ teal: '#009688',
13
+ green: '#4caf50',
14
+ light_green: '#8bc34a',
15
+ lime: '#cddc39',
16
+ yellow: '#ffeb3b',
17
+ amber: '#ffc107',
18
+ orange: '#ff9800',
19
+ deep_orange: '#ff5722',
20
+ brown: '#795548',
21
+ grey: '#9e9e9e',
22
+ blue_grey: '#607d8b',
23
+ white: '#ffffff',
24
+ black: '#000000'
25
+ }
26
+
27
+ def perform
28
+ puts 'Adding swatches...'
29
+
30
+ COLORS.each do |name, hex|
31
+ Catalog::Swatch.create!(name: name.to_s.titleize, hex: hex)
32
+ end
33
+
34
+ Search::Settings.current.update_attributes!(swatch_facets: ['Color'])
35
+ end
36
+ end
37
+ end