diff --git a/Gemfile b/Gemfile
index 3f50fdb8..6468dcee 100644
--- a/Gemfile
+++ b/Gemfile
@@ -60,5 +60,5 @@ group :test do
end
gem "cssbundling-rails", "~> 1.4"
-
+gem "stimulus-rails"
gem "jsbundling-rails", "~> 1.3"
diff --git a/Gemfile.lock b/Gemfile.lock
index c3bce76e..b57dac4c 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -410,6 +410,8 @@ GEM
standard-performance (1.5.0)
lint_roller (~> 1.1)
rubocop-performance (~> 1.22.0)
+ stimulus-rails (1.3.4)
+ railties (>= 6.0.0)
stringio (3.1.1)
terser (1.2.4)
execjs (>= 0.3.0, < 3)
@@ -498,6 +500,7 @@ DEPENDENCIES
spring
spring-commands-rspec
standard
+ stimulus-rails
terser
turbo-rails
turbolinks (~> 5)
diff --git a/app/assets/stylesheets/application.tailwind.css.scss b/app/assets/stylesheets/application.tailwind.css.scss
index 2d0e913c..9947c61a 100644
--- a/app/assets/stylesheets/application.tailwind.css.scss
+++ b/app/assets/stylesheets/application.tailwind.css.scss
@@ -3,3 +3,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
+
+.active {
+ @apply border-[3px] border-black border-solid border-b-0 text-black -mb-[3px] bg-white;
+}
diff --git a/app/javascript/application.js b/app/javascript/application.js
index c6cee654..34536749 100644
--- a/app/javascript/application.js
+++ b/app/javascript/application.js
@@ -71,3 +71,4 @@ document.addEventListener("DOMContentLoaded", function () {
});
});
});
+import "./controllers";
diff --git a/app/javascript/controllers/application.js b/app/javascript/controllers/application.js
new file mode 100644
index 00000000..d6fe5ebe
--- /dev/null
+++ b/app/javascript/controllers/application.js
@@ -0,0 +1,9 @@
+import { Application } from "@hotwired/stimulus";
+
+const application = Application.start();
+
+// Configure Stimulus development experience
+application.debug = false;
+window.Stimulus = application;
+
+export { application };
diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js
new file mode 100644
index 00000000..4c8a6827
--- /dev/null
+++ b/app/javascript/controllers/index.js
@@ -0,0 +1,8 @@
+// This file is auto-generated by ./bin/rails stimulus:manifest:update
+// Run that command whenever you add a new controller or create them with
+// ./bin/rails generate stimulus controllerName
+
+import { application } from "./application";
+
+import TabController from "./tab_controller";
+application.register("tab", TabController);
diff --git a/app/javascript/controllers/tab_controller.js b/app/javascript/controllers/tab_controller.js
new file mode 100644
index 00000000..4ebf3ae8
--- /dev/null
+++ b/app/javascript/controllers/tab_controller.js
@@ -0,0 +1,20 @@
+import { Controller } from "@hotwired/stimulus";
+
+// Connects to data-controller="tab"
+export default class extends Controller {
+ static classes = ["active"];
+ static targets = ["day"];
+
+ connect() {}
+
+ switch_tab() {
+ this.makeAllTabsInactive();
+ this.dayTarget.classList.toggle(this.activeClass);
+ }
+
+ makeAllTabsInactive() {
+ document
+ .querySelectorAll(".tabs .tab")
+ .forEach((el) => el.classList.remove(this.activeClass));
+ }
+}
diff --git a/app/views/styled_forecasts/_day_tab.html.erb b/app/views/styled_forecasts/_day_tab.html.erb
index d98d7e60..bafe73b2 100644
--- a/app/views/styled_forecasts/_day_tab.html.erb
+++ b/app/views/styled_forecasts/_day_tab.html.erb
@@ -1,19 +1,31 @@
-
- <%= link_to update_styled_forecast_path(day: day), data: { turbo_frame: "day_predictions" } do %>
-
- <%= forecast.date == Date.today ? 'Today' : forecast.date.strftime('%A') %>
-
-
- <%= forecast.date.strftime("%d %B") %>
-
-
- ●
-
-
- <%= forecast.air_pollution.label.capitalize %>
-
-
- Index <%= forecast.air_pollution.value %>/10
-
- <% end %>
-
+<%= tag.div class: "tab py-4 flex-1 #{day} mx-2 px-1 text-center text-gray-400 daqi-low border-b-0 border-l-2 border-r-2 border-t-2 border-gray-400 border-dashed #{day == :today ? "active" : ''}",
+ data: {
+ date: forecast.date.to_s,
+ controller: "tab",
+ "tab-active-class": "active",
+ "tab-target": "day"
+ } do
+ %>
+<%=
+ link_to update_styled_forecast_path(day: day),
+ data: {
+ turbo_frame: "day_predictions",
+ action: "click->tab#switch_tab"
+ } do %>
+
+ <%= forecast.date == Date.today ? 'Today' : forecast.date.strftime('%A') %>
+
+
+
<%= render "day_tab", forecast: @forecasts.first, day: :today %>
<%= render "day_tab", forecast: @forecasts.second, day: :tomorrow %>
<%= render "day_tab", forecast: @forecasts.third, day: :day_after_tomorrow %>
diff --git a/package.json b/package.json
index a64ae9dc..3f77cb2b 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
"yarn": "1.22.22"
},
"dependencies": {
+ "@hotwired/stimulus": "^3.2.2",
"@hotwired/turbo-rails": "^8.0.10",
"autoprefixer": "^10.4.20",
"govuk-frontend": "^5.6.0",
diff --git a/spec/feature_steps/forecast_steps.rb b/spec/feature_steps/forecast_steps.rb
index 14505487..317a32e0 100644
--- a/spec/feature_steps/forecast_steps.rb
+++ b/spec/feature_steps/forecast_steps.rb
@@ -50,10 +50,16 @@ def and_i_switch_to_the_tab_for_tomorrow
switch_to_tab_for(:tomorrow)
end
+ def and_i_switch_to_the_tab_for_day_after_tomorrow
+ switch_to_tab_for(:day_after_tomorrow)
+ end
+
def switch_to_tab_for(day)
case day
when :tomorrow
find(".tab.tomorrow a").click
+ when :day_after_tomorrow
+ find(".tab.day_after_tomorrow a").click
else
raise "day: #{day} not expected"
end
@@ -105,6 +111,27 @@ def and_i_see_predicted_uv_level_v2
expect_prediction_v2(category: "ultraviolet-rays-uv", value: "Low")
end
+ def then_i_see_that_the_tomorrow_tab_is_active
+ expect(page).to have_css(".tab.tomorrow.active")
+
+ expect(page).not_to have_css(".tab.today.active")
+ expect(page).not_to have_css(".tab.day_after_tomorrow.active")
+ end
+
+ def and_i_see_that_the_today_tab_is_active
+ expect(page).to have_css(".tab.today.active")
+
+ expect(page).not_to have_css(".tab.tomorrow.active")
+ expect(page).not_to have_css(".tab.day_after_tomorrow.active")
+ end
+
+ def then_i_see_that_the_day_after_tomorrow_tab_is_active
+ expect(page).to have_css(".tab.day_after_tomorrow.active")
+
+ expect(page).not_to have_css(".tab.today.active")
+ expect(page).not_to have_css(".tab.tomorrow.active")
+ end
+
def and_i_see_predicted_uv_level_for_tomorrow
expect_styled_prediction(category: :"ultraviolet-rays-uv", level: :moderate)
end
@@ -117,6 +144,18 @@ def and_i_see_predicted_temperature_level_for_tomorrow
expect_styled_prediction(category: :temperature, level: :moderate)
end
+ def and_i_see_predicted_uv_level_for_day_after_tomorrow
+ expect_styled_prediction(category: :"ultraviolet-rays-uv", level: :high)
+ end
+
+ def and_i_see_predicted_pollen_level_for_day_after_tomorrow
+ expect_styled_prediction(category: :pollen, level: :high)
+ end
+
+ def and_i_see_predicted_temperature_level_for_day_after_tomorrow
+ expect_styled_prediction(category: :temperature, level: :high)
+ end
+
def and_i_see_predicted_pollen_level_v2
expect_prediction_v2(category: :pollen, value: "Low")
end
@@ -163,6 +202,9 @@ def expect_uv_content_for_level(level)
when :moderate
expect(page).to have_content("Moderate")
expect(page).to have_content(I18n.t("prediction.guidance.ultraviolet_rays_uv.#{level}"))
+ when :high
+ expect(page).to have_content("High")
+ expect(page).to have_content(I18n.t("prediction.guidance.ultraviolet_rays_uv.#{level}"))
else
raise "unexpected level #{level}"
end
@@ -173,6 +215,9 @@ def expect_pollen_content_for_level(level)
when :moderate
expect(page).to have_content("Moderate")
expect(page).to have_content(I18n.t("prediction.guidance.pollen.#{level}"))
+ when :high
+ expect(page).to have_content("High")
+ expect(page).to have_content(I18n.t("prediction.guidance.pollen.#{level}"))
else
raise "unexpected level #{level}"
end
@@ -182,6 +227,8 @@ def expect_temperature_content_for_level(level)
case level
when :moderate
expect(page).to have_content("9°C - 16°C")
+ when :high
+ expect(page).to have_content("27°C - 31°C")
else
raise "unexpected level #{level}"
end
diff --git a/spec/features/visitors/view_styled_forecasts_spec.rb b/spec/features/visitors/view_styled_forecasts_spec.rb
index af9fe291..f367c562 100644
--- a/spec/features/visitors/view_styled_forecasts_spec.rb
+++ b/spec/features/visitors/view_styled_forecasts_spec.rb
@@ -24,6 +24,8 @@
visit root_path
when_i_select_view_forecasts_v2
then_i_see_the_forecasts_page_v2
+
+ and_i_see_that_the_today_tab_is_active
and_i_see_predicted_air_pollution_status_for_each_day_v2
and_i_see_predicted_uv_level_v2
and_i_see_predicted_pollen_level_v2
@@ -40,9 +42,25 @@
when_i_select_view_forecasts_v2
and_i_switch_to_the_tab_for_tomorrow
- # then I see that the _tomorrow_ tab is active
+ then_i_see_that_the_tomorrow_tab_is_active
and_i_see_predicted_uv_level_for_tomorrow
and_i_see_predicted_pollen_level_for_tomorrow
and_i_see_predicted_temperature_level_for_tomorrow
end
+
+ scenario "See detail for day after tomorrow", js: true do
+ given_a_forecast_for_today
+ and_a_forecast_for_tomorrow
+ and_a_forecast_for_the_day_after_tomorrow
+ and_the_response_from_cercs_api_is_stubbed_accordingly
+
+ visit root_path
+ when_i_select_view_forecasts_v2
+ and_i_switch_to_the_tab_for_day_after_tomorrow
+
+ then_i_see_that_the_day_after_tomorrow_tab_is_active
+ and_i_see_predicted_uv_level_for_day_after_tomorrow
+ and_i_see_predicted_pollen_level_for_day_after_tomorrow
+ and_i_see_predicted_temperature_level_for_day_after_tomorrow
+ end
end
diff --git a/yarn.lock b/yarn.lock
index 02ce53ae..2aacc04c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -233,6 +233,11 @@
dependencies:
levn "^0.4.1"
+"@hotwired/stimulus@^3.2.2":
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/@hotwired/stimulus/-/stimulus-3.2.2.tgz#071aab59c600fed95b97939e605ff261a4251608"
+ integrity sha512-eGeIqNOQpXoPAIP7tC1+1Yc1yl1xnwYqg+3mzqxyrbE5pg5YFBZcA6YoTiByJB6DKAEsiWtl6tjTJS4IYtbB7A==
+
"@hotwired/turbo-rails@^8.0.10":
version "8.0.12"
resolved "https://registry.yarnpkg.com/@hotwired/turbo-rails/-/turbo-rails-8.0.12.tgz#6f1a2661122c0a2bf717f3bc68b5106638798c89"