Replace javascript logic with hotwire. Also hide district dropdown on

home page if there is only one district.
pull/1/head
rebuilt 3 years ago
parent 87802c034e
commit 69179ce157

@ -2,10 +2,53 @@
class HomeController < ApplicationController class HomeController < ApplicationController
helper HeaderHelper helper HeaderHelper
def index def index
@districts = District.all.order(:name) @districts = districts
@schools = School.all.includes([:district]).order(:name) @district = district
@schools = schools
@school = school
@year = year
@categories = Category.sorted.map { |category| CategoryPresenter.new(category:) } @categories = Category.sorted.map { |category| CategoryPresenter.new(category:) }
end end
private
def districts
District.all.order(:name).map do |district|
[district.name, district.id]
end
end
def district
return District.first if District.count == 1
District.find(params[:district]) if params[:district].present?
end
def schools
if district.present?
district.schools.order(:name).map do |school|
[school.name, school.id]
end
else
[]
end
end
def school
School.find(params[:school]) if params[:school].present?
end
def year
latest_response_rate = ResponseRate.where(school:)
.where('meets_student_threshold = ? or meets_teacher_threshold = ?', true, true)
.joins('inner join academic_years a on response_rates.academic_year_id=a.id')
.order('a.range DESC').first
academic_year = latest_response_rate.academic_year.range if latest_response_rate.present?
academic_year || AcademicYear.order('range DESC').first.range
end
end end

@ -34,26 +34,6 @@ module HeaderHelper
end end
end end
def school_mapper(school)
academic_year = latest_year(school)
{
name: school.name,
district_id: school.district_id,
url: district_school_overview_index_path(school.district, school,
{ year: academic_year.range })
}
end
def latest_year(school)
latest_response_rate = ResponseRate.where(school:)
.where('meets_student_threshold = ? or meets_teacher_threshold = ?', true, true)
.joins('inner join academic_years a on response_rates.academic_year_id=a.id')
.order('a.range DESC').first
academic_year = latest_response_rate.academic_year if latest_response_rate.present?
academic_year || AcademicYear.order('range DESC').first
end
def link_weight(path:) def link_weight(path:)
active?(path:) ? 'weight-700' : 'weight-400' active?(path:) ? 'weight-700' : 'weight-400'
end end

@ -9,12 +9,10 @@ import {
initializeListenersForNavDropdowns, initializeListenersForNavDropdowns,
initializePopovers, initializePopovers,
} from "./overview"; } from "./overview";
import { initializeListenersForHomeDropdowns } from "./home";
import { showEmptyDatasetModal } from "./modal"; import { showEmptyDatasetModal } from "./modal";
document.addEventListener("turbo:load", () => { document.addEventListener("turbo:load", () => {
initializeListenersForNavDropdowns(); initializeListenersForNavDropdowns();
initializeListenersForHomeDropdowns();
initializePopovers(); initializePopovers();
showEmptyDatasetModal(); showEmptyDatasetModal();
}); });

@ -0,0 +1,13 @@
import { Controller } from "@hotwired/stimulus"
import debounce from "debounce";
// Connects to data-controller="form"
export default class extends Controller {
initialize() {
this.submit = debounce(this.submit.bind(this), 300)
}
submit() {
this.element.requestSubmit();
}
}

@ -6,3 +6,6 @@ import { application } from "./application"
import AnalyzeController from "./analyze_controller.js" import AnalyzeController from "./analyze_controller.js"
application.register("analyze", AnalyzeController) application.register("analyze", AnalyzeController)
import FormController from "./form_controller.js"
application.register("form", FormController)

@ -1,49 +0,0 @@
import 'bootstrap';
export function initializeListenersForHomeDropdowns() {
const districtDropdown = document.querySelector("#district-dropdown");
if (districtDropdown) {
const schoolDropdown = document.querySelector("#school-dropdown");
districtDropdown.addEventListener("change", (event) => {
const districtId = Number(event.target.value);
const schoolsInDistrict = window.schools.filter(
(school) => school.district_id === districtId
);
schoolDropdown.replaceChildren(
...schoolsInDistrict.map((school) => {
return createOptionForSelect(school.name, school.url, false);
})
);
let optionElem = createOptionForSelect("Select a school", schoolDropdown.firstChild.value, true)
schoolDropdown.insertBefore(optionElem, schoolDropdown.firstChild);
schoolDropdown.disabled = false;
});
schoolDropdown.addEventListener("change", (event) => {
const goButton = document.querySelector('button[data-id="go-to-school"]');
goButton.disabled = false;
});
document
.querySelector('button[data-id="go-to-school"]')
.addEventListener("click", (event) => {
const selectedSchoolURL = schoolDropdown.value;
window.location = selectedSchoolURL;
});
}
}
function createOptionForSelect(name, value, selected) {
const optionElem = document.createElement("option");
optionElem.setAttribute("value", value);
if (selected === true) {
optionElem.setAttribute("selected", "selected");
}
const schoolNameNode = document.createTextNode(name);
optionElem.appendChild(schoolNameNode);
return optionElem;
}

@ -2,20 +2,46 @@
<div class="hero-text"> <div class="hero-text">
<h1 class="sub-header">School Quality Measures Dashboard</h1> <h1 class="sub-header">School Quality Measures Dashboard</h1>
<p class="hero-description">A school quality framework with multiple measures that offers a fair and comprehensive <p class="hero-description">A school quality framework with multiple measures that offers a fair and comprehensive
picture of school performance</p> picture of school performance</p>
</div> </div>
</div> </div>
<div class="bg-color-blue"> <div class="bg-color-blue">
<div class="container"> <div class="container">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-6 welcome-controls d-flex justify-content-center"> <div class="col-6 welcome-controls d-flex justify-content-center">
<%= collection_select(:district, :id, @districts, :id, :name, { prompt: 'Select a district', selected: "" }, { id: 'district-dropdown', 'data-id': 'district-dropdown', class: 'form-select' }) %>
<select id="school-dropdown" class="mx-3 form-select" data-id="school-dropdown" disabled> <%= form_with(url: welcome_path, method: :get,
<option value="Select a school">Select a school</option> data: {
</select> turbo_frame: "schools",
turbo_action: "advance",
controller: "form",
action: "input->form#submit"
}) do |f| %>
<%= turbo_frame_tag "schools" do %>
<div class="d-flex">
<% if District.count > 1 %>
<div class="row">
<%= f.select :district, @districts,
{include_blank: "Select a District", selected: params[:district] } , {id: "district-dropdown", class: "form-select", hidden: @districts.count == 1} %>
</div>
<% end %>
<div>
<%= f.select :school, @schools,
{include_blank: "Select a School", selected: params[:school]}, { id: "school-dropdown", class: "form-select mx-3"} if @schools %>
</div>
<% if @school.present? %>
<%= link_to "Go", district_school_overview_index_path(@district, @school, {year: @year} ), class: "mx-4 btn btn-secondary" , data: {turbo_frame: "_top"} %>
<% else %>
<%= button_to "Go", "/", class: "mx-4 btn btn-secondary" , data: {turbo_frame: "_top"}, disabled: true %>
<% end %>
</div>
<% end %>
<% end %>
<button data-id="go-to-school" class="mx-3 btn btn-secondary" disabled>Go</button>
</div> </div>
</div> </div>
</div> </div>
@ -30,8 +56,8 @@
</div> </div>
<p class="mt-5">The School Quality Measures Framework aims to describe the full measure of what makes a good <p class="mt-5">The School Quality Measures Framework aims to describe the full measure of what makes a good
school. The three outer categories are essential inputs to school quality that influence the center two key school. The three outer categories are essential inputs to school quality that influence the center two key
outcomes.</p> outcomes.</p>
</div> </div>
<div class="col-lg-7"> <div class="col-lg-7">
<h2 class="mb-4">School Quality Measures Framework</h2> <h2 class="mb-4">School Quality Measures Framework</h2>

@ -27,8 +27,5 @@
<%= render partial: 'layouts/footer' %> <%= render partial: 'layouts/footer' %>
</div> </div>
<script>
window.schools = <%= @schools.map{ |school| school_mapper(school) }.to_json.html_safe %>;
</script>
</body> </body>
</html> </html>

File diff suppressed because it is too large Load Diff

@ -17,7 +17,8 @@
"babel-preset-es2015": "^6.24.1", "babel-preset-es2015": "^6.24.1",
"bootstrap": "^5.1.3", "bootstrap": "^5.1.3",
"esbuild": "^0.13.6", "esbuild": "^0.13.6",
"sass": "^1.43.4" "sass": "^1.43.4",
"debounce": "^1.2.1"
}, },
"scripts": { "scripts": {
"build": "esbuild app/javascript/*.* --bundle --outdir=app/assets/builds", "build": "esbuild app/javascript/*.* --bundle --outdir=app/assets/builds",

@ -4,6 +4,11 @@ describe HomeController, type: :controller do
let!(:categories) do let!(:categories) do
[create(:category, name: 'Second', sort_index: 2), create(:category, name: 'First', sort_index: 1)] [create(:category, name: 'Second', sort_index: 2), create(:category, name: 'First', sort_index: 1)]
end end
let(:academic_year) { create(:academic_year) }
before :each do
academic_year
end
it 'fetches categories sorted by sort_index' do it 'fetches categories sorted by sort_index' do
get :index get :index

@ -1,94 +0,0 @@
import { initializeListenersForHomeDropdowns } from "home";
describe("School selection and go button", () => {
let changeDistrict, changeSchool, clickGo;
beforeEach(() => {
window.schools = [
{
district_id: 1,
url: "/school1url",
name: "school 1 name",
},
{
district_id: 2,
url: "/school2url",
name: "school 2 name",
},
];
document.body.innerHTML = `
<div>
<select id="school-dropdown" data-id="school-dropdown" disabled></select>
<select id="district-dropdown" data-id="district-dropdown">
<option value="1">District 1</option>
<option value="2">District 2</option>
</select>
<button data-id="go-to-school" disabled></button>
</div>`;
const districtDropdown = document.getElementById("district-dropdown");
changeDistrict = (districtName) => {
districtDropdown.value = Array.from(districtDropdown.children).find(
(element) => element.innerHTML === districtName
).value;
const event = new Event("change");
districtDropdown.dispatchEvent(event);
};
changeSchool = (schoolName) => {
const schoolDropdown = document.getElementById("school-dropdown");
schoolDropdown.value = Array.from(schoolDropdown.children).find(
(element) => element.innerHTML === schoolName
).value;
const event = new Event("change");
schoolDropdown.dispatchEvent(event);
};
clickGo = () => {
const goButton = document.querySelector('button[data-id="go-to-school"]');
const clickEvent = new Event("click");
goButton.dispatchEvent(clickEvent);
};
initializeListenersForHomeDropdowns();
});
it("populates school dropdown only with schools from the selected district and the prompt", () => {
const schoolDropdown = document.getElementById("school-dropdown");
changeDistrict("District 1");
expect(schoolDropdown.firstChild.innerHTML).toBe("Select a school");
expect(schoolDropdown.childElementCount).toBe(2);
changeDistrict("District 2");
expect(schoolDropdown.childElementCount).toBe(2);
expect(Array.from(schoolDropdown.options).map((o) => o.text)).toContain(
"school 2 name"
);
});
it("Clicking the go button redirects to the school url", () => {
delete window.location;
window.location = "";
changeDistrict("District 1");
changeSchool("school 1 name");
clickGo();
expect(window.location).toBe("/school1url");
});
it("enables School dropdown once a district is selected", () => {
const schoolDropdown = document.getElementById("school-dropdown");
changeDistrict("District 1");
expect(schoolDropdown.disabled).toBe(false);
});
it("enables Go button once a school is selected", () => {
changeDistrict("District 1");
changeSchool("school 1 name");
const goButton = document.querySelector('button[data-id="go-to-school"]');
expect(goButton.disabled).toBe(false);
});
});

@ -163,8 +163,13 @@ def district_admin_sees_problem_solving_emphasis
end end
def go_to_school_overview_from_welcome_page(district, school) def go_to_school_overview_from_welcome_page(district, school)
expect(page).to have_select('district', selected: 'Select a District')
select district.name, from: 'district-dropdown' select district.name, from: 'district-dropdown'
expect(page).to have_select('school', selected: 'Select a School')
visit welcome_path({ district: district.id, school: school.id })
expect(page).to have_select('school', selected: 'Winchester High School')
select school.name, from: 'school-dropdown' select school.name, from: 'school-dropdown'
click_on 'Go' click_on 'Go'
end end

@ -1,6 +1,8 @@
require 'rails_helper' require 'rails_helper'
describe 'home/index' do describe 'home/index' do
let(:school) { create(:school) }
let(:district) { create(:district) }
subject { Nokogiri::HTML(rendered) } subject { Nokogiri::HTML(rendered) }
before :each do before :each do

File diff suppressed because it is too large Load Diff

@ -2205,6 +2205,11 @@ data-urls@^2.0.0:
whatwg-mimetype "^2.3.0" whatwg-mimetype "^2.3.0"
whatwg-url "^8.0.0" whatwg-url "^8.0.0"
debounce@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5"
integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==
debug@4, debug@^4.1.0, debug@^4.1.1: debug@4, debug@^4.1.0, debug@^4.1.1:
version "4.3.4" version "4.3.4"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"

Loading…
Cancel
Save