Merge pull request #5330 from openfoodfoundation/mobile-ux-filters

[mobile ux] Merge search and filter epic to master
This commit is contained in:
Luis Ramos
2020-05-13 08:59:32 +01:00
committed by GitHub
49 changed files with 527 additions and 208 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 B

View File

@@ -1,4 +1,4 @@
Darkswarm.controller "ProductsCtrl", ($scope, $filter, $rootScope, Products, OrderCycle, OrderCycleResource, FilterSelectorsService, Cart, Dereferencer, Taxons, Properties, currentHub, $timeout) ->
Darkswarm.controller "ProductsCtrl", ($scope, $sce, $filter, $rootScope, Products, OrderCycle, OrderCycleResource, FilterSelectorsService, Cart, Dereferencer, Taxons, Properties, currentHub, $timeout) ->
$scope.Products = Products
$scope.Cart = Cart
$scope.query = ""
@@ -10,6 +10,7 @@ Darkswarm.controller "ProductsCtrl", ($scope, $filter, $rootScope, Products, Ord
$scope.order_cycle = OrderCycle.order_cycle
$scope.supplied_taxons = null
$scope.supplied_properties = null
$scope.showFilterSidebar = false
$rootScope.$on "orderCycleSelected", ->
$scope.update_filters()
@@ -75,15 +76,24 @@ Darkswarm.controller "ProductsCtrl", ($scope, $filter, $rootScope, Products, Ord
$scope.appliedTaxonsList = ->
$scope.activeTaxons.map( (taxon_id) ->
Taxons.taxons_by_id[taxon_id].name
).join(" #{t('products_or')} ") if $scope.activeTaxons?
).join($scope.filtersJoinWord()) if $scope.activeTaxons?
$scope.appliedPropertiesList = ->
$scope.activeProperties.map( (property_id) ->
Properties.properties_by_id[property_id].name
).join(" #{t('products_or')} ") if $scope.activeProperties?
).join($scope.filtersJoinWord()) if $scope.activeProperties?
$scope.filtersJoinWord = ->
$sce.trustAsHtml(" <span class='join-word'>#{t('products_or')}</span> ")
$scope.clearAll = ->
$scope.clearQuery()
$scope.clearFilters()
$scope.clearQuery = ->
$scope.query = ""
$scope.clearFilters = ->
$scope.taxonSelectors.clearAll()
$scope.propertySelectors.clearAll()
@@ -94,3 +104,9 @@ Darkswarm.controller "ProductsCtrl", ($scope, $filter, $rootScope, Products, Ord
$scope.Products.products = []
$scope.update_filters()
$scope.loadProducts()
$scope.filtersCount = () ->
$scope.taxonSelectors.totalActive() + $scope.propertySelectors.totalActive()
$scope.toggleFilterSidebar = ->
$scope.showFilterSidebar = !$scope.showFilterSidebar

View File

@@ -0,0 +1,6 @@
Darkswarm.directive "focusSearch", ->
restrict: 'A'
link: (scope, element, attr)->
element.bind 'click', (event) ->
# Focus seach field, ready for typing
$(element).siblings('#search').focus()

View File

@@ -1,4 +1,3 @@
%ul
%active-selector{ ng: { repeat: "selector in allSelectors", show: "ifDefined(selector.fits, true)" } }
%render-svg{path: "{{selector.object.icon}}", ng: { if: "selector.object.icon"} }
%span{"ng-bind" => "::selector.object.name"}

View File

@@ -1,2 +1,2 @@
%button.graph-button{"ng-class" => "{open: tt_isOpen}"}
%button.graph-button{"ng-class" => "{open: tt_isOpen}", type: 'button'}
/ %i.ofn-i_058-graph

View File

@@ -2,6 +2,7 @@
@import "branding";
@import "big-input";
@import "animations";
@import "variables";
@mixin filter-selector($base-clr, $border-clr, $hover-clr) {
&.inline-block, ul.inline-block {
@@ -14,7 +15,7 @@
@include border-radius(0);
padding: 0;
margin: 0 0 0.25rem 0.25rem;
margin: 0 0.5rem 0.5rem 0;
&:hover, &:focus {
background: transparent;
@@ -107,26 +108,63 @@
// Alert when search, taxon, filter is triggered
.alert-box.search-alert {
background-color: $clr-yellow-light;
border-color: $clr-yellow-light;
background-color: $white;
color: #777;
font-size: 0.75rem;
padding: 0.5rem 0.75rem;
font-size: 1em;
padding: 0.35em 0 0;
border: 0;
margin: 0;
span.applied-properties {
color: #333;
.clear-all {
color: $grey-500;
margin-left: 1.5em;
&:hover {
color: $grey-600;
}
}
span.applied-taxons {
color: $clr-blue;
.no-results-bar {
@include breakpoint(desktop) {
text-align: center;
}
}
span.applied-search {
color: $clr-brick;
.no-results {
color: $grey-800;
font-style: italic;
font-size: 1.25em;
}
span.filter-label {
opacity: 0.75;
.clear-search {
background-color: transparent;
padding: 0;
margin: 0;
color: $orange-500;
font-size: 1.25em;
&:hover {
color: $orange-400;
}
}
span {
color: $grey-800;
font-style: italic;
&.applied-taxons, &.applied-properties {
color: $clr-blue;
font-weight: bold;
.join-word {
font-weight: normal;
}
}
&.applied-search {
font-weight: bold;
color: $teal-500;
}
}
}
@@ -140,25 +178,6 @@
.filter-shopfront {
&.taxon-selectors, &.property-selectors {
background: transparent;
single-line-selectors {
overflow-x: hidden;
white-space: nowrap;
.f-dropdown {
overflow-x: auto;
white-space: normal;
}
}
ul {
margin: 0;
display: inline-block;
}
ul, ul li {
list-style: none;
}
}
// Shopfront taxons
@@ -170,4 +189,8 @@
&.property-selectors {
@include filter-selector(#666, #ccc, #777);
}
ul {
margin: 0;
}
}

View File

@@ -7,13 +7,6 @@
// #search
@include placeholder(rgba(0, 0, 0, 0.4), #777);
input#search {
@include medium-input(rgba(0, 0, 0, 0.3), #777, $clr-brick);
// avoid zoom on iphone, see issue #4535
font-size: 1rem;
}
// ordering
product {
input {
@@ -30,22 +23,22 @@
border-color: #b3b3b3;
text-align: right;
@media all and (max-width: 1024px) {
@include breakpoint(desktop) {
width: 8rem;
}
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
width: 7rem;
}
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
float: left !important;
font-size: 0.75rem;
padding-left: 0.25rem;
padding-right: 0.25rem;
}
@media all and (max-width: 480px) {
@include breakpoint(mobile) {
width: 5.8rem;
}
@@ -69,15 +62,15 @@
input.bulk {
width: 5rem;
@media all and (max-width: 1024px) {
@include breakpoint(desktop) {
width: 4rem;
}
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
width: 3.5rem;
}
@media all and (max-width: 480px) {
@include breakpoint(mobile) {
width: 2.8rem;
}
}
@@ -93,7 +86,7 @@
.bulk-input-container {
float: right;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
float: left !important;
}

View File

@@ -1,11 +1,13 @@
@import "mixins";
@import "typography";
@import "variables";
ordercycle {
float: right;
background: $grey-050;
color: $grey-800;
width: 100%;
border-radius: 0.5em 0.5em 0 0;
border-radius: $radius-medium $radius-medium 0 0;
margin-top: 1em;
padding: 1em 1.25em 0;
@@ -17,7 +19,7 @@ ordercycle {
margin-right: 0.3rem;
}
@media all and (max-width: 1024px) {
@include breakpoint(desktop) {
float: none;
padding: 0.5em 1em;
width: 100%;
@@ -33,7 +35,7 @@ ordercycle {
}
}
@media all and (max-width: 480px) {
@include breakpoint(mobile) {
padding: 0.5em 1em 0.75em;
}
@@ -41,12 +43,12 @@ ordercycle {
border: 1px solid $teal-300;
display: inline-block;
font-size: 1em;
border-radius: 0.25em;
border-radius: $radius-small;
.select-label {
background-color: rgba($teal-300, 0.5);
display: inline-block;
border-radius: 0.25em 0 0 0.25em;
border-radius: $radius-small 0 0 $radius-small;
float: left;
font-size: 1em;
line-height: 1.3em;
@@ -80,11 +82,12 @@ ordercycle {
padding: 0.5em 1.25em 0.5em 0.75em;
height: 2.35em;
background-size: 30px auto;
border-radius: 0 0.25em 0.25em 0;
border-radius: 0 $radius-small $radius-small 0;
min-width: 13em;
@media all and (max-width: 480px) {
@include breakpoint(mobile) {
width: 100%;
min-width: 0;
}
}
@@ -92,16 +95,17 @@ ordercycle {
color: $grey-700;
}
@media all and (max-width: 1024px) {
@include breakpoint(desktop) {
float: none;
margin-right: 1em;
}
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
float: none;
margin-right: 0;
}
@media all and (max-width: 480px) {
@include breakpoint(mobile) {
display: flex;
}
}
@@ -114,7 +118,7 @@ ordercycle {
padding: 0.5em 0;
span {
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
font-size: 0.875em;
}
}
@@ -148,7 +152,7 @@ shop ordercycle {
color: $white;
padding: 0 0 12px;
@media all and (max-width: 1024px) {
@include breakpoint(desktop) {
float: none;
display: inline-block;
padding: 0.2em 0 0;
@@ -156,7 +160,7 @@ shop ordercycle {
margin-right: 1em;
}
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
float: none;
padding: 0 0 10px;
}

View File

@@ -114,7 +114,7 @@ button.graph-button {
}
}
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
// Hide for small
display: none;
}

View File

@@ -1,3 +1,4 @@
@import "mixins";
@import "branding";
@import "animations";
@@ -14,11 +15,11 @@
// outline: 1px solid red
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
font-size: 0.875rem;
}
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
font-size: 0.75rem;
}
}
@@ -56,13 +57,13 @@
.variant-name {
padding-left: 7.9375rem;
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
padding-left: 4.9375rem;
}
}
.variant-name {
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
background: #333;
color: white;
padding-left: 0.9375rem;
@@ -82,7 +83,7 @@
font-size: 0.875rem;
overflow: hidden;
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
font-size: 0.75rem;
}
}
@@ -92,7 +93,7 @@
padding-left: 0.25rem;
padding-right: 0.25rem;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
text-align: right;
}
}
@@ -106,7 +107,7 @@
color: $med-drk-grey;
}
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
background: #777;
color: $disabled-med;
@@ -132,7 +133,7 @@
padding-bottom: 1em;
line-height: 1;
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
padding-top: 0.65rem;
padding-bottom: 0.65rem;
}
@@ -141,11 +142,11 @@
.summary-header {
padding-left: 7.9375rem;
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
padding-left: 4.9375rem;
}
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
padding-left: 0.9375rem;
}

View File

@@ -1,3 +1,4 @@
@import "mixins";
@import "branding";
@import "animations";
@@ -56,7 +57,7 @@
}
}
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
top: 2px;
width: 4rem;
height: 4rem;
@@ -70,7 +71,7 @@
}
}
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
display: none;
width: 0rem;
height: 0rem;

View File

@@ -1,3 +1,5 @@
@import "mixins";
.darkswarm {
products {
product {
@@ -10,7 +12,7 @@
padding-top: 0.25rem;
z-index: 999999;
@media all and (max-width: 480px) {
@include breakpoint(mobile) {
background-size: 28px 32px;
min-height: 32px;
width: 28px;
@@ -27,11 +29,11 @@
}
}
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
margin-top: -0.85rem;
}
@media all and (max-width: 480px) {
@include breakpoint(mobile) {
render-svg {
svg {
width: 18px;

View File

@@ -1,5 +1,6 @@
@import "branding";
@import "mixins";
@import "variables";
.account-summary {
color: #4a4a4a;
@@ -99,7 +100,7 @@
table {
width: 100%;
border-radius: 0.5em 0.5em 0 0;
border-radius: $radius-medium $radius-medium 0 0;
tr:nth-of-type(even) {
background: transparent;

View File

@@ -26,7 +26,7 @@
display: block;
border: 0;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
margin-bottom: 1rem;
}
@@ -45,7 +45,7 @@
}
// Generic text resize
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
&, & * {
font-size: 0.875rem;
}
@@ -114,7 +114,7 @@
.fat > div {
border-top: 1px solid #aaa;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
margin-top: 1em;
}

View File

@@ -16,7 +16,7 @@
margin-top: 2px;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
margin-bottom: 1em;
}
}

View File

@@ -23,7 +23,7 @@
box-shadow: none;
color: $inputactv;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
font-size: 1.25rem;
}

View File

@@ -43,6 +43,9 @@ $black: #000;
$white: #fff;
$grey-050: #f7f7f7;
$grey-100: #e6e6e6;
$grey-200: #ddd;
$grey-300: #ccc;
$grey-400: #bbb;
$grey-500: #999;
$grey-600: #777;

View File

@@ -13,7 +13,7 @@
checkout {
display: block;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
&.row .row {
margin-left: 0;
margin-right: 0;
@@ -24,7 +24,7 @@ checkout {
.button, table {
width: 100%;
}
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
form.edit_order {
border: 1px solid $disabled-bright;
margin-bottom: 2rem;

View File

@@ -1,3 +1,4 @@
@import "mixins";
@import 'typography';
section {
@@ -34,7 +35,7 @@ section {
@include headingFont;
}
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
location, location + small {
display: block;
}
@@ -44,7 +45,7 @@ section {
margin-top: 0;
padding-top: 0.45em;
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
margin-bottom: 8px;
}
}

View File

@@ -1,3 +1,4 @@
@import "mixins";
@import "typography";
$large-menu-height: 4.6875rem;
@@ -97,7 +98,7 @@ body.embedded {
display: none;
}
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
nav.top-bar {
height: 3.4rem;
padding: 0.2rem $gutter-width;
@@ -141,7 +142,7 @@ body.embedded {
}
}
@media all and (max-width: 480px) {
@include breakpoint(mobile) {
ul.left li.powered-by span {
display: none;
}

View File

@@ -66,7 +66,7 @@
font-weight: 300;
}
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
h2 {
font-size: 52px;
}
@@ -87,7 +87,7 @@
padding-bottom: 0;
}
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
.row .row {
padding: 0;
}
@@ -139,7 +139,7 @@
font-weight: 300;
color: $brand-colour;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
font-size: 45px;
}
}

View File

@@ -45,7 +45,7 @@
}
//Hub Link
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
a.hub {
display: block;
}
@@ -67,7 +67,7 @@
.active_table_row {
border: 1px solid transparent;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
border-color: $clr-brick-light;
}
@@ -85,7 +85,7 @@
}
&.open, &.closed {
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
.active_table_row:first-child .skinny-head {
background-color: $clr-brick-light;
@@ -164,7 +164,7 @@
}
}
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
.active_table_row:first-child .skinny-head {
background-color: rgba(255, 255, 255, 0.85);
}
@@ -218,7 +218,7 @@
}
// Small devices
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
.active_table_row:first-child .skinny-head {
background-color: $disabled-bright;
}
@@ -226,7 +226,7 @@
}
// Small devices
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
.active_table_row, .active_table_row:first-child, .active_table_row:last-child {
border-color: $disabled-bright;
background-color: transparent;
@@ -253,7 +253,7 @@
cursor: auto;
}
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
border-color: transparent;
}
}

View File

@@ -13,7 +13,7 @@
&.placeholder {
opacity: 0.35;
@media all and (max-width: 1024px) {
@include breakpoint(desktop) {
display: none;
}
}
@@ -31,7 +31,7 @@
max-height: 260px;
overflow: hidden;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
min-height: 68px;
}
}

View File

@@ -1,6 +1,7 @@
// Place all the styles related to the map controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
@import "mixins";
@import "big-input";
.map-container {
@@ -29,7 +30,7 @@
margin-top: 1.2rem;
margin-left: 1rem;
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
width: 80%;
}

View File

@@ -171,7 +171,7 @@ nav.top-bar {
.tab-bar {
background-color: white;
border-bottom: 1px solid $light-grey-transparency;
height: 2.8em;
height: $mobile-nav-height;
position: fixed;
width: 100%;
z-index: 1;
@@ -210,6 +210,10 @@ nav.top-bar {
}
}
.off-canvas-wrap {
overflow: inherit;
}
.off-canvas-list li.language-switcher ul li {
list-style-type: none;
padding-left: 0.5em;

View File

@@ -16,7 +16,7 @@
padding-top: 100px;
padding-bottom: 100px;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
padding-top: 25px;
}
}
@@ -255,3 +255,18 @@
background-repeat: no-repeat;
background-size: 922px 922px;
}
@mixin breakpoint($point) {
@if $point == desktop {
@media all and (max-width: 1024px) { @content; }
}
@else if $point == tablet {
@media all and (max-width: 768px) { @content; }
}
@else if $point == phablet {
@media all and (max-width: 640px) { @content; }
}
@else if $point == mobile {
@media all and (max-width: 480px) { @content; }
}
}

View File

@@ -57,7 +57,7 @@
p {
line-height: 2.4;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
line-height: 1.4;
}
}
@@ -193,7 +193,7 @@
display: inline-block;
border-bottom: 1px solid transparent;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
display: none;
}
}

View File

@@ -1,3 +1,4 @@
@import "mixins";
@import "branding";
@import "animations";
@import "compass/css3/transition";
@@ -19,7 +20,7 @@ $page-alert-height: 55px;
margin: 0;
h6 {
@media all and (max-width: 480px) {
@include breakpoint(mobile) {
font-size: 10px;
line-height: 24px;
}

View File

@@ -4,7 +4,7 @@
.producers {
.active_table .active_table_node {
// Header row
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
.skinny-head {
background-color: $clr-turquoise-light;
@@ -137,7 +137,7 @@
.active_table_row.closed {
border: 1px solid transparent;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
border-color: $clr-turquoise-light;
}

View File

@@ -11,18 +11,98 @@
@import "shop-taxon-flag";
@import "shop-popovers";
$sidebar-small-width: 75%;
$sidebar-medium-width: 65%;
$sidebar-large-width: 45%;
$sidebar-footer-height: 5em;
.darkswarm {
.shop-filters-sidebar {
display: flex;
flex-direction: column;
height: 100%;
.background {
position: fixed;
top: 0;
right: 0;
z-index: 200;
height: 100%;
width: 100%;
background-color: $shop-sidebar-overlay;
opacity: 0;
transition: opacity $transition-sidebar;
}
&.shown {
.background {
opacity: 1;
}
.sidebar, .sidebar-footer {
margin-right: 0;
}
}
.sidebar {
position: fixed;
top: 0;
right: 0;
z-index: 210;
height: 100%;
width: $sidebar-large-width;
margin-right: -$sidebar-large-width;
background-color: rgba($white, 0.95);
padding: 1em;
transition: margin $transition-sidebar;
overflow-y: scroll;
.property-selectors {
margin-bottom: $sidebar-footer-height + 2em;
}
}
.sidebar-footer {
background-color: $grey-800;
width: $sidebar-large-width;
margin-right: -$sidebar-large-width;
height: $sidebar-footer-height;
position: fixed;
bottom: 0;
right: 0;
transition: margin $transition-sidebar;
padding: 1em;
button {
width: 48%;
}
}
@include breakpoint(tablet) {
.sidebar, .sidebar-footer {
width: $sidebar-medium-width;
margin-right: -$sidebar-medium-width;
}
}
@include breakpoint(mobile) {
.sidebar, .sidebar-footer {
width: $sidebar-small-width;
margin-right: -$sidebar-small-width;
}
}
}
products {
display: block;
padding-top: 20px;
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
input.button.right {
float: left;
}
}
@media all and (max-width: 480px) {
@include breakpoint(mobile) {
.add_to_cart {
margin-top: 2rem;
}
@@ -69,7 +149,7 @@
.bulk-buy {
font-size: 0.875rem;
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
font-size: 0.75rem;
}
}
@@ -92,7 +172,7 @@
font-size: 0.75em;
padding-right: 0.9375rem;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
padding-right: 0.25rem;
}
}

View File

@@ -0,0 +1,75 @@
@import "mixins";
@import "branding";
@import "variables";
.shop-searchbar {
background-color: $grey-100;
height: 5em;
padding: 1em 0;
margin-bottom: 1em;
position: relative;
z-index: 5;
.search-wrap {
position: relative;
width: 100%;
display: inline-flex;
.clear {
height: 1em;
width: 1em;
margin-top: 1em;
position: absolute;
right: 1em;
}
}
input#search {
height: 3em;
border-radius: $radius-small;
border: solid 1px $grey-300;
margin: 0;
padding: 0 2.25em 0 2.75em;
width: 100%;
min-width: 0;
background: $white url("/assets/icn-search-grey.png") 1em center no-repeat;
font-size: 1rem; // avoid zoom on iphone, see issue #4535
&::placeholder {
font-style: italic;
}
// Remove conflicting "clear search" buttons added by Chrome
&::-webkit-search-decoration,
&::-webkit-search-cancel-button,
&::-webkit-search-results-button,
&::-webkit-search-results-decoration {
display: none;
}
}
button {
background-color: $grey-600;
margin-left: 1em;
height: 3em;
width: 7em;
padding: 0;
font-size: 1em;
border-radius: $radius-small;
transition: none;
&:hover {
background-color: $grey-700;
}
@include breakpoint(mobile) {
margin-left: 0.75em;
}
}
@include breakpoint(desktop) {
position: -webkit-sticky;
position: sticky;
top: $mobile-nav-height;
}
}

View File

@@ -8,16 +8,18 @@
.tab-buttons {
color: $dark-grey;
box-shadow: $distributor-header-shadow;
position: relative;
z-index: 10;
.columns {
display: flex;
@media all and (max-width: 1024px) {
@include breakpoint(desktop) {
display: table;
width: 100%;
}
@media all and (max-width: 480px) {
@include breakpoint(mobile) {
padding: 0;
}
}
@@ -54,7 +56,7 @@
background: none;
}
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
padding: 0.35em 0 0.65em 0;
}
}
@@ -67,7 +69,7 @@
}
}
@media all and (max-width: 1024px) {
@include breakpoint(desktop) {
display: table-cell;
width: auto;
}
@@ -104,7 +106,7 @@
p {
max-width: 100%;
@media all and (max-width: 768px) {
@include breakpoint(tablet) {
height: auto !important;
}
}

View File

@@ -10,7 +10,7 @@
.tab {
text-align: center;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
text-align: left;
}
@@ -24,7 +24,7 @@
padding: 1em;
border: none;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
padding: 0.35em 0 0.65em 0;
text-shadow: none;
}
@@ -37,7 +37,7 @@
border-bottom: 4px solid $clr-brick-bright;
cursor: pointer;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
transition: none;
color: white;
background-color: $clr-brick-bright;
@@ -46,7 +46,7 @@
a {
color: $clr-brick-bright;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
color: #ffffff;
}
}
@@ -55,14 +55,14 @@
&.selected {
border-bottom: 4px solid $clr-brick;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
background-color: $clr-brick;
}
a {
color: $clr-brick;
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
color: #ffffff;
}
}

View File

@@ -57,7 +57,7 @@
}
}
@media all and (max-width: 640px) {
@include breakpoint(phablet) {
render-svg {
svg {
width: 24px;

View File

@@ -2,6 +2,7 @@
@import "branding";
@import "mixins";
@import "typography";
@import "variables";
// Button class extensions
@@ -123,9 +124,37 @@ button.success, .button.success {
}
}
button.large {
height: 3em;
font-size: 1em;
color: $white;
border-radius: $radius-medium;
margin: 0;
padding: 0;
&.dark {
background-color: $grey-800;
border: 1px solid $grey-600;
}
&.bright {
background-color: $orange-500;
border: none;
}
}
// Responsive
@media screen and (min-width: 768px) {
[role="main"] {
padding: 0;
}
}
.flex {
display: flex;
}
.no-gutter {
padding-right: 0;
padding-left: 0;
}

View File

@@ -30,3 +30,11 @@ $topbar-dropdown-link-color: $black;
$topbar-dropdown-bg: $white;
$topbar-dropdown-link-bg: $white;
$topbar-dropdown-link-bg-hover: $white;
$mobile-nav-height: 2.8em;
$radius-small: 0.25em;
$radius-medium: 0.5em;
$shop-sidebar-overlay: rgba(0, 0, 0, 0.5);
$transition-sidebar: 250ms ease-in-out 0s;

View File

@@ -1,4 +1,4 @@
.joyride-tip-guide{"ng-class" => "{ in: open }", "ng-show" => "open"}
.cart-dropdown.joyride-tip-guide{"ng-class" => "{ in: open }", "ng-show" => "open"}
%span.joyride-nub.top
.joyride-content-wrapper
%h5

View File

@@ -0,0 +1,9 @@
%span{ "ng-show" => "query && ( appliedPropertiesList() || appliedTaxonsList() )" }
= t :products_filters_in
%span.applied-properties{'ng-bind-html' => 'appliedPropertiesList()'}
%span{ "ng-show" => "appliedPropertiesList() && appliedTaxonsList()" }
= t :products_and
%span.applied-taxons{'ng-bind-html' => 'appliedTaxonsList()'}

View File

@@ -1,5 +1,5 @@
.filter-shopfront.taxon-selectors.text-right{ng: {show: 'supplied_taxons != null'}}
%single-line-selectors{ selectors: "taxonSelectors", objects: "supplied_taxons", "active-selectors" => "activeTaxons"}
.filter-shopfront.taxon-selectors{ng: {show: 'supplied_taxons != null'}}
%filter-selector{ 'selector-set' => "taxonSelectors", objects: "supplied_taxons", "active-selectors" => "activeTaxons"}
.filter-shopfront.property-selectors.text-right{ng: {show: 'supplied_properties != null'}}
%single-line-selectors{ selectors: "propertySelectors", objects: "supplied_properties", "active-selectors" => "activeProperties"}
.filter-shopfront.property-selectors{ng: {show: 'supplied_properties != null'}}
%filter-selector{ 'selector-set' => "propertySelectors", objects: "supplied_properties", "active-selectors" => "activeProperties"}

View File

@@ -1,54 +1,48 @@
.footer-pad.small-12.columns
%form{action: main_app.cart_path}
%products{"ng-controller" => "ProductsCtrl", "ng-init" => "refreshStaleData()", "ng-show" => "order_cycle.order_cycle_id != null", "ng-cloak" => true }
// TODO: Needs an ng-show to slide content down
.row.animate-slide{ "ng-show" => "query || appliedPropertiesList() || appliedTaxonsList()" }
= render partial: "shop/products/searchbar"
.row
.small-12.columns
.alert-box.search-alert.ng-scope
%a.right{"ng-click" => "clearAll()"}
= t :products_clear_all
%i.ofn-i_009-close
%span.filter-label
= t :products_showing
%span.applied-properties
{{ appliedPropertiesList() }}
%span.applied-taxons
{{ appliedTaxonsList() }}
%span{ ng: { hide: "!query"} }
%span{ "ng-show" => "appliedPropertiesList() || appliedTaxonsList()" }
= t :products_with
%span.applied-search "{{ query }}"
.row
.small-12.medium-6.large-5.columns
%input#search.text{"ng-model" => "query",
placeholder: t(:products_search),
"ng-debounce" => "200",
"ofn-disable-enter" => true}
.small-12.medium-6.large-6.large-offset-1.columns
= render partial: "shop/products/filters"
.row
%div.pad-top{ "infinite-scroll" => "loadMore()", "infinite-scroll-distance" => "1", "infinite-scroll-disabled" => 'Products.loading' }
%product.animate-repeat{"ng-controller" => "ProductNodeCtrl", "ng-repeat" => "product in Products.products track by product.id", "id" => "product-{{ product.id }}"}
= render "shop/products/summary"
%shop-variant{variant: 'variant', "ng-repeat" => "variant in product.variants | orderBy: ['name_to_display','unit_value'] track by variant.id", "id" => "variant-{{ variant.id }}", "ng-class" => "{'out-of-stock': !variant.on_demand && variant.on_hand == 0}"}
%product{"ng-show" => "Products.loading"}
.row.summary
.small-12.columns.text-center
= t :products_loading
.footer-pad.small-12.columns.no-gutter
.row
.small-12.columns.text-center
%img.spinner{ src: "/assets/spinning-circles.svg" }
.medium-12.large-10.columns
= render partial: "shop/products/search_feedback"
%div{"ng-show" => "Products.products.length == 0 && !Products.loading"}
.row.summary
.small-12.columns
%p.no-results
= t :search_no_results_html, query: "<strong>{{query}}</strong>".html_safe
.row
.small-12.columns
%form{action: main_app.cart_path}
%i.ofn-i_011-spinner.cart-spinner{"ng-show" => "Cart.dirty"}
%input.small.button.primary.right.add_to_cart{type: :submit, value: "{{ Cart.dirty ? '#{t(:products_updating_cart)}' : (Cart.empty() ? '#{t(:products_cart_empty)}' : '#{t(:products_edit_cart)}' ) }}", "ng-disabled" => "Cart.dirty || Cart.empty()", "ng-class" => "{ dirty: Cart.dirty }" }
%div.pad-top{ "infinite-scroll" => "loadMore()", "infinite-scroll-distance" => "1", "infinite-scroll-disabled" => 'Products.loading' }
%product.animate-repeat{"ng-controller" => "ProductNodeCtrl", "ng-repeat" => "product in Products.products track by product.id", "id" => "product-{{ product.id }}"}
= render "shop/products/summary"
%shop-variant{variant: 'variant', "ng-repeat" => "variant in product.variants | orderBy: ['name_to_display','unit_value'] track by variant.id", "id" => "variant-{{ variant.id }}", "ng-class" => "{'out-of-stock': !variant.on_demand && variant.on_hand == 0}"}
%product{"ng-show" => "Products.loading"}
.row.summary
.small-12.columns.text-center
= t :products_loading
.row
.small-12.columns.text-center
%img.spinner{ src: "/assets/spinning-circles.svg" }
.hide-for-medium-down.large-2.columns
%h5
= t(:products_filter_by)
%span{ng: {show: 'filtersCount()' }}
= "({{ filtersCount() }} #{t(:products_filter_selected)})"
= render partial: "shop/products/filters"
.shop-filters-sidebar.hide-for-large-up{ng: {show: 'showFilterSidebar', class: "{'shown': showFilterSidebar}"}}
.background{ng: {click: 'toggleFilterSidebar()'}}
.sidebar
%h5
= t(:products_filter_by)
%span{ng: {show: 'filtersCount()' }}
= "({{ filtersCount() }} #{t(:products_filter_selected)})"
= render partial: "shop/products/filters"
.sidebar-footer
%button.large.dark.left{type: 'button', ng: {click: 'clearFilters()'}}
= t(:products_filter_clear)
%button.large.bright.right{type: 'button', ng: {click: 'toggleFilterSidebar()'}}
= t(:products_filter_done)

View File

@@ -0,0 +1,24 @@
.row.animate-slide{ "ng-show" => "query || appliedPropertiesList() || appliedTaxonsList()" }
.small-12.columns
.alert-box.search-alert.ng-scope
%div{"ng-show" => "Products.products.length > 0"}
%a.clear-all.right{"ng-click" => "clearAll()"}
= t :products_clear
%i.ofn-i_009-close
%span.filter-label
= t :products_results_for
%span{ ng: { hide: "!query"} }
%span.applied-search
{{ query }}
= render partial: 'shop/products/applied_filters_feedback'
%div.no-results-bar{"ng-show" => "Products.products.length == 0 && !Products.loading"}
.row.summary
.small-12.columns
%p.no-results
= t :products_no_results_html, query: "<span class='applied-search'>{{query}}</span>".html_safe
= render partial: 'shop/products/applied_filters_feedback'
%button.clear-search{type: 'button', ng: {click: 'clearAll()'}}
= t :products_clear_search

View File

@@ -0,0 +1,17 @@
.shop-searchbar
.row
.small-12.large-5.columns.flex
%div.search-wrap
%input#search.text{"ng-model" => "query",
type: 'search',
placeholder: t(:products_search),
"ng-debounce" => "200",
"ofn-disable-enter" => true}
%a.clear{type: 'button', ng: {show: 'query', click: 'clearQuery()'}, 'focus-search' => true}
%img{ src: "/assets/icn-close.png" }
.hide-for-large-up
%button{type: 'button', ng: {click: 'toggleFilterSidebar()'}}
= t(:products_filter_heading)
%span{ng: {show: 'filtersCount()' }}
({{ filtersCount() }})

View File

@@ -6,7 +6,5 @@
= render partial: "shop/messages/closed_shop"
- else
.row
.small-12.columns
= render partial: "shop/messages/select_oc"
= render partial: "shop/products/form"
= render partial: "shop/messages/select_oc"
= render partial: "shop/products/form"

View File

@@ -1648,11 +1648,19 @@ See the %{link} to find out more about %{sitename}'s features and to start using
other: You have <a href='%{path}' target='_blank'>%{count} orders with %{shop}</a> currently open for review. You can make changes until %{oc_close}.
orders_changeable_orders_alert_html: This order has been confirmed, but you can make changes until <strong>%{oc_close}</strong>.
products_clear_all: Clear all
products_clear: Clear
products_showing: "Showing:"
products_or: "OR"
products_results_for: "Results for"
products_or: "or"
products_and: "and"
products_filters_in: "in"
products_with: with
products_search: "Search by product or producer"
products_search: "Search..."
products_filter_by: "Filter by"
products_filter_selected: "selected"
products_filter_heading: "Filters"
products_filter_clear: "Clear"
products_filter_done: "Done"
products_loading: "Loading products..."
products_updating_cart: "Updating cart..."
products_cart_empty: "Cart empty"
@@ -1663,6 +1671,8 @@ See the %{link} to find out more about %{sitename}'s features and to start using
products_update_error_msg: "Saving failed."
products_update_error_data: "Save failed due to invalid data:"
products_changes_saved: "Changes saved."
products_no_results_html: "Sorry, no results found for %{query}"
products_clear_search: "Clear search"
search_no_results_html: "Sorry, no results found for %{query}. Try another search?"

View File

@@ -34,7 +34,7 @@ feature "full-page cart", js: true do
click_link "Continue shopping"
expect(page).to have_no_link "Continue shopping"
expect(page).to have_button "Edit your cart"
expect(page).to have_link "Shop"
expect(page).to have_no_content distributor.preferred_shopfront_message
end
end

View File

@@ -46,9 +46,8 @@ feature "Using embedded shopfront functionality", js: true do
it "allows shopping and checkout" do
on_embedded_page do
fill_in "variants[#{variant.id}]", with: 1
wait_until_enabled 'input.add_to_cart'
first("input.add_to_cart:not([disabled='disabled'])").click
edit_cart
expect(page).to have_text 'Your shopping cart'
find('a#checkout-link').click
@@ -91,11 +90,11 @@ feature "Using embedded shopfront functionality", js: true do
it "redirects to embedded hub on logout when embedded" do
on_embedded_page do
wait_for_shop_loaded
wait_for_cart
find('ul.right li#login-link a').click
login_with_modal
wait_for_shop_loaded
wait_for_cart
wait_until { page.find('ul.right li.user-menu.has-dropdown').value.present? }
logout_via_navigation
@@ -106,14 +105,6 @@ feature "Using embedded shopfront functionality", js: true do
private
# When you have pending changes and try to navigate away from a page, it asks you "Are you sure?".
# When we click the "Update" button to save changes, we need to wait
# until it is actually saved and "loading" disappears before doing anything else.
def wait_for_shop_loaded
page.has_no_content? "Loading"
page.has_no_css? "input[value='Updating cart...']"
end
def login_with_modal
page.has_selector? 'div.login-modal', visible: true

View File

@@ -86,7 +86,7 @@ feature "shopping with variant overrides defined", js: true do
it "shows the correct prices in the shopping cart" do
fill_in "variants[#{product1_variant1.id}]", with: "2"
add_to_cart
edit_cart
expect(page).to have_selector "tr.line-item.variant-#{product1_variant1.id} .cart-item-price", text: with_currency(61.11)
expect(page).to have_field "order[line_items_attributes][0][quantity]", with: '2'

View File

@@ -1,7 +1,17 @@
module ShopWorkflow
def add_to_cart
wait_until_enabled 'input.add_to_cart'
first("input.add_to_cart:not([disabled='disabled'])").click
def wait_for_cart
first("#cart").click
within '.cart-dropdown' do
expect(page).to_not have_link "Updating cart..."
end
end
def edit_cart
wait_for_cart
within '.cart-dropdown' do
expect(page).to have_link "Edit your cart"
end
first("a.add_to_cart").click
end
def have_price(price)