workarea 3.4.12

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 (571) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +326 -0
  3. data/CHANGELOG.md +20501 -0
  4. data/README.md +163 -0
  5. data/docker-compose.yml +27 -0
  6. data/docs/Gemfile +8 -0
  7. data/docs/bin/middleman +29 -0
  8. data/docs/config.rb +87 -0
  9. data/docs/config.ru +7 -0
  10. data/docs/data/articles.yml +157 -0
  11. data/docs/package.json +15 -0
  12. data/docs/source/404.html.erb +13 -0
  13. data/docs/source/articles/access-routes-in-javascript.html.md +33 -0
  14. data/docs/source/articles/add-a-content-area.html.md +169 -0
  15. data/docs/source/articles/add-a-content-block-type.html.md +334 -0
  16. data/docs/source/articles/add-a-report.html.md +202 -0
  17. data/docs/source/articles/add-css-through-the-admin-ui.html.md +30 -0
  18. data/docs/source/articles/add-javascript-through-a-manifest.html.md +367 -0
  19. data/docs/source/articles/add-javascript-through-a-view.html.md +80 -0
  20. data/docs/source/articles/add-javascript-through-the-admin-ui.html.md +30 -0
  21. data/docs/source/articles/add-metrics.html.md +58 -0
  22. data/docs/source/articles/add-or-replace-a-pricing-calculator.html.md +150 -0
  23. data/docs/source/articles/add-remove-or-change-a-mongoid-validation.html.md +147 -0
  24. data/docs/source/articles/add-remove-or-change-a-product-template.html.md +142 -0
  25. data/docs/source/articles/add-remove-sort-and-group-storefront-search-filters.html.md +483 -0
  26. data/docs/source/articles/add-stylesheets-through-a-manifest.html.md +276 -0
  27. data/docs/source/articles/add-system-content.html.md +138 -0
  28. data/docs/source/articles/analytics-overview.html.md +51 -0
  29. data/docs/source/articles/analyze-storefront-search-results.html.md +261 -0
  30. data/docs/source/articles/api-overview.html.md +35 -0
  31. data/docs/source/articles/appending.html.md +506 -0
  32. data/docs/source/articles/application-document.html.md +88 -0
  33. data/docs/source/articles/automated-javascript-testing.html.md +162 -0
  34. data/docs/source/articles/b2b-overview.html.md +64 -0
  35. data/docs/source/articles/browser-and-device-support.html.md +47 -0
  36. data/docs/source/articles/change-product-placeholder-image.html.md +39 -0
  37. data/docs/source/articles/change-storefront-search-results.html.md +283 -0
  38. data/docs/source/articles/change-the-storefront-product-pricing-ui.html.md +348 -0
  39. data/docs/source/articles/change-the-storefront-search-filters-ui.html.md +103 -0
  40. data/docs/source/articles/checkout.html.md +479 -0
  41. data/docs/source/articles/commerce-model.html.md +164 -0
  42. data/docs/source/articles/configuration-for-hosting.html.md +106 -0
  43. data/docs/source/articles/configuration.html.md +406 -0
  44. data/docs/source/articles/configure-a-payment-gateway.html.md +58 -0
  45. data/docs/source/articles/configure-asset-storage.html.md +29 -0
  46. data/docs/source/articles/configure-asset-types.html.md +18 -0
  47. data/docs/source/articles/configure-contact-form-subjects-list.html.md +24 -0
  48. data/docs/source/articles/configure-imageoptim.html.md +23 -0
  49. data/docs/source/articles/configure-locales.html.md +45 -0
  50. data/docs/source/articles/configure-logins-and-authentication.html.md +42 -0
  51. data/docs/source/articles/configure-low-inventory-threshold.html.md +26 -0
  52. data/docs/source/articles/configure-product-image-sizes-and-processing.html.md +28 -0
  53. data/docs/source/articles/content.html.md +554 -0
  54. data/docs/source/articles/contentable.html.md +41 -0
  55. data/docs/source/articles/contribute-code.html.md +69 -0
  56. data/docs/source/articles/contribute-documentation.html.md +60 -0
  57. data/docs/source/articles/create-a-custom-discount.html.md +234 -0
  58. data/docs/source/articles/create-a-new-app.html.md +131 -0
  59. data/docs/source/articles/create-a-plugin.html.md +19 -0
  60. data/docs/source/articles/create-a-style-guide.html.md +71 -0
  61. data/docs/source/articles/create-a-theme.html.md +134 -0
  62. data/docs/source/articles/css-architectural-overview.html.md +89 -0
  63. data/docs/source/articles/customize-a-helper.html.md +91 -0
  64. data/docs/source/articles/decoration.html.md +415 -0
  65. data/docs/source/articles/define-and-configure-inventory-policies.html.md +107 -0
  66. data/docs/source/articles/documentation-style-guide.html.md +48 -0
  67. data/docs/source/articles/documentation.html.md +54 -0
  68. data/docs/source/articles/domain-modeling.html.md +82 -0
  69. data/docs/source/articles/error-pages.html.md.erb +95 -0
  70. data/docs/source/articles/extension-overview.html.md +152 -0
  71. data/docs/source/articles/favicon-support.html.md +112 -0
  72. data/docs/source/articles/feature-spec-helper-stylesheet.html.md +25 -0
  73. data/docs/source/articles/featurejs-and-feature-spec-helper.html.md +20 -0
  74. data/docs/source/articles/help-and-support.html.md +34 -0
  75. data/docs/source/articles/html-fragment-caching.html.md +46 -0
  76. data/docs/source/articles/http-caching.html.md +43 -0
  77. data/docs/source/articles/i18n.html.md +35 -0
  78. data/docs/source/articles/images-flow.html.md +10 -0
  79. data/docs/source/articles/index-storefront-search-documents.html.md +104 -0
  80. data/docs/source/articles/infrastructure.html.md +46 -0
  81. data/docs/source/articles/installing.html.md +61 -0
  82. data/docs/source/articles/integrate-a-payment-gateway.html.md +124 -0
  83. data/docs/source/articles/integrate-a-web-analytics-provider.html.md +35 -0
  84. data/docs/source/articles/integrate-an-inventory-management-system.html.md +88 -0
  85. data/docs/source/articles/integrating-with-other-software.html.md +59 -0
  86. data/docs/source/articles/inventory.html.md +352 -0
  87. data/docs/source/articles/javascript-coding-standards.html.md +30 -0
  88. data/docs/source/articles/javascript-modules.html.md +174 -0
  89. data/docs/source/articles/javascript-overview.html.md +62 -0
  90. data/docs/source/articles/javascript-reference-documentation.html.md +51 -0
  91. data/docs/source/articles/javascript-templates.html.md +52 -0
  92. data/docs/source/articles/low-level-caching.html.md +25 -0
  93. data/docs/source/articles/maintain-a-plugin.html.md +12 -0
  94. data/docs/source/articles/maintenance-policy.html.md +79 -0
  95. data/docs/source/articles/navigable.html.md +51 -0
  96. data/docs/source/articles/navigating-the-code.html.md +149 -0
  97. data/docs/source/articles/navigation.html.md +386 -0
  98. data/docs/source/articles/order-life-cycle.html.md +546 -0
  99. data/docs/source/articles/order-pricing.html.md +389 -0
  100. data/docs/source/articles/orders-and-items.html.md +210 -0
  101. data/docs/source/articles/orders.html.md +66 -0
  102. data/docs/source/articles/overriding.html.md +155 -0
  103. data/docs/source/articles/overview.html.md +43 -0
  104. data/docs/source/articles/plugins-overview.html.md +12 -0
  105. data/docs/source/articles/prerequisites-and-dependencies.html.md +202 -0
  106. data/docs/source/articles/products.html.md.erb +1270 -0
  107. data/docs/source/articles/progressive-web-application-support.html.md +148 -0
  108. data/docs/source/articles/rails-asset-manifests.html.md +33 -0
  109. data/docs/source/articles/rails-asset-view-helpers.html.md +25 -0
  110. data/docs/source/articles/reading-data.html.md +10 -0
  111. data/docs/source/articles/releasable.html.md +37 -0
  112. data/docs/source/articles/report-a-bug.html.md +75 -0
  113. data/docs/source/articles/ruby-coding-standards.html.md +10 -0
  114. data/docs/source/articles/run-sidekiq-in-a-local-environment.html.md +40 -0
  115. data/docs/source/articles/searching.html.md +1005 -0
  116. data/docs/source/articles/security-policy.html.md +42 -0
  117. data/docs/source/articles/seeds.html.md +345 -0
  118. data/docs/source/articles/shipping.html.md +756 -0
  119. data/docs/source/articles/sort-and-exclude-product-options.html.md +47 -0
  120. data/docs/source/articles/storefront-search-features.html.md +568 -0
  121. data/docs/source/articles/storefront-searches.html.md +126 -0
  122. data/docs/source/articles/style-guides.html.md +21 -0
  123. data/docs/source/articles/stylesheet-coding-standards.html.md +24 -0
  124. data/docs/source/articles/stylesheets-overview.html.md +67 -0
  125. data/docs/source/articles/swappable-list-data-structure.html.md +81 -0
  126. data/docs/source/articles/system-emails.html.md +102 -0
  127. data/docs/source/articles/taggable.html.md +8 -0
  128. data/docs/source/articles/test-a-credit-card-transaction.html.md +16 -0
  129. data/docs/source/articles/test-if-a-plugin-is-installed.html.md +34 -0
  130. data/docs/source/articles/testing.html.md +914 -0
  131. data/docs/source/articles/themes-overview.html.md +155 -0
  132. data/docs/source/articles/translate-administrable-content.html.md +14 -0
  133. data/docs/source/articles/translate-javascript-content.html.md +16 -0
  134. data/docs/source/articles/translate-or-customize-message-content.html.md +29 -0
  135. data/docs/source/articles/translate-or-customize-static-content.html.md +30 -0
  136. data/docs/source/articles/use-an-existing-workarea-app.html.md +108 -0
  137. data/docs/source/articles/view-models.html.md +509 -0
  138. data/docs/source/articles/views.html.md +14 -0
  139. data/docs/source/articles/workers.html.md +613 -0
  140. data/docs/source/articles/writing-data.html.md +10 -0
  141. data/docs/source/cli.html.md +163 -0
  142. data/docs/source/favicon.ico +0 -0
  143. data/docs/source/images/3-variants-1-option.png +0 -0
  144. data/docs/source/images/3-variants-3-options.png +0 -0
  145. data/docs/source/images/3-years-primary-image.png +0 -0
  146. data/docs/source/images/404-storefront-error-page.png +0 -0
  147. data/docs/source/images/404-system-content-admin.png +0 -0
  148. data/docs/source/images/404.jpg +0 -0
  149. data/docs/source/images/5-years-primary-image.png +0 -0
  150. data/docs/source/images/activity-dashboard.png +0 -0
  151. data/docs/source/images/activity-for-object.png +0 -0
  152. data/docs/source/images/activity-ui.png +0 -0
  153. data/docs/source/images/adding-captioned-image-block-custom-icon.png +0 -0
  154. data/docs/source/images/adding-captioned-image-block-default-icon.png +0 -0
  155. data/docs/source/images/admin-alerts-ui.png +0 -0
  156. data/docs/source/images/admin-category-range-filters.png +0 -0
  157. data/docs/source/images/admin-for-3-column-hero.png +0 -0
  158. data/docs/source/images/admin-help-index.png +0 -0
  159. data/docs/source/images/admin-help-ui.png +0 -0
  160. data/docs/source/images/admin-javascript.png +0 -0
  161. data/docs/source/images/admin-notification-for-deactivated-discount.png +0 -0
  162. data/docs/source/images/admin-notifications-ui.png +0 -0
  163. data/docs/source/images/admin-product-show-page.png +0 -0
  164. data/docs/source/images/admin-products-index-page.png +0 -0
  165. data/docs/source/images/admin-range-filters.png +0 -0
  166. data/docs/source/images/admin-style-guides-navigation.png +0 -0
  167. data/docs/source/images/after-re-seeding.png +0 -0
  168. data/docs/source/images/after-seeding-localhost-3000.png +0 -0
  169. data/docs/source/images/after-seeding.png +0 -0
  170. data/docs/source/images/arrow.svg +1 -0
  171. data/docs/source/images/arrow_white.svg +1 -0
  172. data/docs/source/images/aws-resource-map.png +0 -0
  173. data/docs/source/images/backordered-until-output-on-inventory-sku-card.png +0 -0
  174. data/docs/source/images/before-seeding-localhost-3000.png +0 -0
  175. data/docs/source/images/before-seeding.png +0 -0
  176. data/docs/source/images/browsing-workarea-versions-on-the-web.png +0 -0
  177. data/docs/source/images/bulk-asset-upload-on-assets-index-page.png +0 -0
  178. data/docs/source/images/bulk-asset-upload-while-editing-content.png +0 -0
  179. data/docs/source/images/bundle-show-workarea-core.png +0 -0
  180. data/docs/source/images/bundle-show-workarea.png +0 -0
  181. data/docs/source/images/calendar-for-backordered-until-field.png +0 -0
  182. data/docs/source/images/captioned-image-block-in-storefront.png +0 -0
  183. data/docs/source/images/captioned-image-content-block-storefront-component-style-guide.png +0 -0
  184. data/docs/source/images/cart-system-content-in-admin.png +0 -0
  185. data/docs/source/images/cart-system-content-in-storefront.png +0 -0
  186. data/docs/source/images/checkout-addresses-guest.png +0 -0
  187. data/docs/source/images/checkout-addresses-user.png +0 -0
  188. data/docs/source/images/checkout-confirmation.png +0 -0
  189. data/docs/source/images/checkout-flow-0.png +0 -0
  190. data/docs/source/images/checkout-flow-1.png +0 -0
  191. data/docs/source/images/checkout-flow-2.png +0 -0
  192. data/docs/source/images/checkout-flow-3.png +0 -0
  193. data/docs/source/images/checkout-flow-4.png +0 -0
  194. data/docs/source/images/checkout-payment-guest.png +0 -0
  195. data/docs/source/images/checkout-payment-user.png +0 -0
  196. data/docs/source/images/checkout-shipping.png +0 -0
  197. data/docs/source/images/color-picker-component-admin-style-guide.png +0 -0
  198. data/docs/source/images/color-picker-component-on-content-editing-screen.png +0 -0
  199. data/docs/source/images/commerce-model-carts-orders.png +0 -0
  200. data/docs/source/images/commerce-model-order-pricing.png +0 -0
  201. data/docs/source/images/commerce-model.png +0 -0
  202. data/docs/source/images/configuring-an-index-pattern-in-kibana.png +0 -0
  203. data/docs/source/images/content-block-presets.png +0 -0
  204. data/docs/source/images/content-search-customization.png +0 -0
  205. data/docs/source/images/country-with-region-data-in-address-form.png +0 -0
  206. data/docs/source/images/country-without-region-data-in-address-form.png +0 -0
  207. data/docs/source/images/create-content-block-preset-ui.png +0 -0
  208. data/docs/source/images/credit-card-icons.png +0 -0
  209. data/docs/source/images/css-added-through-admin.png +0 -0
  210. data/docs/source/images/css-admin-ui.png +0 -0
  211. data/docs/source/images/current-configuration-shown-in-admin-settings.png +0 -0
  212. data/docs/source/images/customer-impersonation-in-admin.png +0 -0
  213. data/docs/source/images/customer-impersonation-in-store-front.png +0 -0
  214. data/docs/source/images/date-filter-same-day.png +0 -0
  215. data/docs/source/images/developer-toolbar-in-store-front.png +0 -0
  216. data/docs/source/images/discounts-sorted-by-most-redeemed.png +0 -0
  217. data/docs/source/images/edit-help-article.png +0 -0
  218. data/docs/source/images/editing-content-for-search-customization.png +0 -0
  219. data/docs/source/images/editing-dynamic-captioned-image-block.png +0 -0
  220. data/docs/source/images/editing-product-fields-in-the-admin.png +0 -0
  221. data/docs/source/images/editing-search-system-content.png +0 -0
  222. data/docs/source/images/editing-static-captioned-image-block-custom-icon.png +0 -0
  223. data/docs/source/images/editing-static-captioned-image-block-default-icon.png +0 -0
  224. data/docs/source/images/external.svg +1 -0
  225. data/docs/source/images/favicon_16.png +0 -0
  226. data/docs/source/images/favicon_180.png +0 -0
  227. data/docs/source/images/favicon_32.png +0 -0
  228. data/docs/source/images/filters-all.png +0 -0
  229. data/docs/source/images/filters-control.png +0 -0
  230. data/docs/source/images/filters-custom.png +0 -0
  231. data/docs/source/images/filters-groups.png +0 -0
  232. data/docs/source/images/filters-material.png +0 -0
  233. data/docs/source/images/filters-omitted.png +0 -0
  234. data/docs/source/images/filters-pinned.png +0 -0
  235. data/docs/source/images/filters-range.png +0 -0
  236. data/docs/source/images/filters-sorted.png +0 -0
  237. data/docs/source/images/filters-wrapping-to-second-line-in-admin.png +0 -0
  238. data/docs/source/images/generic-product-template-images-no-options-selected.png +0 -0
  239. data/docs/source/images/generic-product-template-images-options-selected.png +0 -0
  240. data/docs/source/images/generic-template.png +0 -0
  241. data/docs/source/images/hosting.svg +1 -0
  242. data/docs/source/images/image-group-content-block-in-storefront.png +0 -0
  243. data/docs/source/images/images.svg +1 -0
  244. data/docs/source/images/import-export-screenshot.png +0 -0
  245. data/docs/source/images/invalid-display.png +0 -0
  246. data/docs/source/images/itcss.png +0 -0
  247. data/docs/source/images/kibana-dev-tools-console.png +0 -0
  248. data/docs/source/images/layout-content-admin-with-2-areas.png +0 -0
  249. data/docs/source/images/layout-content-admin-with-3-areas.png +0 -0
  250. data/docs/source/images/link-to-search-system-content.png +0 -0
  251. data/docs/source/images/logo.svg +1 -0
  252. data/docs/source/images/menu.svg +2 -0
  253. data/docs/source/images/mongo-replica-set.svg +1 -0
  254. data/docs/source/images/multi-column-hero-blocks.png +0 -0
  255. data/docs/source/images/option-selects-product-template-images-options-selected.png +0 -0
  256. data/docs/source/images/option-selects-template.png +0 -0
  257. data/docs/source/images/option-thumbnails-template.png +0 -0
  258. data/docs/source/images/order-item-total-price-diagram.png +0 -0
  259. data/docs/source/images/order-pricing-cart-example.png +0 -0
  260. data/docs/source/images/order-pricing-example-adjustments.png +0 -0
  261. data/docs/source/images/order-pricing-example-totals.png +0 -0
  262. data/docs/source/images/order-pricing-placed-order-example.png +0 -0
  263. data/docs/source/images/order-shipping-total-diagram.png +0 -0
  264. data/docs/source/images/order-show-with-multiple-tenders.png +0 -0
  265. data/docs/source/images/order-subtotal-price-diagram.png +0 -0
  266. data/docs/source/images/order-tax-total-diagram.png +0 -0
  267. data/docs/source/images/order-total-price-diagram.png +0 -0
  268. data/docs/source/images/order-total-value-diagram.png +0 -0
  269. data/docs/source/images/orders-dashboard-links.png +0 -0
  270. data/docs/source/images/oval.svg +1 -0
  271. data/docs/source/images/payment-icon-storefront-style-guide.png +0 -0
  272. data/docs/source/images/people-dashboard-links.png +0 -0
  273. data/docs/source/images/price-adjustments-diagram.png +0 -0
  274. data/docs/source/images/price-display-no-options.png +0 -0
  275. data/docs/source/images/price-display-options-selected.png +0 -0
  276. data/docs/source/images/pricing-calculators-diagram.png +0 -0
  277. data/docs/source/images/product-list-content-block-admin.png +0 -0
  278. data/docs/source/images/product-list-content-block-in-store-front.png +0 -0
  279. data/docs/source/images/promo-products-excluded-autocomplete-results-after.png +0 -0
  280. data/docs/source/images/promo-products-excluded-featured-category-results-after.png +0 -0
  281. data/docs/source/images/promo-products-excluded-recommendations-results-after.png +0 -0
  282. data/docs/source/images/promo-products-excluded-search-category-results-after.png +0 -0
  283. data/docs/source/images/promo-products-excluded-search-results-after.png +0 -0
  284. data/docs/source/images/promo-products-included-autocomplete-results-before.png +0 -0
  285. data/docs/source/images/promo-products-included-featured-category-results-before.png +0 -0
  286. data/docs/source/images/promo-products-included-recommendations-results-before.png +0 -0
  287. data/docs/source/images/promo-products-included-search-category-results-before.png +0 -0
  288. data/docs/source/images/promo-products-included-search-results-before.png +0 -0
  289. data/docs/source/images/rails-version-constraint.png +0 -0
  290. data/docs/source/images/re-enable-discount.png +0 -0
  291. data/docs/source/images/reading-data.svg +1 -0
  292. data/docs/source/images/readme-hero.png +0 -0
  293. data/docs/source/images/redesigned-customized-sort-for-search-results.png +0 -0
  294. data/docs/source/images/reviews-summary-above-share-buttons.png +0 -0
  295. data/docs/source/images/reviews-summary-below-product-name.png +0 -0
  296. data/docs/source/images/reviews-summary-below-share-buttons.png +0 -0
  297. data/docs/source/images/reviews-summary-removed.png +0 -0
  298. data/docs/source/images/rsa-fingerprint-for-stash.png +0 -0
  299. data/docs/source/images/ruby-version-constraint.png +0 -0
  300. data/docs/source/images/script-tag-added-through-admin.png +0 -0
  301. data/docs/source/images/search-analysis-admin-alternate-rendering.png +0 -0
  302. data/docs/source/images/search-analysis-admin.png +0 -0
  303. data/docs/source/images/search-quality-report.png +0 -0
  304. data/docs/source/images/search.svg +1 -0
  305. data/docs/source/images/searching-for-cart-system-content-in-admin.png +0 -0
  306. data/docs/source/images/searching-for-layout-system-content-in-admin.png +0 -0
  307. data/docs/source/images/seeded-admin.png +0 -0
  308. data/docs/source/images/seeds-from-plugins.png +0 -0
  309. data/docs/source/images/seo-metadata-automation-ui.png +0 -0
  310. data/docs/source/images/show-password-button.png +0 -0
  311. data/docs/source/images/storefront-autocomplete.png +0 -0
  312. data/docs/source/images/storefront-category-summary-content-block.png +0 -0
  313. data/docs/source/images/storefront-category.png +0 -0
  314. data/docs/source/images/storefront-product-after-overriding.png +0 -0
  315. data/docs/source/images/storefront-product-before-overriding.png +0 -0
  316. data/docs/source/images/storefront-product-browse-page.png +0 -0
  317. data/docs/source/images/storefront-product-recommendations.png +0 -0
  318. data/docs/source/images/storefront-product-show-page.png +0 -0
  319. data/docs/source/images/storefront-requests-and-search-requests.png +0 -0
  320. data/docs/source/images/storefront-search-request-handling.png +0 -0
  321. data/docs/source/images/storefront-search-response-creation.png +0 -0
  322. data/docs/source/images/storefront-search.png +0 -0
  323. data/docs/source/images/storefront-style-guides-navigation.png +0 -0
  324. data/docs/source/images/styles.css +3 -0
  325. data/docs/source/images/tax-categories-ui.png +0 -0
  326. data/docs/source/images/tax-rates-ui.png +0 -0
  327. data/docs/source/images/unpurchasable-product.png +0 -0
  328. data/docs/source/images/url-redirects-filtering.png +0 -0
  329. data/docs/source/images/utility-nav-area-in-admin.png +0 -0
  330. data/docs/source/images/utility-nav-area-in-storefront.png +0 -0
  331. data/docs/source/images/validation-message-in-storefront.png +0 -0
  332. data/docs/source/images/view-model-interface.png +0 -0
  333. data/docs/source/images/viewing-workarea-version-in-source.png +0 -0
  334. data/docs/source/images/workarea.svg +1 -0
  335. data/docs/source/images/worst-performing-searches-on-results-customization-page.png +0 -0
  336. data/docs/source/images/writing-data.svg +1 -0
  337. data/docs/source/index.html.erb +167 -0
  338. data/docs/source/javascripts/jquery.js +2 -0
  339. data/docs/source/javascripts/lunr.js +7 -0
  340. data/docs/source/javascripts/site.js +299 -0
  341. data/docs/source/javascripts/vendor/highlight.pack.js +2 -0
  342. data/docs/source/layouts/article.erb +106 -0
  343. data/docs/source/layouts/bare.erb +46 -0
  344. data/docs/source/layouts/layout.erb +43 -0
  345. data/docs/source/release-notes.html.md +258 -0
  346. data/docs/source/release-notes/workarea-3-0-0.html.md +146 -0
  347. data/docs/source/release-notes/workarea-3-0-1.html.md +161 -0
  348. data/docs/source/release-notes/workarea-3-0-10.html.md +39 -0
  349. data/docs/source/release-notes/workarea-3-0-11.html.md +277 -0
  350. data/docs/source/release-notes/workarea-3-0-12.html.md +14 -0
  351. data/docs/source/release-notes/workarea-3-0-13.html.md +153 -0
  352. data/docs/source/release-notes/workarea-3-0-14.html.md +93 -0
  353. data/docs/source/release-notes/workarea-3-0-15.html.md +107 -0
  354. data/docs/source/release-notes/workarea-3-0-16.html.md +36 -0
  355. data/docs/source/release-notes/workarea-3-0-17.html.md +141 -0
  356. data/docs/source/release-notes/workarea-3-0-18.html.md +123 -0
  357. data/docs/source/release-notes/workarea-3-0-19.html.md +160 -0
  358. data/docs/source/release-notes/workarea-3-0-2.html.md +222 -0
  359. data/docs/source/release-notes/workarea-3-0-20.html.md +95 -0
  360. data/docs/source/release-notes/workarea-3-0-21.html.md +168 -0
  361. data/docs/source/release-notes/workarea-3-0-22.html.md +268 -0
  362. data/docs/source/release-notes/workarea-3-0-23.html.md +173 -0
  363. data/docs/source/release-notes/workarea-3-0-24.html.md +19 -0
  364. data/docs/source/release-notes/workarea-3-0-25.html.md +26 -0
  365. data/docs/source/release-notes/workarea-3-0-26.html.md +199 -0
  366. data/docs/source/release-notes/workarea-3-0-27.html.md +113 -0
  367. data/docs/source/release-notes/workarea-3-0-28.html.md +39 -0
  368. data/docs/source/release-notes/workarea-3-0-29.html.md +73 -0
  369. data/docs/source/release-notes/workarea-3-0-3.html.md +35 -0
  370. data/docs/source/release-notes/workarea-3-0-30.html.md +186 -0
  371. data/docs/source/release-notes/workarea-3-0-31.html.md +125 -0
  372. data/docs/source/release-notes/workarea-3-0-32.html.md +73 -0
  373. data/docs/source/release-notes/workarea-3-0-33.html.md +137 -0
  374. data/docs/source/release-notes/workarea-3-0-34.html.md +203 -0
  375. data/docs/source/release-notes/workarea-3-0-35.html.md +205 -0
  376. data/docs/source/release-notes/workarea-3-0-36.html.md +105 -0
  377. data/docs/source/release-notes/workarea-3-0-37.html.md +144 -0
  378. data/docs/source/release-notes/workarea-3-0-38.html.md +73 -0
  379. data/docs/source/release-notes/workarea-3-0-39.html.md +77 -0
  380. data/docs/source/release-notes/workarea-3-0-4.html.md +14 -0
  381. data/docs/source/release-notes/workarea-3-0-40.html.md +130 -0
  382. data/docs/source/release-notes/workarea-3-0-41.html.md +70 -0
  383. data/docs/source/release-notes/workarea-3-0-42.html.md +52 -0
  384. data/docs/source/release-notes/workarea-3-0-43.html.md +72 -0
  385. data/docs/source/release-notes/workarea-3-0-44.html.md +93 -0
  386. data/docs/source/release-notes/workarea-3-0-45.html.md +61 -0
  387. data/docs/source/release-notes/workarea-3-0-46.html.md +171 -0
  388. data/docs/source/release-notes/workarea-3-0-47.html.md +130 -0
  389. data/docs/source/release-notes/workarea-3-0-48.html.md +160 -0
  390. data/docs/source/release-notes/workarea-3-0-49.html.md +28 -0
  391. data/docs/source/release-notes/workarea-3-0-5.html.md +225 -0
  392. data/docs/source/release-notes/workarea-3-0-50.html.md +74 -0
  393. data/docs/source/release-notes/workarea-3-0-51.html.md +61 -0
  394. data/docs/source/release-notes/workarea-3-0-52.html.md +76 -0
  395. data/docs/source/release-notes/workarea-3-0-53.html.md +126 -0
  396. data/docs/source/release-notes/workarea-3-0-54.html.md +112 -0
  397. data/docs/source/release-notes/workarea-3-0-55.html.md +105 -0
  398. data/docs/source/release-notes/workarea-3-0-56.html.md +56 -0
  399. data/docs/source/release-notes/workarea-3-0-57.html.md +82 -0
  400. data/docs/source/release-notes/workarea-3-0-58.html.md +153 -0
  401. data/docs/source/release-notes/workarea-3-0-59.html.md +78 -0
  402. data/docs/source/release-notes/workarea-3-0-6.html.md +165 -0
  403. data/docs/source/release-notes/workarea-3-0-60.html.md +43 -0
  404. data/docs/source/release-notes/workarea-3-0-61.html.md +46 -0
  405. data/docs/source/release-notes/workarea-3-0-62.html.md +23 -0
  406. data/docs/source/release-notes/workarea-3-0-63.html.md +25 -0
  407. data/docs/source/release-notes/workarea-3-0-64.html.md +25 -0
  408. data/docs/source/release-notes/workarea-3-0-65.html.md +37 -0
  409. data/docs/source/release-notes/workarea-3-0-7.html.md +207 -0
  410. data/docs/source/release-notes/workarea-3-0-8.html.md +337 -0
  411. data/docs/source/release-notes/workarea-3-0-9.html.md +196 -0
  412. data/docs/source/release-notes/workarea-3-1-0.html.md +414 -0
  413. data/docs/source/release-notes/workarea-3-1-1.html.md +139 -0
  414. data/docs/source/release-notes/workarea-3-1-10.html.md +19 -0
  415. data/docs/source/release-notes/workarea-3-1-11.html.md +27 -0
  416. data/docs/source/release-notes/workarea-3-1-12.html.md +216 -0
  417. data/docs/source/release-notes/workarea-3-1-13.html.md +113 -0
  418. data/docs/source/release-notes/workarea-3-1-14.html.md +39 -0
  419. data/docs/source/release-notes/workarea-3-1-15.html.md +107 -0
  420. data/docs/source/release-notes/workarea-3-1-16.html.md +188 -0
  421. data/docs/source/release-notes/workarea-3-1-17.html.md +141 -0
  422. data/docs/source/release-notes/workarea-3-1-18.html.md +73 -0
  423. data/docs/source/release-notes/workarea-3-1-19.html.md +137 -0
  424. data/docs/source/release-notes/workarea-3-1-2.html.md +55 -0
  425. data/docs/source/release-notes/workarea-3-1-20.html.md +203 -0
  426. data/docs/source/release-notes/workarea-3-1-21.html.md +205 -0
  427. data/docs/source/release-notes/workarea-3-1-22.html.md +121 -0
  428. data/docs/source/release-notes/workarea-3-1-23.html.md +144 -0
  429. data/docs/source/release-notes/workarea-3-1-24.html.md +94 -0
  430. data/docs/source/release-notes/workarea-3-1-25.html.md +77 -0
  431. data/docs/source/release-notes/workarea-3-1-26.html.md +130 -0
  432. data/docs/source/release-notes/workarea-3-1-27.html.md +70 -0
  433. data/docs/source/release-notes/workarea-3-1-28.html.md +52 -0
  434. data/docs/source/release-notes/workarea-3-1-29.html.md +44 -0
  435. data/docs/source/release-notes/workarea-3-1-3.html.md +185 -0
  436. data/docs/source/release-notes/workarea-3-1-30.html.md +72 -0
  437. data/docs/source/release-notes/workarea-3-1-31.html.md +93 -0
  438. data/docs/source/release-notes/workarea-3-1-32.html.md +61 -0
  439. data/docs/source/release-notes/workarea-3-1-33.html.md +171 -0
  440. data/docs/source/release-notes/workarea-3-1-34.html.md +130 -0
  441. data/docs/source/release-notes/workarea-3-1-35.html.md +179 -0
  442. data/docs/source/release-notes/workarea-3-1-36.html.md +28 -0
  443. data/docs/source/release-notes/workarea-3-1-37.html.md +74 -0
  444. data/docs/source/release-notes/workarea-3-1-38.html.md +61 -0
  445. data/docs/source/release-notes/workarea-3-1-39.html.md +96 -0
  446. data/docs/source/release-notes/workarea-3-1-4.html.md +148 -0
  447. data/docs/source/release-notes/workarea-3-1-40.html.md +126 -0
  448. data/docs/source/release-notes/workarea-3-1-41.html.md +128 -0
  449. data/docs/source/release-notes/workarea-3-1-42.html.md +105 -0
  450. data/docs/source/release-notes/workarea-3-1-43.html.md +37 -0
  451. data/docs/source/release-notes/workarea-3-1-44.html.md +82 -0
  452. data/docs/source/release-notes/workarea-3-1-45.html.md +153 -0
  453. data/docs/source/release-notes/workarea-3-1-46.html.md +91 -0
  454. data/docs/source/release-notes/workarea-3-1-47.html.md +65 -0
  455. data/docs/source/release-notes/workarea-3-1-48.html.md +46 -0
  456. data/docs/source/release-notes/workarea-3-1-49.html.md +23 -0
  457. data/docs/source/release-notes/workarea-3-1-5.html.md +169 -0
  458. data/docs/source/release-notes/workarea-3-1-50.html.md +42 -0
  459. data/docs/source/release-notes/workarea-3-1-51.html.md +25 -0
  460. data/docs/source/release-notes/workarea-3-1-52.html.md +57 -0
  461. data/docs/source/release-notes/workarea-3-1-6.html.md +117 -0
  462. data/docs/source/release-notes/workarea-3-1-7.html.md +176 -0
  463. data/docs/source/release-notes/workarea-3-1-8.html.md +283 -0
  464. data/docs/source/release-notes/workarea-3-1-9.html.md +212 -0
  465. data/docs/source/release-notes/workarea-3-2-0.html.md +1705 -0
  466. data/docs/source/release-notes/workarea-3-2-1.html.md +216 -0
  467. data/docs/source/release-notes/workarea-3-2-10.html.md +237 -0
  468. data/docs/source/release-notes/workarea-3-2-11.html.md +121 -0
  469. data/docs/source/release-notes/workarea-3-2-12.html.md +145 -0
  470. data/docs/source/release-notes/workarea-3-2-13.html.md +138 -0
  471. data/docs/source/release-notes/workarea-3-2-14.html.md +77 -0
  472. data/docs/source/release-notes/workarea-3-2-15.html.md +130 -0
  473. data/docs/source/release-notes/workarea-3-2-16.html.md +111 -0
  474. data/docs/source/release-notes/workarea-3-2-17.html.md +52 -0
  475. data/docs/source/release-notes/workarea-3-2-18.html.md +44 -0
  476. data/docs/source/release-notes/workarea-3-2-19.html.md +72 -0
  477. data/docs/source/release-notes/workarea-3-2-2.html.md +145 -0
  478. data/docs/source/release-notes/workarea-3-2-20.html.md +93 -0
  479. data/docs/source/release-notes/workarea-3-2-21.html.md +61 -0
  480. data/docs/source/release-notes/workarea-3-2-22.html.md +154 -0
  481. data/docs/source/release-notes/workarea-3-2-23.html.md +130 -0
  482. data/docs/source/release-notes/workarea-3-2-24.html.md +200 -0
  483. data/docs/source/release-notes/workarea-3-2-25.html.md +28 -0
  484. data/docs/source/release-notes/workarea-3-2-26.html.md +94 -0
  485. data/docs/source/release-notes/workarea-3-2-27.html.md +61 -0
  486. data/docs/source/release-notes/workarea-3-2-28.html.md +96 -0
  487. data/docs/source/release-notes/workarea-3-2-29.html.md +126 -0
  488. data/docs/source/release-notes/workarea-3-2-30.html.md +112 -0
  489. data/docs/source/release-notes/workarea-3-2-31.html.md +105 -0
  490. data/docs/source/release-notes/workarea-3-2-32.html.md +56 -0
  491. data/docs/source/release-notes/workarea-3-2-33.html.md +82 -0
  492. data/docs/source/release-notes/workarea-3-2-34.html.md +153 -0
  493. data/docs/source/release-notes/workarea-3-2-35.html.md +91 -0
  494. data/docs/source/release-notes/workarea-3-2-36.html.md +118 -0
  495. data/docs/source/release-notes/workarea-3-2-37.html.md +46 -0
  496. data/docs/source/release-notes/workarea-3-2-38.html.md +23 -0
  497. data/docs/source/release-notes/workarea-3-2-39.html.md +42 -0
  498. data/docs/source/release-notes/workarea-3-2-4.html.md +109 -0
  499. data/docs/source/release-notes/workarea-3-2-40.html.md +25 -0
  500. data/docs/source/release-notes/workarea-3-2-41.html.md +90 -0
  501. data/docs/source/release-notes/workarea-3-2-5.html.md +186 -0
  502. data/docs/source/release-notes/workarea-3-2-6.html.md +173 -0
  503. data/docs/source/release-notes/workarea-3-2-7.html.md +89 -0
  504. data/docs/source/release-notes/workarea-3-2-8.html.md +137 -0
  505. data/docs/source/release-notes/workarea-3-2-9.html.md +219 -0
  506. data/docs/source/release-notes/workarea-3-3-0.html.md +1272 -0
  507. data/docs/source/release-notes/workarea-3-3-1.html.md +324 -0
  508. data/docs/source/release-notes/workarea-3-3-10.html.md +69 -0
  509. data/docs/source/release-notes/workarea-3-3-11.html.md +72 -0
  510. data/docs/source/release-notes/workarea-3-3-12.html.md +136 -0
  511. data/docs/source/release-notes/workarea-3-3-13.html.md +61 -0
  512. data/docs/source/release-notes/workarea-3-3-14.html.md +196 -0
  513. data/docs/source/release-notes/workarea-3-3-15.html.md +167 -0
  514. data/docs/source/release-notes/workarea-3-3-16.html.md +234 -0
  515. data/docs/source/release-notes/workarea-3-3-17.html.md +82 -0
  516. data/docs/source/release-notes/workarea-3-3-18.html.md +165 -0
  517. data/docs/source/release-notes/workarea-3-3-19.html.md +106 -0
  518. data/docs/source/release-notes/workarea-3-3-2.html.md +72 -0
  519. data/docs/source/release-notes/workarea-3-3-20.html.md +116 -0
  520. data/docs/source/release-notes/workarea-3-3-21.html.md +228 -0
  521. data/docs/source/release-notes/workarea-3-3-22.html.md +125 -0
  522. data/docs/source/release-notes/workarea-3-3-23.html.md +154 -0
  523. data/docs/source/release-notes/workarea-3-3-24.html.md +70 -0
  524. data/docs/source/release-notes/workarea-3-3-25.html.md +114 -0
  525. data/docs/source/release-notes/workarea-3-3-26.html.md +260 -0
  526. data/docs/source/release-notes/workarea-3-3-27.html.md +138 -0
  527. data/docs/source/release-notes/workarea-3-3-28.html.md +147 -0
  528. data/docs/source/release-notes/workarea-3-3-29.html.md +63 -0
  529. data/docs/source/release-notes/workarea-3-3-3.html.md +153 -0
  530. data/docs/source/release-notes/workarea-3-3-30.html.md +102 -0
  531. data/docs/source/release-notes/workarea-3-3-31.html.md +57 -0
  532. data/docs/source/release-notes/workarea-3-3-32.html.md +44 -0
  533. data/docs/source/release-notes/workarea-3-3-33.html.md +114 -0
  534. data/docs/source/release-notes/workarea-3-3-4.html.md +332 -0
  535. data/docs/source/release-notes/workarea-3-3-5.html.md +242 -0
  536. data/docs/source/release-notes/workarea-3-3-6.html.md +100 -0
  537. data/docs/source/release-notes/workarea-3-3-7.html.md +148 -0
  538. data/docs/source/release-notes/workarea-3-3-8.html.md +163 -0
  539. data/docs/source/release-notes/workarea-3-3-9.html.md +93 -0
  540. data/docs/source/release-notes/workarea-3-4-0.html.md +580 -0
  541. data/docs/source/release-notes/workarea-3-4-1.html.md +150 -0
  542. data/docs/source/release-notes/workarea-3-4-10.html.md +72 -0
  543. data/docs/source/release-notes/workarea-3-4-11.html.md +60 -0
  544. data/docs/source/release-notes/workarea-3-4-12.html.md +155 -0
  545. data/docs/source/release-notes/workarea-3-4-2.html.md +188 -0
  546. data/docs/source/release-notes/workarea-3-4-3.html.md +136 -0
  547. data/docs/source/release-notes/workarea-3-4-4.html.md +114 -0
  548. data/docs/source/release-notes/workarea-3-4-5.html.md +275 -0
  549. data/docs/source/release-notes/workarea-3-4-6.html.md +169 -0
  550. data/docs/source/release-notes/workarea-3-4-7.html.md +162 -0
  551. data/docs/source/release-notes/workarea-3-4-8.html.md +95 -0
  552. data/docs/source/release-notes/workarea-3-4-9.html.md +135 -0
  553. data/docs/source/search.html.erb +34 -0
  554. data/docs/source/shared/_header.erb +61 -0
  555. data/docs/source/shared/_svgs.erb +17 -0
  556. data/docs/source/style_guide/index.html.erb +382 -0
  557. data/docs/source/stylesheets/_base.scss +125 -0
  558. data/docs/source/stylesheets/_components.scss +669 -0
  559. data/docs/source/stylesheets/_helpers.scss +10 -0
  560. data/docs/source/stylesheets/_opinions.scss +42 -0
  561. data/docs/source/stylesheets/_settings.scss +56 -0
  562. data/docs/source/stylesheets/_typography.scss +119 -0
  563. data/docs/source/stylesheets/site.css.scss +14 -0
  564. data/docs/source/stylesheets/vendor/_avalanche.scss +328 -0
  565. data/docs/source/stylesheets/vendor/_normalize.scss +341 -0
  566. data/docs/source/stylesheets/vendor/highlight/_tomorrow_night_blue.scss +75 -0
  567. data/docs/source/upgrade-guides.html.md +18 -0
  568. data/docs/source/upgrade-guides/workarea-3-4-0.html.md +152 -0
  569. data/docs/workarea_renderer.rb +8 -0
  570. data/docs/yarn.lock +2522 -0
  571. metadata +669 -0
@@ -0,0 +1,142 @@
1
+ ---
2
+ title: Add, Remove, or Change a Product Template
3
+ excerpt: Product Templates are useful to change how the product is displayed on its detail page. This guide will show you how to manipulate these templates both within a plugin and in an application.
4
+ ---
5
+
6
+ # Add, Remove, or Change a Product Template
7
+
8
+ Product Templates are useful for changing how products are displayed on their detail pages. They allow the same site to serve several "editions" of a product detail page, for clients with very diverse offerings of products. Workarea provides full control over how the product detail page is displayed, allowing developers to make both logical and cosmetic customizations to different classes of products. Product Templates can originate in the core Workarea platform, a Workarea plugin you install, or customized specifically for your application. This guide will show you how to manipulate those templates both within a plugin and in an application.
9
+
10
+ ## Creating a Product Template
11
+
12
+ To create a new product template, you can run the `workarea:product_template` generator that comes bundled with Workarea. This generator is responsible for doing three things:
13
+
14
+ 1. Add the template's **slug** (a human-readable unique identifier used to select the template) to `Workarea.config.product_templates`
15
+ 2. Create the **partial template** from Workarea's generic template
16
+ 3. Define a **view model** that inherits from `Storefront::ProductViewModel` for encapsulating view-level logic concerns. This step is optional, and the `Storefront::ProductViewModel` will be used if your template's view model is not defined.
17
+
18
+ To learn more about the various options of this generator, run the generator without arguments:
19
+
20
+ ```
21
+ cd path/to/your_app
22
+ bin/rails g workarea:product_template
23
+ ```
24
+
25
+ This should result in the following:
26
+
27
+ ```
28
+ Usage:
29
+ rails generate workarea:product_template NAME [options]
30
+
31
+ Options:
32
+ [--skip-namespace], [--no-skip-namespace] # Skip namespace (affects only isolated applications)
33
+ [--skip-view-model], [--no-skip-view-model] # Indicates when to generate skip-view-model
34
+
35
+ Runtime options:
36
+ -f, [--force] # Overwrite files that already exist
37
+ -p, [--pretend], [--no-pretend] # Run but do not make any changes
38
+ -q, [--quiet], [--no-quiet] # Suppress status output
39
+ -s, [--skip], [--no-skip] # Skip files that already exist
40
+
41
+ Description:
42
+ Boilerplate set up for a custom product template.
43
+
44
+ Example:
45
+ rails generate workarea:product_template TemplateName
46
+
47
+ modifies:
48
+ config/initializers/workarea.rb
49
+ creates:
50
+ app/views/workarea/storefront/products/templates/_template_name.html.haml
51
+ app/view_models/workarea/storefront/template_name_view_model.rb
52
+ ```
53
+
54
+ You also have the option of implementing each step of this yourself. Below is an overview of each step in the creation process.
55
+
56
+ ### Create Partial Template
57
+
58
+ The partial template you create must be placed in `app/views/workarea/storefront/products/templates`, and must be named matching the configuration. For instance, if your template's name is `your_template_name`, your partial filename would be `_your_template_name.html.haml`. The example below is the minimum viable implementation of a product detail template (the form that adds the SKU to the cart):
59
+
60
+ ```haml
61
+ = form_tag cart_items_path, method: 'post' do
62
+ = hidden_field_tag :product_id, product.id, id: dom_id(product, 'product_id')
63
+ = hidden_field_tag :sku, product.sku_options.first.second
64
+ = number_field_tag :quantity, 1, required: true, min: 1
65
+ = button_tag t('workarea.storefront.products.add_to_cart'), value: 'add_to_cart'
66
+ ```
67
+
68
+ The template includes a `product` local variable, passed in via the `storefront/products#show` template. This local is set to the `Workarea::Storefront::ProductViewModel` (or a child class of `ProductViewModel`, as explained below) representing the `Workarea::Catalog::Product` being rendered on the current page.
69
+
70
+ ### Create View Model (Optional)
71
+
72
+ Often times, a different product template will require different presentation logic for how to display attributes/images/variants for the product. This can be done by defining a class in the `Workarea::Storefront::ProductTemplates` module, which inherits from `Workarea::Storefront::ProductViewModel`. When wrapping the `Workarea::Catalog::Product`, the system will use the name of the template to determine whether a view model is defined for this template, and wrap the `Workarea::Catalog::Product` accordingly. Otherwise, `Workarea::Storefront::ProductViewModel` will be used as the default product view model.
73
+
74
+ In `app/view_models/workarea/storefront/product_templates/your_template_view_model.rb`:
75
+
76
+ ```ruby
77
+ module Workarea
78
+ module Storefront
79
+ module ProductTemplates
80
+ class YourTemplateViewModel < ProductViewModel
81
+ def your_special_logic
82
+ # ...
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ ```
89
+
90
+ ### Add Configuration
91
+
92
+ In order to make the template usable, an identifier to it must be added to the `Workarea.config.product_templates` collection. In a plugin, this is typically a file like **config/initializers/configuration.rb**, but it can be named anything you want. In an application, this configuration typically lives in **config/initializers/workarea.rb**. The configuration for adding a product template is as follows:
93
+
94
+ ```ruby
95
+ Workarea.configure do |config|
96
+ config.product_templates << :your_template_name
97
+ end
98
+ ```
99
+
100
+ ## Remove a Product Template
101
+
102
+ To prevent products from taking on a given product template, you can remove the template from the `config.product_templates`. Prior to doing this, however, you might want to make sure that `Catalog::Product` records which already exist in the database do not have this product template set, otherwise an error will occur when someone tries to visit the product's detail page. Make sure products for your omitted template are set to something that will display on the PDP, like `generic`, using the following line of code in the `rails console`:
103
+
104
+ ```ruby
105
+ Workarea::Catalog::Product.where(template: :omitted).update_all(template: :generic)
106
+ ```
107
+
108
+ If you're in a Workarea application, you can use the following code in an initializer to omit a given product template from being usable:
109
+
110
+ ```ruby
111
+ Workarea.configure do |config|
112
+ config.product_templates.reject! { |template| template == :omitted }
113
+ end
114
+ ```
115
+
116
+ ## Changing a Product Template
117
+
118
+ Product templates from base or acquired through a plugin may still require some changes in order for products to display according to your specifications. To accomplish this, you can decorate the view model provided by the template or override its product template to accomplish your goals. Note that most templates, as well as the `workarea/storefront/products#show` view, include append points so you don't have to override the entire template, as full view overrides tend to cause problems when upgrading between minor versions of either a plugin or the core platform.
119
+
120
+ To add logic to a plugin's product template, for example `Swatches`, decorate its product template by running the generator:
121
+
122
+ ```
123
+ ./bin/rails generate workarea:decorator storefront/product_templates/swatches_view_model
124
+ ```
125
+
126
+ Then, edit the generated `.decorator` file to add custom attributes to `#browse_link_options`:
127
+
128
+ ```ruby
129
+ module Workarea
130
+ decorate Storefront::ProductTemplates::SwatchesViewModel do
131
+ def browse_link_options
132
+ super.merge(foo: 'bar')
133
+ end
134
+ end
135
+ end
136
+ ```
137
+
138
+ You can also override an entire product template with the `workarea:override` generator:
139
+
140
+ ```
141
+ ./bin/rails generate workarea:override views workarea/storefront/products/templates/_swatches
142
+ ```
@@ -0,0 +1,483 @@
1
+ ---
2
+ title: Add, Remove, Sort, and Group Storefront Search Filters
3
+ excerpt: Learn how to programmatically manipulate and customize rendering filters on the storefront
4
+ ---
5
+
6
+ # Add, Remove, Sort, and Group Storefront Search Filters
7
+
8
+ In the course of developing a Workarea application, you may need to customize the UI for search filters on category browse and search results pages.
9
+
10
+ ![Full example](/images/filters-all.png)
11
+
12
+ In this example, the filters shown are "Category", "Color", "Size", and "Price". Although "Category" is a special case, the "Color" and "Size" filters are configurable terms filters, while "Price" is exposed as a range filter.
13
+
14
+ You may need to add a new filter...
15
+
16
+ ![Add new filter](/images/filters-material.png)
17
+
18
+ ...remove an existing filter...
19
+
20
+ ![Remove existing filter](/images/filters-omitted.png)
21
+
22
+ ...sort them in a different order...
23
+
24
+ ![Sort filters](/images/filters-sorted.png)
25
+
26
+ ..."pin" the filter to the top of the page, treating it like a special case...
27
+
28
+ ![Pin filter to the top of the page](/images/filters-pinned.png)
29
+
30
+ ...or, arrange them into groups or some other custom design:
31
+
32
+ ![Group filters together](/images/filters-groups.png)
33
+
34
+ This guide will show you how to do all of these things, and by the end of it, you'll be a Workarea filter expert!
35
+
36
+ ## Etymology
37
+
38
+ Although the user sees these objects as "filters", you may see the word "facet" referring to the same feature, especially in the backend Ruby code (for example, the `Facet` class). The term "faceted search" is derived from Elasticsearch, which originally implemented a feature called "facets" (now replaced with "aggregations") that enabled filtering a resultset by various parameters. Workarea's implementation of filters echoes and compliments this functionality provided out-of-the-box by Elasticsearch. You'll see the terms "filter" and "facet" used interchangeably in this guide, but they refer to the same feature.
39
+
40
+ ## Implementation
41
+
42
+ The API calls for the above customizations are mostly contained within the view model for the given page, so either `Storefront::SearchViewModel` or `Storefront::CategoryViewModel`. Workarea iterates over the `#facets` method for the view model in the view. This represents the collection of filters (as well as their returned data) in storefront search and category browse pages. These are the points at which one must extend in order to customize how filter display works.
43
+
44
+ The data for the `#facets` method is derived from the `#terms_facets` and `#range_facets` methods on `Search::Settings` and `Catalog::Category` (again, dependent on whether you're browsing a category or viewing search results).
45
+
46
+ To summarize, here are the relevant API calls for this guide:
47
+
48
+ - `Workarea::Storefront::SearchViewModel#facets`
49
+ - `Workarea::Storefront::CategoryViewModel#facets`
50
+ - `Workarea::Search::Settings#terms_facets`
51
+ - `Workarea::Search::Settings#range_facets`
52
+ - `Workarea::Catalog::Category#terms_facets`
53
+ - `Workarea::Catalog::Category#range_facets`
54
+
55
+ ## Add a Search Filter
56
+
57
+ The attributes to filter on are enumerated in the site's global `Search::Settings` configuration, which is editable in the admin by visiting **/admin/search_settings**. There are two types of filters provided out-of-the-box for you, **Terms** filters and **Range** filters. Let's learn more about how to manipulate both kinds:
58
+
59
+ ### Add a Terms Filter
60
+
61
+ Terms filters are completely configurable in the admin's "Search Settings" page, and require no developer assistance to configure. However, developers setting up an application for the very first time may want to decorate the `Workarea::SearchSettingsSeeds` from core to add their own filterable attributes like so:
62
+
63
+ ```ruby
64
+ module Workarea
65
+ decorate SearchSettingsSeeds do
66
+ def perform
67
+ Search::Settings.current.update_attributes!(
68
+ terms_facets: %w(Color Size Material)
69
+ )
70
+ end
71
+ end
72
+ end
73
+ ```
74
+
75
+ This will result in the "Material" filter rendering on the storefront below "Color" and "Size", since that's the order they were configured in:
76
+
77
+ ![Add new terms filter](/images/filters-material.png)
78
+
79
+ ### Add a Range Filter
80
+
81
+ The only range filter provided out-of-the-box for you by Workarea is the "Price" filter. This is configurable through the admin, but requires developer intervention to customize the admin UI so the data for these filters can be entered in.
82
+
83
+ First, configure the range facet within search settings:
84
+
85
+ ```ruby
86
+ module Workarea
87
+ decorate SearchSettingsSeeds do
88
+ def perform
89
+ Search::Settings.current.update_attributes!(
90
+ range_facets: %w(Price Height)
91
+ )
92
+ end
93
+ end
94
+ end
95
+ ```
96
+
97
+ Also, ensure that the search mapper for storefront products knows about the height and treats it as a numeric field.
98
+
99
+ ```bash
100
+ $ bin/rails generate workarea:decorator app/models/workarea/search/storefront/product.rb
101
+ ```
102
+
103
+ ```ruby
104
+ module Workarea
105
+ decorate Search::Storefront::Product do
106
+ def height
107
+ model.filters['Height'].first || 0
108
+ end
109
+
110
+ def numeric
111
+ super.merge(height: height)
112
+ end
113
+ end
114
+ end
115
+ ```
116
+
117
+ Optionally, define some tests in **test/models/workarea/search/storefront/product_test.decorator** for the new method(s) you've added:
118
+
119
+ ```ruby
120
+ require 'test_helper'
121
+
122
+ module Workarea
123
+ decorate Search::Storefront::ProductTest, with: :store do
124
+ def test_height
125
+ product = create_product(filters: { 'Height' => [6] })
126
+ mapper = Search::Storefront::Product.new(product)
127
+
128
+ assert_equal(mapper.height, product.filters['Height'].first)
129
+ end
130
+
131
+ def test_numeric
132
+ product = create_product(filters: { 'Height' => [6] })
133
+ mapper = Search::Storefront::Product.new(product)
134
+
135
+ assert_includes(mapper.numeric.keys, :height)
136
+ end
137
+ end
138
+ end
139
+ ```
140
+
141
+ Run these tests against the rest of the tests in the file by performing the following command:
142
+
143
+ ```bash
144
+ $ ./bin/rails test $(bundle show workarea-core)/test/models/workarea/search/storefront/product_test.rb
145
+ ```
146
+
147
+ Next, extend the admin UI to allow admins to update the range filter values. First, you'll learn how to add this UI to the global search settings, then you'll learn how new range facets can be added to category edit pages.
148
+
149
+ To get started, decorate `Admin::SearchSettingsController` to output the data for your custom range facet:
150
+
151
+ ```bash
152
+ $ bin/rails generate workarea:decorator app/controllers/workarea/admin/search_settings_controller.rb
153
+ ```
154
+
155
+ Replace the generated file with:
156
+
157
+ ```ruby
158
+ module Workarea
159
+ decorate Admin::SearchSettingsController do
160
+ def show
161
+ super
162
+ @height_facets = @settings.range_facets['height'] || []
163
+ end
164
+ end
165
+ end
166
+ ```
167
+
168
+ You'll now need to update the markup to add the fields necessary for editing the various range values for the filter. The easiest way to do this is to override the **workarea/admin/facets/price_inputs** partial and _rename_ it to match your new filter, like **workarea/admin/facets/height_inputs**...
169
+
170
+ ```bash
171
+ $ bin/rails generate workarea:override views workarea/admin/facets/_price_inputs.html.haml
172
+ $ cp app/views/workarea/admin/facets/_price_inputs.html.haml app/views/workarea/admin/facets/_height_inputs.html.haml
173
+ $ rm -f app/views/workarea/admin/facets/_price_inputs.html.haml
174
+ ```
175
+
176
+ This Haml template includes code specific to filtering by price, so you'll need to go through the partial and change those spots to match your new filter:
177
+
178
+ ```diff
179
+ @@ -1,5 +1,5 @@
180
+ .property
181
+ - = label_tag 'range_facets', t('workarea.admin.facets.price_inputs.height_ranges_label'), class: 'property__name'
182
+ + = label_tag 'range_facets', t('workarea.admin.facets.price_inputs.price_ranges_label'), class: 'property__name'
183
+ %table
184
+ %thead
185
+ %tr
186
+ @@ -10,14 +10,14 @@
187
+ %tr
188
+ %td
189
+ - = currency_symbol
190
+ - = text_field_tag 'range_facets[height][][from]', range['from'], title: t('workarea.admin.facets.price_inputs.from'), class: 'text-box text-box--small', id: "range_facets[height][][from][#{index}]"
191
+ + = text_field_tag 'range_facets[price][][from]', range['from'], title: t('workarea.admin.facets.price_inputs.from'), class: 'text-box text-box--small', id: "range_facets[price][][from][#{index}]"
192
+ %td
193
+ - = currency_symbol
194
+ - = text_field_tag 'range_facets[height][][to]', range['to'], title: t('workarea.admin.facets.price_inputs.to'), class: 'text-box text-box--small', id: "range_facets[height][][to][#{index}]"
195
+ + = text_field_tag 'range_facets[price][][to]', range['to'], title: t('workarea.admin.facets.price_inputs.to'), class: 'text-box text-box--small', id: "range_facets[price][][to][#{index}]"
196
+ %tr{ data: { cloneable_row: '' } }
197
+ %td
198
+ - = currency_symbol
199
+ - = text_field_tag 'range_facets[height][][from]', nil, title: t('workarea.admin.facets.price_inputs.from'), class: 'text-box text-box--small'
200
+ + = text_field_tag 'range_facets[price][][from]', nil, title: t('workarea.admin.facets.price_inputs.from'), class: 'text-box text-box--small'
201
+ %td
202
+ - = currency_symbol
203
+ - = text_field_tag 'range_facets[height][][to]', nil, title: t('workarea.admin.facets.price_inputs.to'), class: 'text-box text-box--small'
204
+ + = text_field_tag 'range_facets[price][][to]', nil, title: t('workarea.admin.facets.price_inputs.to'), class: 'text-box text-box--small'
205
+ ```
206
+
207
+ To display this new markup on the search settings page, override the **workarea/admin/search_settings/show.html.haml** partial to render your new range filter:
208
+
209
+ ```bash
210
+ $ bin/rails generate workarea:override views workarea/admin/search_settings/show.html.haml
211
+ ```
212
+
213
+ Then, on line 60 of the overridden file...
214
+
215
+ ```diff
216
+ .tabs__panel
217
+ %h2.tabs__heading= t('workarea.admin.search_settings.show.filters.title')
218
+ %p= t('workarea.admin.search_settings.show.filters.description')
219
+
220
+ .property
221
+ = label_tag 'terms_facets_list', t('workarea.admin.search_settings.show.filters.title'), class: 'property__name'
222
+ = text_field_tag 'terms_facets_list', @settings.terms_facets_list, class: 'text-box'
223
+ %span.property__note= t('workarea.admin.form.csv_field_note')
224
+
225
+ = render 'workarea/admin/facets/price_inputs', facet: @price_facets
226
+ + = render 'workarea/admin/facets/height_inputs', facet: @height_facets
227
+ ```
228
+
229
+ Finally, create a new label for the field and restart your server:
230
+
231
+ ```yaml
232
+ en:
233
+ workarea:
234
+ admin:
235
+ facets:
236
+ price_inputs:
237
+ height_ranges_label: Height Ranges
238
+ ```
239
+
240
+ The search settings page on admin will now look like this:
241
+
242
+ ![Custom admin range filter fields](/images/admin-range-filters.png)
243
+
244
+ Now that you've added a range filter to global search settings, you'll need to add it to the category edit page in order to allow categories to override the global search settings. To do this, you'll follow a slightly different path than what was described above, but the concepts are the same.
245
+
246
+ Start off by decorating `Admin::CategoryViewModel`
247
+
248
+ ```bash
249
+ $ bin/rails generate workarea:decorator app/view_models/workarea/admin/category_view_model.rb
250
+ ```
251
+
252
+ Add a new method called `#height_facet`, similar to the `@height_facet` instance variable you created in the last exercise:
253
+
254
+ ```ruby
255
+ module Workarea
256
+ decorate Admin::CategoryViewModel do
257
+ def height_facet
258
+ @height_facet ||= price_facets['height'] || []
259
+ end
260
+ end
261
+ end
262
+ ```
263
+
264
+ Then, override **workarea/admin/catalog_categories/edit.html.haml** to add your new inputs partial right after line 70:
265
+
266
+ ```bash
267
+ $ bin/rails generate workarea:override views workarea/admin/catalog_categories/edit.html.haml
268
+ ```
269
+
270
+ ```diff
271
+ = t('workarea.admin.catalog_categories.edit.filters_note_html', search_settings_link: link_to(t('workarea.admin.catalog_categories.edit.search_settings'), search_settings_path(anchor: 'filters-tab-panel')))
272
+
273
+ = render 'workarea/admin/facets/price_inputs', facet: @category.price_facet
274
+ + = render 'workarea/admin/facets/height_inputs', facet: @category.height_facet
275
+
276
+ .grid.grid--huge
277
+ .grid__cell.grid__cell--50.grid__cell--25-at-medium
278
+ ```
279
+
280
+ Now, when you restart your server and refresh the category edit page in admin, you'll see your new range filter!
281
+
282
+ ![Admin category range filter](/images/admin-category-range-filters.png)
283
+
284
+ Add in the ranges you wish to filter on, ensure there's product data for that filter, and then you'll be ready to show it on the storefront.
285
+
286
+ ![Range filter example](/images/filters-range.png)
287
+
288
+ You may have to prevent the existing price filter from showing twice, as well:
289
+
290
+ ```ruby
291
+ module Workarea
292
+ decorate Storefront::CategoryViewModel, Storefront::SearchViewModel do
293
+ def facets
294
+ super.uniq(&:system_name)
295
+ end
296
+ end
297
+ end
298
+ ```
299
+
300
+ ## Remove a Search Filter
301
+
302
+ Filters can be omitted from display on the storefront by removing them from the search settings. But this will remove the filter from displaying at all. You may want to display the filter in certain cases, for example, on a category browse page but not on a search results page. To do this, you'll need to decorate the relevant view model. Here's an example of removing the price filter from category browse pages in **app/view_models/workarea/storefront/category_view_model.decorator**:
303
+
304
+ ```ruby
305
+ module Workarea
306
+ decorate Storefront::CategoryViewModel do
307
+ def facets
308
+ super.delete_if do |facet|
309
+ facet.system_name == 'price'
310
+ end
311
+ end
312
+ end
313
+ end
314
+ ```
315
+
316
+ Before applying this decoration, filters might look like something like this:
317
+
318
+ ![Before applying the decoration](/images/filters-control.png)
319
+
320
+ After the decoration is applied, you should see the price filter omitted on category pages...
321
+
322
+ ![After applying the decoration (browse)](/images/filters-omitted.png)
323
+
324
+ ...but not on search pages!
325
+
326
+ ![After applying the decoration (search)](/images/filters-control.png)
327
+
328
+ ## Sort Filters
329
+
330
+ Out of the box, Workarea provides the following default sort order for your filters:
331
+
332
+ 1. Category (when searching)
333
+ 2. Terms filters in the order they appear in the `Search::Settings#terms_facets` Array
334
+ 3. Range filters in the order they appear in the `Search::Settings#range_facets` Array
335
+
336
+ Sorting filters can be done by manipulating the order that filters appear in the collection:
337
+
338
+ ```bash
339
+ $ bin/rails generate workarea:decorator app/seeds/workarea/search_settings_seeds.rb
340
+ ```
341
+
342
+ ```ruby
343
+ module Workarea
344
+ decorate SearchSettingsSeeds do
345
+ def perform
346
+ Search::Settings.current.update_attributes!(
347
+ terms_facets: %w(Size Color) # original order was "Color", "Size"
348
+ )
349
+ end
350
+ end
351
+ end
352
+ ```
353
+
354
+ Your filters should now look something like this:
355
+
356
+ ![Changing Size and Color Filter Sort](/images/filters-sorted.png)
357
+
358
+ ### "Special Case" Sorting
359
+
360
+ It is also possible to sort filters programmatically in the codebase to treat these filters like a "special case", for example in the case of category filtering only applying on search pages, and always sticking to the top of the filter navigation. To do so, follow the logic in "Remove a Search Filter" to decorate the appropriate view models' `#facets` method. Here's an example of "pinning" the price filter to the top of the sidebar:
361
+
362
+ ```ruby
363
+ module Workarea
364
+ decorate Storefront::CategoryViewModel, Storefront::SearchViewModel do
365
+ def facets
366
+ all_facets = super
367
+ pinned_facet = all_facets.find { |facet| facet.system_name == 'price' }
368
+
369
+ return all_facets unless pinned_facet.present?
370
+
371
+ [pinned_facet] + all_facets.delete_if { |facet| facet.system_name == 'price' }
372
+ end
373
+ end
374
+ end
375
+ ```
376
+
377
+ This multiple decoration is best defined in the file **app/view_models/workarea/storefront/product_browsing.decorator**. While you can't actually decorate the `ProductBrowsing` module, this file path will be looked up if `ProductBrowsing` is decorated, and thus your multiple decorations will apply cleanly without the need to manually load them at app initialization.
378
+
379
+ Before applying this decoration, your filters might look like something like this:
380
+
381
+ ![Before applying the decoration](../images/filters-control.png)
382
+
383
+ After the decoration is applied, you should see the price filters appearing first:
384
+
385
+ ![After applying the decoration](../images/filters-pinned.png)
386
+
387
+ ## Grouping Filters
388
+
389
+ A growing trend for retailers is to group multiple filters together in the UI. For example, a shoe retailer might want to express "Color" and "Material" within the same filter group, even though these are two distinct facets of the items in search results. In this example, you'll learn how to combine these filter values together visually, and call it "Style". To accomplish this, you'll need to override the **workarea/storefront/categories/show.html.haml** and **workarea/storefront/searches/show.html.haml** to render these filters in a slightly different way. To provide the data for this special filter group, you'll also need to override `Storefront::SearchViewModel` and `Storefront::CategoryViewModel`.
390
+
391
+ First, create a decorator at **app/view_models/workarea/storefront/product_browsing.decorator** to decorate and provide facet data for search & category browse:
392
+
393
+ ```ruby
394
+ module Workarea
395
+ decorate Storefront::CategoryViewModel, Storefront::SearchViewModel do
396
+ def style_facet
397
+ facets.find { |facet| facet.system_name == 'style' }
398
+ end
399
+
400
+ def facets_without_style
401
+ facets.reject { |facet| facet.system_name == 'style' }
402
+ end
403
+ end
404
+ end
405
+ ```
406
+
407
+ Create a new partial at **app/views/workarea/storefront/facets/_style.html.haml**, this is the partial that will be used to render your new grouped facet. Here's an example of what that might look like:
408
+
409
+ ```haml
410
+ .result-filters__section{ class: "result-filters__section--style" }
411
+ %h2= t('workarea.storefront.products.filter_title', name: 'Style')
412
+ - [color_facet, material_facet].compact.each do |facet|
413
+ %h3= facet.name
414
+ %ul.result-filters__group
415
+ - facet.results.each do |value_name, count|
416
+ %li.result-filters__filter{ class: ('result-filters__filter--selected' if facet.selected?(value_name)) }
417
+ = link_to facet_path(facet, value_name), rel: 'nofollow', class: 'result-filters__link' do
418
+ = value_name.titleize
419
+ - if facet.selected?(value_name)
420
+ %strong.result-filters__remove= t('workarea.storefront.products.remove_filter')
421
+ - else
422
+ %span.result-filters__count (#{count})
423
+ ```
424
+
425
+ It's heavily based on the out-of-box **workarea/storefront/facets/_terms.html.haml** partial, but includes two separate dependencies (`color_facet` and `material_facet`) rather than the general `facet` used in the terms filter.
426
+
427
+ Now that your partial is defined, you'll need a way to render it. Begin by generating overrides for the aforementioned views:
428
+
429
+ ```bash
430
+ $ ./bin/rails generate workarea:override views workarea/storefront/categories/show.html.haml
431
+ $ ./bin/rails generate workarea:override views workarea/storefront/searches/show.html.haml
432
+ ```
433
+
434
+ Finally, update the view to add your new facet. Here's an example of what the override to **workarea/storefront/searches#show** might look like:
435
+
436
+ ```diff
437
+ diff --git a/storefront/app/views/workarea/storefront/searches/show.html.haml b/storefront/app/views/workarea/storefront/searches/show.html.haml
438
+ index d90fcb09a..99453b389 100644
439
+ --- a/storefront/app/views/workarea/storefront/searches/show.html.haml
440
+ +++ b/storefront/app/views/workarea/storefront/searches/show.html.haml
441
+ @@ -19,12 +19,18 @@
442
+ %span.breadcrumbs__node{ itemprop: 'breadcrumb' }
443
+ %span.breadcrumbs__text= @search.query_string
444
+
445
+ - content_for :page_aside do
446
+ - if @search.facets.any?
447
+ .result-filters
448
+ - - @search.facets.each do |facet|
449
+ + - @search.facets_without_style.each do |facet|
450
+ - unless @search.autoselected_filter?(facet.system_name)
451
+ - = render "workarea/storefront/facets/#{facet.type}", facet: facet
452
+ + - if facet.system_name == 'color' && @search.style_facet.present?
453
+ + = render "workarea/storefront/facets/style", facet: facet, size_group: @search.style_facet
454
+ + - else
455
+ + = render "workarea/storefront/facets/#{facet.type}", facet: facet
456
+
457
+ .view{ data: { analytics: search_results_view_analytics_data(@search).to_json } }
458
+
459
+ @@ -81,10 +87,12 @@
460
+ .mobile-filters__content
461
+ - if @search.facets.any?
462
+ .result-filters
463
+ - - @search.facets.each do |facet|
464
+ + - @search.facets_without_style.each do |facet|
465
+ - unless @search.autoselected_filter?(facet.system_name)
466
+ - = render "workarea/storefront/facets/#{facet.type}", facet: facet
467
+ -
468
+ + - if facet.system_name == 'size' && @search.style_facet.present?
469
+ + = render "workarea/storefront/facets/style", facet: facet, size_group: @search.style_facet
470
+ + - else
471
+ + = render "workarea/storefront/facets/#{facet.type}", facet: facet
472
+ .pagination{ data: { analytics: product_list_analytics_data("Search Results for \"#{@search.query_string}\"").to_json, pagination: pagination_data(@search.products), back_to_top_button: '' } }
473
+ .grid
474
+ - @search.products.each_with_index do |product, position|
475
+ ```
476
+
477
+ Make sure `Search::Settings#terms_facets` includes the "Material" filter, and your new grouped filter will render in the storefront!
478
+
479
+ ![Filter groups](../images/filters-groups.png)
480
+
481
+ ## Additional Considerations
482
+
483
+ The storefront search filter UI is heavily cached on category browse pages. Some changes you make may not be visible until those caches expire, which can be anywhere from 15 minutes (HTTP page cache) to 1 hour (fragment cache for category pages). For this reason, Workarea developers typically favor testing changes to the filter UI on the search pages, but some special cases may force you to test on the category pages. In these cases, it's best to wait for the cache to expire.