Show modal when no measures for a school/year have meet their threshold

pull/1/head
Liam Morley 4 years ago
parent edeb3f4e59
commit cf6e80ce6b

@ -30,6 +30,9 @@ $input-border-radius: 0;
$input-btn-focus-color-opacity: 0.35;
$btn-border-radius: 0;
$btn-close-width: 1.5em;
$btn-close-opacity: 1;
$nav-link-color: $white;
$nav-link-font-weight: 600;
$nav-link-hover-color: $nav-link-color;
@ -48,3 +51,6 @@ $popover-border-color: $gray-2;
$popover-border-radius: 4px;
$popover-body-padding-x: map-get($spacers, 4);
$popover-body-padding-y: map-get($spacers, 4);
$modal-header-border-color: transparent;
$modal-content-border-radius: 0;

@ -50,3 +50,7 @@
.popover {
box-shadow: 0 0 8px rgba($black, 0.25);
}
.modal-content {
padding: 40px;
}

@ -2,7 +2,6 @@ class DashboardController < SqmApplicationController
def index
@variance_chart_row_presenters = Measure.all.map(&method(:presenter_for_measure))
@category_presenters = SqmCategory.sorted.map { |sqm_category| CategoryPresenter.new(category: sqm_category) }
end

@ -16,6 +16,7 @@ class SqmApplicationController < ActionController::Base
@school = School.find_by_slug school_slug
@schools = School.where(district: @district).sort_by(&:name)
@academic_year = AcademicYear.find_by_range params[:year]
@has_empty_dataset = Measure.none_meet_threshold? school: @school, academic_year: @academic_year
end
def district_slug

@ -9,9 +9,11 @@ Turbolinks.start()
ActiveStorage.start()
import { initializeListenersForNavDropdowns, initializePopovers } from "./dashboard"
import { initializeListenersForHomeDropdowns } from "./home"
import { showEmptyDatasetModal } from "./modal"
document.addEventListener("turbolinks:load", () => {
initializeListenersForNavDropdowns()
initializeListenersForHomeDropdowns()
initializePopovers()
showEmptyDatasetModal()
})

@ -0,0 +1,8 @@
import { Modal } from "bootstrap";
export function showEmptyDatasetModal() {
const modal = document.querySelector('.modal');
if(modal){
new Modal(modal).show();
}
}

@ -7,6 +7,10 @@ class Measure < ActiveRecord::Base
scope :source_includes_survey_items, ->() { joins(:survey_items).uniq }
def self.none_meet_threshold?(school:, academic_year:)
none? { |measure| SurveyItemResponse.sufficient_data?(measure: measure, school: school, academic_year: academic_year) }
end
def teacher_survey_items
@teacher_survey_items ||= survey_items.where("survey_item_id LIKE 't-%'")
end

@ -5,4 +5,5 @@ class SqmCategory < ActiveRecord::Base
scope :sorted, ->() { order(:sort_index) }
has_many :subcategories
has_many :measures, through: :subcategories
end

@ -25,6 +25,12 @@ class SurveyItemResponse < ActiveRecord::Base
end
end
def self.sufficient_data?(measure:, school:, academic_year:)
meets_teacher_threshold = teacher_sufficient_data?(measure: measure, school: school, academic_year: academic_year)
meets_student_threshold = student_sufficient_data?(measure: measure, school: school, academic_year: academic_year)
meets_teacher_threshold || meets_student_threshold
end
private
def self.for_measure_meeting_threshold(measure:, school:, academic_year:)
@ -46,11 +52,6 @@ class SurveyItemResponse < ActiveRecord::Base
scope :teacher_responses_for_measure, ->(measure) { for_measure(measure).where("survey_items.survey_item_id LIKE 't-%'") }
scope :student_responses_for_measure, ->(measure) { for_measure(measure).where("survey_items.survey_item_id LIKE 's-%'") }
def self.sufficient_data?(measure:, school:, academic_year:)
meets_teacher_threshold = teacher_sufficient_data?(measure: measure, school: school, academic_year: academic_year)
meets_student_threshold = student_sufficient_data?(measure: measure, school: school, academic_year: academic_year)
meets_teacher_threshold || meets_student_threshold
end
def self.student_sufficient_data?(measure:, school:, academic_year:)
if measure.includes_student_survey_items?

@ -0,0 +1,20 @@
<div id="empty-dataset-modal" class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title">No results available</h3>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Were unable to display results for the selected school and school year due to one or more of the following reasons:</p>
<ul>
<li>Limited availability of school admin data</li>
<li>Low teacher and/or student survey response rates </li>
<li>Limited or no historical data for this school or district</li>
</ul>
<p>You may continue to explore the structure of the MCIEA School Quality Measures Framework if you like, but we recommend selecting a different school and/or school year for the best experience. </p>
<p>Youre also welcome to <a href="https://www.mciea.org/contact.html" target="_blank">contact MCIEA</a> to help ensure this school has data for you to review next year.</p>
</div>
</div>
</div>
</div>

@ -44,5 +44,7 @@
<%= render partial: 'layouts/sqm/footer' %>
</div>
<%= render partial: 'layouts/sqm/empty_dataset_modal' if @has_empty_dataset %>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

@ -20,7 +20,7 @@ FactoryBot.define do
name { "A #{rand} category" }
category_id { rand.to_s }
description { "A description of a category" }
slug { "a-#{rand}-category" }
slug { name.parameterize }
sort_index { 1 }
end

@ -0,0 +1,49 @@
import { showEmptyDatasetModal } from "modal";
describe("Empty data set modal", () => {
describe("When a modal element exists on the page", () => {
beforeEach(() => {
document.body.innerHTML = `<html><body>
<div class="modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
</body> </html> `;
});
it("Adds a class to make the modal visible", () => {
showEmptyDatasetModal();
const modal = document.querySelector(".modal");
expect(modal.classList.contains('show')).toBe(true);
});
});
describe("When a modal doesn't exist on the page", () => {
beforeEach(() => {
document.body.innerHTML = `
<html>
<body></body>
</html>
`
})
it("ignores the content", () =>{
showEmptyDatasetModal();
const modal = document.querySelector(".modal");
expect(modal).toBe(null);
})
})
});

@ -54,7 +54,12 @@ RSpec.configure do |config|
config.include Capybara::DSL
config.before(:each, type: :system) do
driven_by :rack_test
end
config.before(:each, js: true) do
driven_by :apparition
Capybara.default_max_wait_time = 10
Capybara.page.driver.resize(3000, 3000)
end

@ -0,0 +1,43 @@
require 'rails_helper'
describe 'authentication' do
let(:district) { create(:district) }
let(:school) { create(:school, district: district) }
let(:academic_year) { create(:academic_year) }
context 'when using the wrong credentials' do
before :each do
page.driver.browser.basic_authorize('wrong username', 'wrong password')
end
it 'does not show any information' do
visit dashboard_path
expect(page).not_to have_text(school.name)
end
end
context 'when using the right credentials' do
before :each do
page.driver.browser.basic_authorize(username, password)
end
it 'does show information' do
visit dashboard_path
expect(page).to have_text(school.name)
end
end
private
def username
district.name.downcase
end
def password
"#{username}!"
end
def dashboard_path
district_school_dashboard_index_path(district, school, year: academic_year.range)
end
end

@ -1,6 +1,6 @@
require 'rails_helper'
feature 'School dashboard', type: feature do
describe 'District Admin', js: true do
let(:district) { District.find_by_slug 'winchester' }
let(:different_district) { District.find_by_slug 'boston' }
let(:school) { School.find_by_slug 'winchester-high-school' }
@ -60,19 +60,7 @@ feature 'School dashboard', type: feature do
SurveyItemResponse.import survey_item_responses
end
after :each do
DatabaseCleaner.clean
end
scenario 'User authentication fails' do
page.driver.browser.basic_authorize('wrong username', 'wrong password')
visit "/districts/#{district.slug}/schools/#{school.slug}/dashboard?year=2020-21"
expect(page).not_to have_text(school.name)
end
scenario 'District Admin navigates the site', js: true do
it 'navigates through the site' do
page.driver.basic_authorize(username, password)
visit '/welcome'
@ -132,6 +120,16 @@ def go_to_different_district(district)
select district.name, from: 'select-district'
end
def go_to_browse_page_for_school_without_data(school)
click_on 'Browse'
select school.name, from: 'select-school'
end
def go_to_dashboard_page_for_school_without_data(school)
click_on 'Dashboard'
select school.name, from: 'select-school'
end
def district_admin_sees_schools_change
expected_path = "/districts/#{school_in_same_district.district.slug}/schools/#{school_in_same_district.slug}/dashboard?year=2020-21"
expect(page).to have_current_path(expected_path)

@ -0,0 +1,56 @@
require 'rails_helper'
describe 'SQM Application' do
let(:district) { create(:district) }
let(:school) { create(:school, district: district) }
let(:academic_year) { create(:academic_year) }
let(:category) { create(:sqm_category) }
let(:measure) { create(:measure) }
before :each do
driven_by :rack_test
page.driver.browser.basic_authorize(username, password)
end
context 'when no measures meet their threshold' do
it 'shows a modal on all pages' do
[dashboard_path, browse_path].each do |path|
visit path
expect(page).to have_css '.modal'
end
end
end
context 'at least one measure meets its threshold' do
before :each do
teacher_survey_item = create(:teacher_survey_item, measure: measure)
create_list(:survey_item_response, SurveyItemResponse::TEACHER_RESPONSE_THRESHOLD,
survey_item: teacher_survey_item, academic_year: academic_year, school: school)
end
it 'does not show a modal on any page' do
[dashboard_path, browse_path].each do |path|
visit path
expect(page).not_to have_css '.modal'
end
end
end
private
def username
district.name.downcase
end
def password
"#{username}!"
end
def dashboard_path
district_school_dashboard_index_path(district, school, year: academic_year.range)
end
def browse_path
district_school_sqm_category_path(district, school, category, year: academic_year.range)
end
end
Loading…
Cancel
Save