mirror of
https://github.com/edcommonwealth/sqm-dashboards.git
synced 2026-03-07 21:48:16 -08:00
Show partial data indicators on variance chart
This commit is contained in:
parent
8205578267
commit
aeb6a45a45
34 changed files with 176 additions and 6780 deletions
|
|
@ -84,3 +84,11 @@
|
|||
stroke: $gray-2;
|
||||
fill: $gray-3;
|
||||
}
|
||||
|
||||
.limited-availability-indicator {
|
||||
color: white;
|
||||
background-color: black;
|
||||
line-height: 30px;
|
||||
border-radius: 50%;
|
||||
width: 20px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,111 +0,0 @@
|
|||
// animating icons
|
||||
// --------------------------
|
||||
|
||||
.#{$fa-css-prefix}-beat {
|
||||
animation-name: #{$fa-css-prefix}-beat;
|
||||
animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0);
|
||||
animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal);
|
||||
animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s);
|
||||
animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, ease-in-out);
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix}-fade {
|
||||
animation-name: #{$fa-css-prefix}-fade;
|
||||
animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0);
|
||||
animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal);
|
||||
animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s);
|
||||
animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(.4,0,.6,1));
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix}-flash {
|
||||
animation-name: #{$fa-css-prefix}-flash;
|
||||
animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0);
|
||||
animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal);
|
||||
animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s);
|
||||
animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, cubic-bezier(.4,0,.6,1));
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix}-flip {
|
||||
animation-name: #{$fa-css-prefix}-flip;
|
||||
animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0);
|
||||
animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal);
|
||||
animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s);
|
||||
animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, ease-in-out);
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix}-spin {
|
||||
animation-name: #{$fa-css-prefix}-spin;
|
||||
animation-delay: var(--#{$fa-css-prefix}-animation-delay, 0);
|
||||
animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal);
|
||||
animation-duration: var(--#{$fa-css-prefix}-animation-duration, 2s);
|
||||
animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, linear);
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix}-spin-reverse {
|
||||
--#{$fa-css-prefix}-animation-direction: reverse;
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix}-pulse,
|
||||
.#{$fa-css-prefix}-spin-pulse {
|
||||
animation-name: #{$fa-css-prefix}-spin;
|
||||
animation-direction: var(--#{$fa-css-prefix}-animation-direction, normal);
|
||||
animation-duration: var(--#{$fa-css-prefix}-animation-duration, 1s);
|
||||
animation-iteration-count: var(--#{$fa-css-prefix}-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--#{$fa-css-prefix}-animation-timing, steps(8));
|
||||
}
|
||||
|
||||
// if agent or operating system prefers reduced motion, disable animations
|
||||
// see: https://www.smashingmagazine.com/2020/09/design-reduced-motion-sensitivities/
|
||||
// see: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.#{$fa-css-prefix}-beat,
|
||||
.#{$fa-css-prefix}-fade,
|
||||
.#{$fa-css-prefix}-flash,
|
||||
.#{$fa-css-prefix}-flip,
|
||||
.#{$fa-css-prefix}-pulse,
|
||||
.#{$fa-css-prefix}-spin,
|
||||
.#{$fa-css-prefix}-spin-pulse {
|
||||
animation-delay: -1ms;
|
||||
animation-duration: 1ms;
|
||||
animation-iteration-count: 1;
|
||||
transition-delay: 0s;
|
||||
transition-duration: 0s;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes #{$fa-css-prefix}-beat {
|
||||
0%, 90% { transform: scale(1); }
|
||||
45% { transform: scale(var(--#{$fa-css-prefix}-beat-scale, 1.25)); }
|
||||
}
|
||||
|
||||
@keyframes #{$fa-css-prefix}-fade {
|
||||
50% { opacity: var(--#{$fa-css-prefix}-fade-opacity, 0.4); }
|
||||
}
|
||||
|
||||
@keyframes #{$fa-css-prefix}-flash {
|
||||
0%, 100% {
|
||||
opacity: var(--#{$fa-css-prefix}-flash-opacity, 0.4);
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(var(--#{$fa-css-prefix}-flash-scale, 1.125));
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes #{$fa-css-prefix}-flip {
|
||||
50% {
|
||||
transform: rotate3d(var(--#{$fa-css-prefix}-flip-x, 0), var(--#{$fa-css-prefix}-flip-y, 1), var(--#{$fa-css-prefix}-flip-z, 0), var(--#{$fa-css-prefix}-flip-angle, -180deg));
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes #{$fa-css-prefix}-spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
// bordered + pulled icons
|
||||
// -------------------------
|
||||
|
||||
.#{$fa-css-prefix}-border {
|
||||
border-color: var(--#{$fa-css-prefix}-border-color, #{$fa-border-color});
|
||||
border-radius: var(--#{$fa-css-prefix}-border-radius, #{$fa-border-radius});
|
||||
border-style: var(--#{$fa-css-prefix}-border-style, #{$fa-border-style});
|
||||
border-width: var(--#{$fa-css-prefix}-border-width, #{$fa-border-width});
|
||||
padding: var(--#{$fa-css-prefix}-border-padding, #{$fa-border-padding});
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix}-pull-left {
|
||||
float: left;
|
||||
margin-right: var(--#{$fa-css-prefix}-pull-margin, #{$fa-pull-margin});
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix}-pull-right {
|
||||
float: right;
|
||||
margin-left: var(--#{$fa-css-prefix}-pull-margin, #{$fa-pull-margin});
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
// base icon class definition
|
||||
// -------------------------
|
||||
|
||||
.#{$fa-css-prefix} {
|
||||
font-family: var(--#{$fa-css-prefix}-style-family, '#{$fa-style-family}');
|
||||
font-weight: var(--#{$fa-css-prefix}-style, #{$fa-style});
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix},
|
||||
.fas,
|
||||
.fa-solid,
|
||||
.far,
|
||||
.fa-regular,
|
||||
.fal,
|
||||
.fa-light,
|
||||
.fat,
|
||||
.fa-thin,
|
||||
.fad,
|
||||
.fa-duotone,
|
||||
.fab,
|
||||
.fa-brands {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
display: var(--#{$fa-css-prefix}-display, #{$fa-display});
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
line-height: 1;
|
||||
text-rendering: auto;
|
||||
}
|
||||
|
||||
%fa-icon {
|
||||
@include fa-icon;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
// fixed-width icons
|
||||
// -------------------------
|
||||
|
||||
.#{$fa-css-prefix}-fw {
|
||||
text-align: center;
|
||||
width: $fa-fw-width;
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
// functions
|
||||
// --------------------------
|
||||
|
||||
// Originally obtained from the Bootstrap https://github.com/twbs/bootstrap
|
||||
//
|
||||
// Licensed under: The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2011-2021 Twitter, Inc.
|
||||
// Copyright (c) 2011-2021 The Bootstrap Authors
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
@function divide($dividend, $divisor, $precision: 10) {
|
||||
$sign: if($dividend > 0 and $divisor > 0, 1, -1);
|
||||
$dividend: abs($dividend);
|
||||
$divisor: abs($divisor);
|
||||
$quotient: 0;
|
||||
$remainder: $dividend;
|
||||
@if $dividend == 0 {
|
||||
@return 0;
|
||||
}
|
||||
@if $divisor == 0 {
|
||||
@error "Cannot divide by 0";
|
||||
}
|
||||
@if $divisor == 1 {
|
||||
@return $dividend;
|
||||
}
|
||||
@while $remainder >= $divisor {
|
||||
$quotient: $quotient + 1;
|
||||
$remainder: $remainder - $divisor;
|
||||
}
|
||||
@if $remainder > 0 and $precision > 0 {
|
||||
$remainder: divide($remainder * 10, $divisor, $precision - 1) * .1;
|
||||
}
|
||||
@return ($quotient + $remainder) * $sign;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
// specific icon class definition
|
||||
// -------------------------
|
||||
|
||||
/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
|
||||
readers do not read off random characters that represent icons */
|
||||
|
||||
@each $name, $icon in $fa-icons {
|
||||
.#{$fa-css-prefix}-#{$name}::before { content: fa-content($icon); }
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
// icons in a list
|
||||
// -------------------------
|
||||
|
||||
.#{$fa-css-prefix}-ul {
|
||||
list-style-type: none;
|
||||
margin-left: var(--#{$fa-css-prefix}-li-margin, #{$fa-li-margin});
|
||||
padding-left: 0;
|
||||
|
||||
> li { position: relative; }
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix}-li {
|
||||
left: calc(var(--#{$fa-css-prefix}-li-width, #{$fa-li-width}) * -1);
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
width: var(--#{$fa-css-prefix}-li-width, #{$fa-li-width});
|
||||
line-height: inherit;
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
// mixins
|
||||
// --------------------------
|
||||
|
||||
// base rendering for an icon
|
||||
@mixin fa-icon {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
display: inline-block;
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
font-weight: normal;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
// sets relative font-sizing and alignment (in _sizing)
|
||||
@mixin fa-size ($font-size) {
|
||||
font-size: (divide($font-size, $fa-size-scale-base)) * 1em; // converts step in sizing scale into an em-based value that's relative to the scale's base
|
||||
line-height: (divide(1, $font-size)) * 1em; // sets the line-height of the icon back to that of it's parent
|
||||
vertical-align: ((divide(6, $font-size)) - (divide(3, 8))) * 1em; // vertically centers the icon taking into account the surrounding text's descender
|
||||
}
|
||||
|
||||
// only display content to screen readers
|
||||
// see: https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/
|
||||
// see: https://hugogiraudel.com/2016/10/13/css-hide-and-seek/
|
||||
@mixin fa-sr-only() {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
// use in conjunction with .sr-only to only display content when it's focused
|
||||
@mixin fa-sr-only-focusable() {
|
||||
&:not(:focus) {
|
||||
@include fa-sr-only();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
// rotating + flipping icons
|
||||
// -------------------------
|
||||
|
||||
.#{$fa-css-prefix}-rotate-90 {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix}-rotate-180 {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix}-rotate-270 {
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix}-flip-horizontal {
|
||||
transform: scale(-1, 1);
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix}-flip-vertical {
|
||||
transform: scale(1, -1);
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix}-flip-both,
|
||||
.#{$fa-css-prefix}-flip-horizontal.#{$fa-css-prefix}-flip-vertical {
|
||||
transform: scale(-1, -1);
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix}-rotate-by {
|
||||
transform: rotate(var(--#{$fa-css-prefix}-rotate-angle, none));
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
// screen-reader utilities
|
||||
// -------------------------
|
||||
|
||||
// only display content to screen readers
|
||||
.sr-only,
|
||||
.fa-sr-only {
|
||||
@include fa-sr-only;
|
||||
}
|
||||
|
||||
// use in conjunction with .sr-only to only display content when it's focused
|
||||
.sr-only-focusable,
|
||||
.fa-sr-only-focusable {
|
||||
@include fa-sr-only-focusable;
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,16 +0,0 @@
|
|||
// sizing icons
|
||||
// -------------------------
|
||||
|
||||
// literal magnification scale
|
||||
@for $i from 1 through 10 {
|
||||
.#{$fa-css-prefix}-#{$i}x {
|
||||
font-size: $i * 1em;
|
||||
}
|
||||
}
|
||||
|
||||
// step-based scale (with alignment)
|
||||
@each $size, $value in $fa-sizes {
|
||||
.#{$fa-css-prefix}-#{$size} {
|
||||
@include fa-size($value);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
// stacking icons
|
||||
// -------------------------
|
||||
|
||||
.#{$fa-css-prefix}-stack {
|
||||
display: inline-block;
|
||||
height: 2em;
|
||||
line-height: 2em;
|
||||
position: relative;
|
||||
vertical-align: $fa-stack-vertical-align;
|
||||
width: $fa-stack-width;
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix}-stack-1x,
|
||||
.#{$fa-css-prefix}-stack-2x {
|
||||
left: 0;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
z-index: var(--#{$fa-css-prefix}-stack-z-index, #{$fa-stack-z-index});
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix}-stack-1x {
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix}-stack-2x {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.#{$fa-css-prefix}-inverse {
|
||||
color: var(--#{$fa-css-prefix}-inverse, #{$fa-inverse});
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,25 +0,0 @@
|
|||
/*!
|
||||
* Font Awesome Free 6.0.0-beta2 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
@import 'functions';
|
||||
@import 'variables';
|
||||
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 6 Brands';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: $fa-font-display;
|
||||
src: url('#{$fa-font-path}/fa-brands-400.woff2') format('woff2'),
|
||||
url('#{$fa-font-path}/fa-brands-400.ttf') format('truetype');
|
||||
}
|
||||
|
||||
.fab,
|
||||
.fa-brands {
|
||||
font-family: 'Font Awesome 6 Brands';
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@each $name, $icon in $fa-brand-icons {
|
||||
.#{$fa-css-prefix}-#{$name}:before { content: fa-content($icon); }
|
||||
}
|
||||
20
app/assets/stylesheets/scss/fontawesome.scss
vendored
20
app/assets/stylesheets/scss/fontawesome.scss
vendored
|
|
@ -1,20 +0,0 @@
|
|||
/*!
|
||||
* Font Awesome Free 6.0.0-beta2 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
// Font Awesome core compile (Web Fonts-based)
|
||||
// -------------------------
|
||||
|
||||
@import 'functions';
|
||||
@import 'variables';
|
||||
@import 'mixins';
|
||||
@import 'core';
|
||||
@import 'sizing';
|
||||
@import 'fixed-width';
|
||||
@import 'list';
|
||||
@import 'bordered-pulled';
|
||||
@import 'animated';
|
||||
@import 'rotated-flipped';
|
||||
@import 'stacked';
|
||||
@import 'icons';
|
||||
@import 'screen-reader';
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
/*!
|
||||
* Font Awesome Free 6.0.0-beta2 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
@import 'functions';
|
||||
@import 'variables';
|
||||
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 6 Free';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: $fa-font-display;
|
||||
src: url('#{$fa-font-path}/fa-regular-400.woff2') format('woff2'),
|
||||
url('#{$fa-font-path}/fa-regular-400.ttf') format('truetype');
|
||||
}
|
||||
|
||||
.far,
|
||||
.fa-regular {
|
||||
font-family: 'Font Awesome 6 Free';
|
||||
font-weight: 400;
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
/*!
|
||||
* Font Awesome Free 6.0.0-beta2 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
@import 'functions';
|
||||
@import 'variables';
|
||||
|
||||
@font-face {
|
||||
font-family: 'Font Awesome 6 Free';
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: $fa-font-display;
|
||||
src: url('#{$fa-font-path}/fa-solid-900.woff2') format('woff2'),
|
||||
url('#{$fa-font-path}/fa-solid-900.ttf') format('truetype');
|
||||
}
|
||||
|
||||
.fas,
|
||||
.fa-solid {
|
||||
font-family: 'Font Awesome 6 Free';
|
||||
font-weight: 900;
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
/*!
|
||||
* Font Awesome Free 6.0.0-beta2 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
// V4 shims compile (Web Fonts-based)
|
||||
// -------------------------
|
||||
|
||||
@import 'functions';
|
||||
@import 'variables';
|
||||
@import 'shims';
|
||||
|
|
@ -6,8 +6,8 @@
|
|||
@import "dashboard";
|
||||
@import "navigation";
|
||||
@import "buttons";
|
||||
@import "scss/fontawesome.scss";
|
||||
@import "scss/solid.scss";
|
||||
@import "@fortawesome/fontawesome-free/scss/fontawesome";
|
||||
@import "@fortawesome/fontawesome-free/scss/solid";
|
||||
@import "footer";
|
||||
@import "forms";
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ class DashboardController < SqmApplicationController
|
|||
private
|
||||
|
||||
def presenter_for_measure(measure)
|
||||
score = SurveyItemResponse.score_for_measure(measure: measure, school: @school, academic_year: @academic_year).average
|
||||
score = SurveyItemResponse.score_for_measure(measure: measure, school: @school, academic_year: @academic_year)
|
||||
|
||||
VarianceChartRowPresenter.new(measure: measure, score: score)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -42,4 +42,13 @@ module VarianceHelper
|
|||
def zone_width_percentage
|
||||
100.0/zones.size
|
||||
end
|
||||
|
||||
def availability_indicator_percentage
|
||||
3
|
||||
end
|
||||
|
||||
def partial_data_indicator_size
|
||||
16
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@ class Measure < ActiveRecord::Base
|
|||
|
||||
has_many :survey_item_responses, through: :survey_items
|
||||
|
||||
scope :source_includes_survey_items, ->() { joins(:survey_items).uniq }
|
||||
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) }
|
||||
none? do |measure|
|
||||
SurveyItemResponse.sufficient_data?(measure: measure, school: school, academic_year: academic_year)
|
||||
end
|
||||
end
|
||||
|
||||
def teacher_survey_items
|
||||
|
|
@ -27,4 +29,15 @@ class Measure < ActiveRecord::Base
|
|||
student_survey_items.any?
|
||||
end
|
||||
|
||||
def includes_admin_data_items?
|
||||
admin_data_items.any?
|
||||
end
|
||||
|
||||
def sources
|
||||
sources = []
|
||||
sources << :admin_data if includes_admin_data_items?
|
||||
sources << :student_surveys if includes_student_survey_items?
|
||||
sources << :teacher_surveys if includes_teacher_survey_items?
|
||||
sources
|
||||
end
|
||||
end
|
||||
|
|
|
|||
2
app/models/score.rb
Normal file
2
app/models/score.rb
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
class Score < Struct.new(:average, :meets_teacher_threshold?, :meets_student_threshold?)
|
||||
end
|
||||
|
|
@ -14,8 +14,6 @@ class SurveyItemResponse < ActiveRecord::Base
|
|||
.average(:likert_score)
|
||||
end
|
||||
|
||||
Score = Struct.new(:average, :meets_teacher_threshold?, :meets_student_threshold?)
|
||||
|
||||
def self.score_for_measure(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
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ class VarianceChartRowPresenter
|
|||
|
||||
def initialize(measure:, score:)
|
||||
@measure = measure
|
||||
@score = score
|
||||
@score = score.average
|
||||
@meets_teacher_threshold = score.meets_teacher_threshold?
|
||||
@meets_student_threshold = score.meets_student_threshold?
|
||||
end
|
||||
|
||||
def sufficient_data?
|
||||
|
|
@ -31,7 +33,7 @@ class VarianceChartRowPresenter
|
|||
def x_offset
|
||||
case zone.type
|
||||
when :ideal, :approval
|
||||
"60%"
|
||||
'60%'
|
||||
else
|
||||
"#{((0.6 - bar_width_percentage) * 100).abs.round(2)}%"
|
||||
end
|
||||
|
|
@ -48,8 +50,20 @@ class VarianceChartRowPresenter
|
|||
end
|
||||
end
|
||||
|
||||
def <=>(other_presenter)
|
||||
other_presenter.order <=> order
|
||||
def <=>(other)
|
||||
other.order <=> order
|
||||
end
|
||||
|
||||
def show_partial_data_indicator?
|
||||
partial_data_sources.present?
|
||||
end
|
||||
|
||||
def partial_data_sources
|
||||
Array.new.tap do |sources|
|
||||
sources << 'teacher survey results' if @measure.includes_teacher_survey_items? && !@meets_teacher_threshold
|
||||
sources << 'student survey results' if @measure.includes_student_survey_items? && !@meets_student_threshold
|
||||
sources << 'administrative data' if @measure.includes_admin_data_items?
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
|
@ -86,7 +100,7 @@ class VarianceChartRowPresenter
|
|||
watch_low_benchmark: @measure.watch_low_benchmark,
|
||||
growth_low_benchmark: @measure.growth_low_benchmark,
|
||||
approval_low_benchmark: @measure.approval_low_benchmark,
|
||||
ideal_low_benchmark: @measure.ideal_low_benchmark,
|
||||
ideal_low_benchmark: @measure.ideal_low_benchmark
|
||||
)
|
||||
scale.zone_for_score(@score)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
filter="url(#inset-shadow)"
|
||||
>
|
||||
<g id="zone-headings">
|
||||
<%= zones.each_with_index do |zone, index| %>
|
||||
<% zones.each_with_index do |zone, index| %>
|
||||
<text
|
||||
class="zone-header"
|
||||
x="<%= index * zone_width_percentage + zone_width_percentage / 2.0 %>%"
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
</g>
|
||||
|
||||
<g id="zone-background" transform="translate(0, <%= heading_gutter %>)">
|
||||
<%= zones.each_with_index do |zone, index| %>
|
||||
<% zones.each_with_index do |zone, index| %>
|
||||
<rect
|
||||
id="<%= zone %>-zone"
|
||||
class="zone-background bg-fill-<%= zone %>"
|
||||
|
|
@ -61,14 +61,26 @@
|
|||
</svg>
|
||||
|
||||
<g id="measure-rows">
|
||||
<svg id=measure-row-limited-availability-indicator x="0" y="<%= heading_gutter %>">
|
||||
<% displayed_presenters.each_with_index do |presenter, index| %>
|
||||
<% if presenter.show_partial_data_indicator? %>
|
||||
<use xlink:href="/fontawesome-free/sprites/solid.svg#circle-exclamation" width="<%= partial_data_indicator_size %>"
|
||||
height="<%= partial_data_indicator_size %>"
|
||||
data-bs-toggle="popover" data-bs-placement="right"
|
||||
data-bs-content="The following sources are not included in this measure due to insufficient data: <%= presenter.partial_data_sources.join(' and ') %>."
|
||||
x="<%= partial_data_indicator_size / 2 %>"
|
||||
y="<%= index * measure_row_height + measure_row_height / 2 - partial_data_indicator_size / 2 %>">
|
||||
</use>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</svg>
|
||||
|
||||
<svg id="measure-row-labels" x="0" y=<%= heading_gutter %>>
|
||||
<%= displayed_presenters.each_with_index do |presenter, index| %>
|
||||
<% displayed_presenters.each_with_index do |presenter, index| %>
|
||||
<text
|
||||
class="font-cabin"
|
||||
x="<%= label_width_percentage %>%"
|
||||
dx="<%= -1 * label_padding_right %>"
|
||||
x="<%= availability_indicator_percentage %>%"
|
||||
y="<%= index * measure_row_height + measure_row_height / 2 %>"
|
||||
text-anchor="end"
|
||||
dominant-baseline="middle"
|
||||
data-variance-row-label
|
||||
>
|
||||
|
|
@ -83,7 +95,7 @@
|
|||
y="<%= heading_gutter %>"
|
||||
width="<%= graph_width_percentage %>%"
|
||||
>
|
||||
<%= displayed_presenters.each_with_index do |presenter, index| %>
|
||||
<% displayed_presenters.each_with_index do |presenter, index| %>
|
||||
<rect
|
||||
class="measure-row-bar <%= presenter.bar_color %>"
|
||||
x="<%= presenter.x_offset %>"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
"private": "true",
|
||||
"dependencies": {
|
||||
"@babel/preset-env": "^7.15.8",
|
||||
"@fortawesome/fontawesome-free": "^6.0.0-beta3",
|
||||
"@popperjs/core": "^2.10.2",
|
||||
"@rails/actioncable": "^6.0.0",
|
||||
"@rails/activestorage": "^6.0.0",
|
||||
|
|
|
|||
1
public/fontawesome-free
Symbolic link
1
public/fontawesome-free
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../node_modules/@fortawesome/fontawesome-free/
|
||||
|
|
@ -8,7 +8,8 @@ describe VarianceChartRowPresenter do
|
|||
let(:ideal_low_benchmark) { 3.8 }
|
||||
|
||||
let(:measure) {
|
||||
Measure.new(
|
||||
create(
|
||||
:measure,
|
||||
name: 'Some Title',
|
||||
watch_low_benchmark: watch_low_benchmark,
|
||||
growth_low_benchmark: growth_low_benchmark,
|
||||
|
|
@ -27,8 +28,8 @@ describe VarianceChartRowPresenter do
|
|||
end
|
||||
end
|
||||
|
||||
context('when the score is in the Ideal zone') do
|
||||
let(:score) { 4.4 }
|
||||
context 'when the score is in the Ideal zone' do
|
||||
let(:score) { Score.new(4.4, true, true) }
|
||||
|
||||
it_behaves_like 'measure_name'
|
||||
|
||||
|
|
@ -45,8 +46,8 @@ describe VarianceChartRowPresenter do
|
|||
end
|
||||
end
|
||||
|
||||
context('when the score is in the Approval zone') do
|
||||
let(:score) { 3.7 }
|
||||
context 'when the score is in the Approval zone' do
|
||||
let(:score) { Score.new(3.7, true, true) }
|
||||
|
||||
it_behaves_like 'measure_name'
|
||||
|
||||
|
|
@ -63,8 +64,8 @@ describe VarianceChartRowPresenter do
|
|||
end
|
||||
end
|
||||
|
||||
context('when the score is in the Growth zone') do
|
||||
let(:score) { 3.2 }
|
||||
context 'when the score is in the Growth zone' do
|
||||
let(:score) { Score.new(3.2, true, true) }
|
||||
|
||||
it_behaves_like 'measure_name'
|
||||
|
||||
|
|
@ -83,8 +84,8 @@ describe VarianceChartRowPresenter do
|
|||
end
|
||||
end
|
||||
|
||||
context('when the score is in the Watch zone') do
|
||||
let(:score) { 2.9 }
|
||||
context 'when the score is in the Watch zone' do
|
||||
let(:score) { Score.new(2.9, true, true) }
|
||||
|
||||
it_behaves_like 'measure_name'
|
||||
|
||||
|
|
@ -103,8 +104,8 @@ describe VarianceChartRowPresenter do
|
|||
end
|
||||
end
|
||||
|
||||
context('when the score is in the Warning zone') do
|
||||
let(:score) { 1.0 }
|
||||
context 'when the score is in the Warning zone' do
|
||||
let(:score) { Score.new(1.0, true, true) }
|
||||
|
||||
it_behaves_like 'measure_name'
|
||||
|
||||
|
|
@ -123,17 +124,88 @@ describe VarianceChartRowPresenter do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when a measure does not contain admin data items' do
|
||||
let(:score) { Score.new(nil, false, false) }
|
||||
it 'it does not show a partial data indicator' do
|
||||
expect(presenter.show_partial_data_indicator?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a measure contains admin data items' do
|
||||
before :each do
|
||||
create :admin_data_item, measure: measure
|
||||
end
|
||||
let(:score) { Score.new(nil, false, false) }
|
||||
|
||||
it 'shows a partial data indicator' do
|
||||
expect(presenter.show_partial_data_indicator?).to be true
|
||||
expect(presenter.partial_data_sources).to eq ['administrative data']
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a measure contains teacher survey items' do
|
||||
before :each do
|
||||
create :teacher_survey_item, measure: measure
|
||||
end
|
||||
|
||||
context 'when there are insufficient teacher survey item responses' do
|
||||
let(:score) { Score.new(nil, false, true) }
|
||||
it 'shows a partial data indicator' do
|
||||
expect(presenter.show_partial_data_indicator?).to be true
|
||||
expect(presenter.partial_data_sources).to eq ['teacher survey results']
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are sufficient teacher survey item responses' do
|
||||
let(:score) { Score.new(nil, true, true) }
|
||||
it 'does not show a partial data indicator' do
|
||||
expect(presenter.show_partial_data_indicator?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a measure contains student survey items' do
|
||||
before :each do
|
||||
create :student_survey_item, measure: measure
|
||||
end
|
||||
|
||||
context 'when there are insufficient student survey item responses' do
|
||||
let(:score) { Score.new(nil, true, false) }
|
||||
it 'shows a partial data indicator' do
|
||||
expect(presenter.show_partial_data_indicator?).to be true
|
||||
expect(presenter.partial_data_sources).to eq ['student survey results']
|
||||
end
|
||||
|
||||
context 'where there are also admin data items' do
|
||||
before :each do
|
||||
create :admin_data_item, measure: measure
|
||||
end
|
||||
|
||||
it 'returns the sources for partial results of administrative data and student survey results' do
|
||||
expect(presenter.partial_data_sources).to eq ['student survey results', 'administrative data']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'When there are sufficient student survey item responses' do
|
||||
let(:score) { Score.new(nil, true, true) }
|
||||
it 'does not show a partial data indicator' do
|
||||
expect(presenter.show_partial_data_indicator?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'sorting scores' do
|
||||
it 'selects a longer bar before a shorter bar for measures in the approval/ideal zones' do
|
||||
approval_presenter = VarianceChartRowPresenter.new measure: measure, score: 3.7
|
||||
ideal_presenter = VarianceChartRowPresenter.new measure: measure, score: 4.4
|
||||
approval_presenter = VarianceChartRowPresenter.new measure: measure, score: Score.new(3.7, true, true)
|
||||
ideal_presenter = VarianceChartRowPresenter.new measure: measure, score: Score.new(4.4, true, true)
|
||||
expect(ideal_presenter <=> approval_presenter).to be < 0
|
||||
expect([approval_presenter, ideal_presenter].sort).to eq [ideal_presenter, approval_presenter]
|
||||
end
|
||||
|
||||
it 'selects a warning bar below a ideal bar' do
|
||||
warning_presenter = VarianceChartRowPresenter.new measure: measure, score: 1.0
|
||||
ideal_presenter = VarianceChartRowPresenter.new measure: measure, score: 5.0
|
||||
warning_presenter = VarianceChartRowPresenter.new measure: measure, score: Score.new(1.0, true, true)
|
||||
ideal_presenter = VarianceChartRowPresenter.new measure: measure, score: Score.new(5.0, true, true)
|
||||
expect(warning_presenter <=> ideal_presenter).to be > 0
|
||||
expect([warning_presenter, ideal_presenter].sort).to eq [ideal_presenter, warning_presenter]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ describe 'dashboard/index.html.erb' do
|
|||
context 'when some presenters have a nil score' do
|
||||
let(:variance_chart_row_presenters) {
|
||||
[
|
||||
VarianceChartRowPresenter.new(measure: support_for_teaching, score: nil),
|
||||
VarianceChartRowPresenter.new(measure: create(:measure, name: 'Should Be Displayed', measure_id: 'should-be-displayed'), score: rand),
|
||||
VarianceChartRowPresenter.new(measure: effective_leadership, score: nil),
|
||||
VarianceChartRowPresenter.new(measure: professional_qualifications, score: nil)
|
||||
VarianceChartRowPresenter.new(measure: support_for_teaching, score: Score.new),
|
||||
VarianceChartRowPresenter.new(measure: create(:measure, name: 'Should Be Displayed', measure_id: 'should-be-displayed'), score: Score.new(rand)),
|
||||
VarianceChartRowPresenter.new(measure: effective_leadership, score: Score.new),
|
||||
VarianceChartRowPresenter.new(measure: professional_qualifications, score: Score.new)
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ describe 'dashboard/index.html.erb' do
|
|||
context 'when all the presenters have a non-nil score' do
|
||||
let(:variance_chart_row_presenters) {
|
||||
[
|
||||
VarianceChartRowPresenter.new(measure: create(:measure, name: 'Display Me', measure_id: 'display-me'), score: rand)
|
||||
VarianceChartRowPresenter.new(measure: create(:measure, name: 'Display Me', measure_id: 'display-me'), score: Score.new(rand))
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ describe 'dashboard/_variance_chart.html.erb' do
|
|||
|
||||
before :each do
|
||||
presenters = [
|
||||
VarianceChartRowPresenter.new(measure: lower_scoring_measure, score: 1),
|
||||
VarianceChartRowPresenter.new(measure: higher_scoring_measure, score: 5)
|
||||
VarianceChartRowPresenter.new(measure: lower_scoring_measure, score: Score.new(1)),
|
||||
VarianceChartRowPresenter.new(measure: higher_scoring_measure, score: Score.new(5))
|
||||
]
|
||||
|
||||
render partial: 'variance_chart', locals: { presenters: presenters }
|
||||
|
|
|
|||
|
|
@ -894,6 +894,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||
|
||||
"@fortawesome/fontawesome-free@^6.0.0-beta3":
|
||||
version "6.0.0-beta3"
|
||||
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-6.0.0-beta3.tgz#120e4a158a0de983924ce151bc35f27de46398b7"
|
||||
integrity sha512-4SqOuhC8tSLeQvbW1nDmq6T7+8vdSgHy/w7PRwCFzMQCbKuYFIir/3UuWsV1QblX1lt7SGlSgwbaCv9XhRt8HA==
|
||||
|
||||
"@istanbuljs/load-nyc-config@^1.0.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue