ColorContrastAccessibility.js (5475B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const { 8 Component, 9 createFactory, 10 } = require("resource://devtools/client/shared/vendor/react.mjs"); 11 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs"); 12 const { 13 div, 14 span, 15 h3, 16 } = require("resource://devtools/client/shared/vendor/react-dom-factories.js"); 17 const LearnMoreLink = createFactory( 18 require("resource://devtools/client/accessibility/components/LearnMoreLink.js") 19 ); 20 21 const { 22 A11Y_CONTRAST_LEARN_MORE_LINK, 23 } = require("resource://devtools/client/accessibility/constants.js"); 24 const { 25 L10N, 26 } = require("resource://devtools/client/accessibility/utils/l10n.js"); 27 28 /** 29 * Component that renders a colour contrast value along with a swatch preview of what the 30 * text and background colours are. 31 */ 32 class ContrastValueClass extends Component { 33 static get propTypes() { 34 return { 35 backgroundColor: PropTypes.array.isRequired, 36 color: PropTypes.array.isRequired, 37 value: PropTypes.number.isRequired, 38 score: PropTypes.string, 39 }; 40 } 41 42 render() { 43 const { backgroundColor, color, value, score } = this.props; 44 45 const className = ["accessibility-contrast-value", score].join(" "); 46 47 return span( 48 { 49 className, 50 role: "presentation", 51 style: { 52 "--accessibility-contrast-color": `rgba(${color})`, 53 "--accessibility-contrast-bg": `rgba(${backgroundColor})`, 54 }, 55 }, 56 value.toFixed(2) 57 ); 58 } 59 } 60 61 const ContrastValue = createFactory(ContrastValueClass); 62 63 /** 64 * Component that renders labeled colour contrast values together with the large text 65 * indiscator. 66 */ 67 class ColorContrastAccessibilityClass extends Component { 68 static get propTypes() { 69 return { 70 error: PropTypes.string, 71 isLargeText: PropTypes.bool.isRequired, 72 color: PropTypes.array.isRequired, 73 value: PropTypes.number, 74 min: PropTypes.number, 75 max: PropTypes.number, 76 backgroundColor: PropTypes.array, 77 backgroundColorMin: PropTypes.array, 78 backgroundColorMax: PropTypes.array, 79 score: PropTypes.string, 80 scoreMin: PropTypes.string, 81 scoreMax: PropTypes.string, 82 }; 83 } 84 85 render() { 86 const { 87 error, 88 isLargeText, 89 color, 90 value, 91 backgroundColor, 92 score, 93 min, 94 backgroundColorMin, 95 scoreMin, 96 max, 97 backgroundColorMax, 98 scoreMax, 99 } = this.props; 100 101 const children = []; 102 103 if (error) { 104 children.push( 105 span( 106 { 107 className: "accessibility-color-contrast-error", 108 role: "presentation", 109 }, 110 L10N.getStr("accessibility.contrast.error") 111 ) 112 ); 113 114 return div( 115 { 116 role: "presentation", 117 tabIndex: "-1", 118 className: "accessibility-color-contrast", 119 }, 120 ...children 121 ); 122 } 123 124 if (value) { 125 children.push(ContrastValue({ score, color, backgroundColor, value })); 126 } else { 127 children.push( 128 ContrastValue({ 129 score: scoreMin, 130 color, 131 backgroundColor: backgroundColorMin, 132 value: min, 133 }), 134 div({ 135 role: "presentation", 136 tabIndex: "-1", 137 className: "accessibility-color-contrast-separator", 138 }), 139 ContrastValue({ 140 score: scoreMax, 141 color, 142 backgroundColor: backgroundColorMax, 143 value: max, 144 }) 145 ); 146 } 147 148 if (isLargeText) { 149 children.push( 150 span( 151 { 152 className: "accessibility-color-contrast-large-text", 153 role: "presentation", 154 title: L10N.getStr("accessibility.contrast.large.title"), 155 }, 156 L10N.getStr("accessibility.contrast.large.text") 157 ) 158 ); 159 } 160 161 return div( 162 { 163 role: "presentation", 164 tabIndex: "-1", 165 className: "accessibility-color-contrast", 166 }, 167 ...children 168 ); 169 } 170 } 171 172 const ColorContrastAccessibility = createFactory( 173 ColorContrastAccessibilityClass 174 ); 175 176 class ContrastAnnotationClass extends Component { 177 static get propTypes() { 178 return { 179 score: PropTypes.string, 180 }; 181 } 182 183 render() { 184 const { score } = this.props; 185 186 return LearnMoreLink({ 187 className: "accessibility-check-annotation", 188 href: A11Y_CONTRAST_LEARN_MORE_LINK, 189 learnMoreStringKey: "accessibility.learnMore", 190 l10n: L10N, 191 messageStringKey: `accessibility.contrast.annotation.${score}`, 192 }); 193 } 194 } 195 196 const ContrastAnnotation = createFactory(ContrastAnnotationClass); 197 198 class ColorContrastCheck extends Component { 199 static get propTypes() { 200 return { 201 error: PropTypes.string.isRequired, 202 }; 203 } 204 205 render() { 206 const { error } = this.props; 207 208 return div( 209 { 210 role: "presentation", 211 tabIndex: "-1", 212 className: "accessibility-check", 213 }, 214 h3( 215 { 216 className: "accessibility-check-header", 217 }, 218 L10N.getStr("accessibility.contrast.header") 219 ), 220 ColorContrastAccessibility(this.props), 221 !error && ContrastAnnotation(this.props) 222 ); 223 } 224 } 225 226 module.exports = { 227 ColorContrastAccessibility: ColorContrastAccessibilityClass, 228 ColorContrastCheck, 229 };