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
helper HeaderHelper
def index
@districts = District.all.order(:name)
@schools = School.all.includes([:district]).order(:name)
@districts = districts
@district = district
@schools = schools
@school = school
@year = year
@categories = Category.sorted.map { |category| CategoryPresenter.new(category:) }
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

@ -34,26 +34,6 @@ module HeaderHelper
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:)
active?(path:) ? 'weight-700' : 'weight-400'
end

@ -9,12 +9,10 @@ import {
initializeListenersForNavDropdowns,
initializePopovers,
} from "./overview";
import { initializeListenersForHomeDropdowns } from "./home";
import { showEmptyDatasetModal } from "./modal";
document.addEventListener("turbo:load", () => {
initializeListenersForNavDropdowns();
initializeListenersForHomeDropdowns();
initializePopovers();
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"
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">
<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
picture of school performance</p>
picture of school performance</p>
</div>
</div>
<div class="bg-color-blue">
<div class="container">
<div class="row 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>
<option value="Select a school">Select a school</option>
</select>
<%= form_with(url: welcome_path, method: :get,
data: {
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>
@ -30,8 +56,8 @@
</div>
<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
outcomes.</p>
school. The three outer categories are essential inputs to school quality that influence the center two key
outcomes.</p>
</div>
<div class="col-lg-7">
<h2 class="mb-4">School Quality Measures Framework</h2>

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

File diff suppressed because it is too large Load Diff

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

@ -4,6 +4,11 @@ describe HomeController, type: :controller do
let!(:categories) do
[create(:category, name: 'Second', sort_index: 2), create(:category, name: 'First', sort_index: 1)]
end
let(:academic_year) { create(:academic_year) }
before :each do
academic_year
end
it 'fetches categories sorted by sort_index' do
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
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'
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'
click_on 'Go'
end

@ -1,6 +1,8 @@
require 'rails_helper'
describe 'home/index' do
let(:school) { create(:school) }
let(:district) { create(:district) }
subject { Nokogiri::HTML(rendered) }
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-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:
version "4.3.4"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"

Loading…
Cancel
Save