From 4d95fa28db58c29958bc0c83d021eab68ec0490d Mon Sep 17 00:00:00 2001 From: Luis Oliveira Date: Mon, 29 Nov 2021 23:34:05 -0300 Subject: [PATCH] Apply darker course colors for high contrast UI fixes: LS-2854 flag=high_contrast_course_colors test plan: - Create a user - Enable High Contrast UI on their account. - Navigate to calendar, course list and dashboard. - Create a course for you, and right after, visit the calendar, so the calendar flow defines a color for the course - Create another course and got straight to the dashboard, so the dashboard flow defines a color for that course - Define a custom hash for a third course - Check the colors on the course list, calendar and dashboard - Enable the High Contrast Course Colors - Check that all the colors now have a contrast ratio of at least 4.5:1 Change-Id: Idd5d4475acceb4742a29938237b88b0d403de611 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/279764 Tested-by: Service Cloud Jenkins Product-Review: Luis Oliveira Reviewed-by: Robin Kuss QA-Review: Robin Kuss --- Gemfile.d/app.rb | 1 + app/models/user.rb | 16 ++++++++++++++++ config/feature_flags/00_standard.yml | 8 ++++++++ spec/models/user_spec.rb | 27 +++++++++++++++++++++++++++ ui/features/calendar/jquery/index.js | 5 +---- 5 files changed, 53 insertions(+), 4 deletions(-) diff --git a/Gemfile.d/app.rb b/Gemfile.d/app.rb index 7bff3236b79..576d1cbf391 100644 --- a/Gemfile.d/app.rb +++ b/Gemfile.d/app.rb @@ -139,6 +139,7 @@ gem "simple_oauth", "0.3.1", require: false gem "twilio-ruby", "5.36.0", require: false gem "vault", "0.15.0", require: false gem "vericite_api", "1.5.3" +gem "wcag_color_contrast", "0.1.0" gem "week_of_month", "1.2.5", github: "instructure/week-of-month", ref: "b3013639e9474f302b5a6f27e4e45313e8d24902" gem "will_paginate", "3.3.0", require: false # required for folio-pagination diff --git a/app/models/user.rb b/app/models/user.rb index 5e1f3395136..bd19a391d9e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1602,6 +1602,15 @@ class User < ActiveRecord::Base get_preference(:new_user_tutorial_statuses) || {} end + def apply_contrast(colors) + colors.each do |key, _v| + until WCAGColorContrast.ratio(colors[key].delete("#"), "ffffff") >= 4.5 + rgb = colors[key].match(/^#(..)(..)(..)$/).captures.map { |c| (c.hex.to_i * 0.85).round } + colors[key] = "#%02x%02x%02x" % rgb + end + end + end + def custom_colors colors_hash = get_preference(:custom_colors) || {} if Shard.current != shard @@ -1615,6 +1624,9 @@ class User < ActiveRecord::Base ["#{opts.join("_")}_#{new_id}", value] end.to_h end + + return apply_contrast colors_hash if prefers_high_contrast? && uses_high_contrast_course_colors? + colors_hash end @@ -1716,6 +1728,10 @@ class User < ActiveRecord::Base !!feature_enabled?(:high_contrast) end + def uses_high_contrast_course_colors? + Account.site_admin.feature_enabled?(:high_contrast_course_colors) + end + def auto_show_cc? !!feature_enabled?(:auto_show_cc) end diff --git a/config/feature_flags/00_standard.yml b/config/feature_flags/00_standard.yml index ec9ed473948..eaf5dd8d6fb 100644 --- a/config/feature_flags/00_standard.yml +++ b/config/feature_flags/00_standard.yml @@ -177,6 +177,14 @@ high_contrast: High Contrast enhances the color contrast of the UI (text, buttons, etc.), making those items more distinct and easier to identify. Note: Institution branding will be disabled. applies_to: User +high_contrast_course_colors: + state: allowed + display_name: High Contrast Course Colors + description: + high_contrast_course_colors_description: |- + High Contrast Course Colors darkens system provided and color picker course colors + (not manually defined ones) to improve contrast ratio + applies_to: SiteAdmin important_dates: state: hidden display_name: Important Dates diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index e4be4ba2e32..8e06717a8ca 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -3606,6 +3606,33 @@ describe User do end end + describe "#custom_colors" do + context "user has high_contrast and high_contrast_course_colors enables" do + let(:user) { user_model } + + before do + user.enable_feature!(:high_contrast) + Account.site_admin.enable_feature!(:high_contrast_course_colors) + end + + it "sufficiently darkens colors with a contrast below 4.5" do + user.preferences[:custom_colors] = { + user_1: "#5a92de", + course_1: "#199eb7", + course_2: "#ffffff", + course_3: "#c8c8c8", + course_4: "#767777" + } + expect(user.custom_colors.map { |_k, v| WCAGColorContrast.ratio(v.delete("#"), "ffffff") }).to all(be >= 4.5) + end + + it "leaves colors with enough contrast alone" do + user.preferences[:custom_colors] = { user_1: "#757777" } + expect(user.custom_colors[:user_1]).to be("#757777") + end + end + end + describe "#prefers_no_celebrations?" do let(:user) { user_model } diff --git a/ui/features/calendar/jquery/index.js b/ui/features/calendar/jquery/index.js index a9c54c8f98e..c4587fbbb3f 100644 --- a/ui/features/calendar/jquery/index.js +++ b/ui/features/calendar/jquery/index.js @@ -1024,10 +1024,7 @@ export default class Calendar { // Get any custom colors that have been set $.getJSON(`/api/v1/users/${this.options.userId}/colors/`, data => { const customColors = data.custom_colors - const colors = colorSlicer.getColors(this.contextCodes.length, 275, { - unsafe: !ENV.use_high_contrast - }) - + const colors = colorSlicer.getColors(this.contextCodes.length, 275) const newCustomColors = {} const html = this.contextCodes .map((contextCode, index) => {