diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 956fd4b79..313f6fba9 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -4,6 +4,7 @@ @import 'limber/_labware-mixins'; @import 'bootstrap'; @import 'limber/screen'; +@import 'limber/icons'; @import 'limber/plate'; @import 'limber/tube'; @import 'limber/tube-rack'; diff --git a/app/assets/stylesheets/limber/colours.scss b/app/assets/stylesheets/limber/colours.scss index e852f478c..3fc6edc01 100644 --- a/app/assets/stylesheets/limber/colours.scss +++ b/app/assets/stylesheets/limber/colours.scss @@ -1,3 +1,9 @@ +/* Colours defined per environment */ +$production: $gray-800 !default; +$training: $green !default; +$staging: $red !default; +$development: $gray-100 !default; + @mixin shade-aliquot($base_colour, $text_colour) { &.aliquot, &.pool_name { diff --git a/app/assets/stylesheets/limber/icons.scss b/app/assets/stylesheets/limber/icons.scss new file mode 100644 index 000000000..1c477d831 --- /dev/null +++ b/app/assets/stylesheets/limber/icons.scss @@ -0,0 +1,57 @@ +// Bare Bones Icon System + +// Usage: +// +// Where {name} is the name of the icon and {color} is the bootstrap theme color +// (e.g. icon-user-primary, icon-user-secondary, icon-user-success, etc.) + +$icon-size: 24px; + +/// Replace `$search` with `$replace` in `$string` +/// @author Kitty Giraudel +/// @param {String} $string - Initial string +/// @param {String} $search - Substring to replace +/// @param {String} $replace ('') - New value +/// @return {String} - Updated string +@function str-replace($string, $search, $replace: '') { + $index: str-index($string, $search); + + @if $index { + @return str-slice($string, 1, $index - 1) + $replace + + str-replace(str-slice($string, $index + str-length($search)), $search, $replace); + } + + @return $string; +} + +@mixin icon($name, $path, $theme-color) { + // split theme color into name and value + $theme-color-name: nth($theme-color, 1); + $theme-color-value: nth($theme-color, 2); + // escape characters for html + $escaped-color: str-replace(inspect($theme-color-value), '#', '%23'); + + .icon-#{$name}-#{$theme-color-name} { + display: inline-block; + width: $icon-size; + height: $icon-size; + background-image: url('data:image/svg+xml, #{$path} '); + background-repeat: no-repeat; + background-position: center; + background-size: $icon-size; + } +} + +// icons list +// (add new icons here, note names must not contain dashes '-') +@each $theme-color in $theme-colors { + // iterate over bootstrap theme colors + + // icons by Heroicons https://heroicons.com/ + // user-circle + @include icon( + 'user', + '', + $theme-color + ); +} diff --git a/app/assets/stylesheets/limber/screen.scss b/app/assets/stylesheets/limber/screen.scss index d0b64f0a6..80bc2a471 100644 --- a/app/assets/stylesheets/limber/screen.scss +++ b/app/assets/stylesheets/limber/screen.scss @@ -9,52 +9,134 @@ body { margin-bottom: 50px; } -header.limber-header { +/* Background colours for different environments */ +.bg-production { + background-color: $production; +} +.bg-training { + background-color: $training; +} +.bg-staging { + background-color: $staging; +} +.bg-development { + background-color: $development; +} + +// navbar styles +%navbar-dark-style { nav { - @extend .navbar; - @extend .navbar-expand-md; @extend .navbar-dark; } + .navbar-text { + @extend .text-light; + } + .icon-user { + @extend .icon-user-light; + } } - -#app { - margin-bottom: 100px; +%navbar-light-style { + nav { + @extend .navbar-light; + } + .navbar-text { + @extend .text-dark; + } + .icon-user { + @extend .icon-user-secondary; + } + .btn-logout { + @extend .btn-light; + @extend .border; + } + @extend .border-bottom; // add border to bottom to increase visibility + border-bottom-color: #eaecef !important; // set border color to match jumbotron background } -#flashes { - @extend .col-12; +// development and unknown environments +header.limber-header { + @extend %navbar-light-style; + nav { + @extend .navbar; + @extend .navbar-expand-md; + @extend .py-3; + @extend .bg-development; + } } -#plate-title, -#tube-title, -.card-header, -#tube-rack-title { - font-weight: 300; +.environment { + // surround in bold square brackets + &:before { + content: '['; + font-weight: bold; + } + &:after { + content: ']'; + font-weight: bold; + } + @extend .navbar-text; + @extend .small; + @extend .mr-2; } +// Set the background color of the navbar based on the environment .production { header.limber-header { + @extend %navbar-dark-style; nav { - @extend .bg-dark; + @extend .bg-production; } .environment { display: none; } + .btn-logout { + @extend .btn-danger; + @extend .border-0; + } } } -.nonproduction { + +.training { header.limber-header { + @extend %navbar-dark-style; nav { - @extend .bg-danger; + @extend .bg-training; } - .environment { - @extend .badge; - @extend .badge-light; - @extend .mr-2; + .btn-logout { + @extend .btn-success; + border-color: darken($training, 5%) !important; + } + } +} + +.staging { + header.limber-header { + @extend %navbar-dark-style; + nav { + @extend .bg-staging; + } + .btn-logout { + @extend .btn-danger; + border-color: darken($staging, 5%) !important; } } } +#app { + margin-bottom: 100px; +} + +#flashes { + @extend .col-12; +} + +#plate-title, +#tube-title, +.card-header, +#tube-rack-title { + font-weight: 300; +} + .tube-list, .plate-list { @extend .list-group; @@ -120,6 +202,13 @@ nav.robots-list { font-style: oblique; font-stretch: ultra-condensed; font-weight: lighter; + @extend .mr-2; +} + +.btn-logout { + @extend .btn; + @extend .btn-secondary; + @extend .rounded; } .logged_in { diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 1663b324c..22916a7fa 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -27,10 +27,6 @@ def environment Rails.env end - def environment_type_class - Rails.env.production? ? 'production' : 'nonproduction' - end - def each_robot(&block) Robots.each_robot(&block) end @@ -40,4 +36,33 @@ def pipeline_groups return [] if Settings.pipelines.list.empty? Settings.pipelines.map(&:pipeline_group).uniq.sort end + + # Returns the appropriate icon suffix for the current environment + # Returns empty string for production + # Returns "-#{environment}" for training, staging + # Returns "-development" for any other environment + # @return [String] The suffix to append to the icon name + def icon_suffix + environment = Rails.env + case environment + when 'production' + '' + when 'training', 'staging' + "-#{environment}" + else + '-development' + end + end + + # Return the appropriate favicon for the current environment + # @return [String] The path to the favicon + def favicon + "favicon#{icon_suffix}.ico" + end + + # Return the appropriate apple-touch-icon for the current environment + # @return [String] The path to the apple-touch-icon + def apple_touch_icon + "apple-touch-icon#{icon_suffix}.png" + end end diff --git a/app/helpers/session_helper.rb b/app/helpers/session_helper.rb index cf2e1cb0c..326b0862f 100644 --- a/app/helpers/session_helper.rb +++ b/app/helpers/session_helper.rb @@ -30,6 +30,6 @@ def check_for_login! end def session_switcher - link_to 'Log Out', logout_sessions_path, class: 'btn btn-danger' if logged_in? + link_to 'Log Out', logout_sessions_path, class: 'btn-logout' if logged_in? end end diff --git a/app/views/application/_flash_messages.html.erb b/app/views/application/_flash_messages.html.erb index 2d02be224..f85ef35aa 100644 --- a/app/views/application/_flash_messages.html.erb +++ b/app/views/application/_flash_messages.html.erb @@ -1,8 +1,8 @@
<% flash.each do |name, message| %>
-

<%= name.titleize %>

-
    <% Array(message).each do |m| %>
  • <%= m %> <% end %>
+ <%= name.titleize %> +
    <% Array(message).each do |m| %>
  • <%= m %> <% end %>
<% end %>
diff --git a/app/views/application/_header.html.erb b/app/views/application/_header.html.erb index 65953ac28..1c2a78425 100644 --- a/app/views/application/_header.html.erb +++ b/app/views/application/_header.html.erb @@ -12,11 +12,14 @@ - <% if logged_in? %> - Currently logged in as, <%= user_name.titlecase %> <%= session_switcher %> + + + <%= user_name.titlecase %> + + <%= session_switcher %> <% else %> - Not logged in + Not logged in <% end %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 665cd180c..d7eff1795 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -21,11 +21,12 @@ <% end %> - + + - +
<%= render 'header' %> <%= render partial: 'flash_messages' %> diff --git a/public/apple-touch-icon-development.png b/public/apple-touch-icon-development.png new file mode 100644 index 000000000..cb7ba6536 Binary files /dev/null and b/public/apple-touch-icon-development.png differ diff --git a/public/apple-touch-icon-staging.png b/public/apple-touch-icon-staging.png new file mode 100644 index 000000000..482d58cb0 Binary files /dev/null and b/public/apple-touch-icon-staging.png differ diff --git a/public/apple-touch-icon-training.png b/public/apple-touch-icon-training.png new file mode 100644 index 000000000..5ba98fd52 Binary files /dev/null and b/public/apple-touch-icon-training.png differ diff --git a/public/favicon-development.ico b/public/favicon-development.ico new file mode 100644 index 000000000..5dacd7248 Binary files /dev/null and b/public/favicon-development.ico differ diff --git a/public/favicon-staging.ico b/public/favicon-staging.ico new file mode 100644 index 000000000..6b097e9a1 Binary files /dev/null and b/public/favicon-staging.ico differ diff --git a/public/favicon-training.ico b/public/favicon-training.ico new file mode 100644 index 000000000..e18613323 Binary files /dev/null and b/public/favicon-training.ico differ diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb new file mode 100644 index 000000000..4b6658db1 --- /dev/null +++ b/spec/helpers/application_helper_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ApplicationHelper do + describe '#favicon' do + subject(:favicon) { helper.favicon } + + it 'returns the favicon path for the production environment' do + allow(Rails).to receive(:env).and_return('production') + expect(favicon).to eq('favicon.ico') + end + + it 'returns the favicon path for the training environment' do + allow(Rails).to receive(:env).and_return('training') + expect(favicon).to eq('favicon-training.ico') + end + + it 'returns the favicon path for the staging environment' do + allow(Rails).to receive(:env).and_return('staging') + expect(favicon).to eq('favicon-staging.ico') + end + + it 'returns the favicon path for the development environment' do + allow(Rails).to receive(:env).and_return('development') + expect(favicon).to eq('favicon-development.ico') + end + + it 'returns the favicon path for an unknown environment' do + allow(Rails).to receive(:env).and_return('unknown') + expect(favicon).to eq('favicon-development.ico') + end + end + + describe '#apple_touch_icon' do + subject(:apple_touch_icon) { helper.apple_touch_icon } + + it 'returns the apple-touch-icon path for the production environment' do + allow(Rails).to receive(:env).and_return('production') + expect(apple_touch_icon).to eq('apple-touch-icon.png') + end + + it 'returns the apple-touch-icon path for the training environment' do + allow(Rails).to receive(:env).and_return('training') + expect(apple_touch_icon).to eq('apple-touch-icon-training.png') + end + + it 'returns the apple-touch-icon path for the staging environment' do + allow(Rails).to receive(:env).and_return('staging') + expect(apple_touch_icon).to eq('apple-touch-icon-staging.png') + end + + it 'returns the apple-touch-icon path for the development environment' do + allow(Rails).to receive(:env).and_return('development') + expect(apple_touch_icon).to eq('apple-touch-icon-development.png') + end + + it 'returns the apple-touch-icon path for an unknown environment' do + allow(Rails).to receive(:env).and_return('unknown') + expect(apple_touch_icon).to eq('apple-touch-icon-development.png') + end + end +end