diff --git a/devtools/client/shared/autocomplete-popup.js b/devtools/client/shared/autocomplete-popup.js index bed81a9eb514..69432fd0315e 100644 --- a/devtools/client/shared/autocomplete-popup.js +++ b/devtools/client/shared/autocomplete-popup.js @@ -10,6 +10,7 @@ const Services = require("Services"); const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip"); const EventEmitter = require("devtools/shared/event-emitter"); const {PrefObserver} = require("devtools/client/shared/prefs"); +const {colorUtils} = require("devtools/shared/css/color"); let itemIdCounter = 0; /** @@ -461,8 +462,15 @@ AutocompletePopup.prototype = { if (item.postLabel) { let postDesc = this._document.createElementNS(HTML_NS, "span"); - postDesc.textContent = item.postLabel; postDesc.className = "autocomplete-postlabel"; + postDesc.textContent = item.postLabel; + // Determines if the postlabel is a valid colour or other value + if (this._isValidColor(item.postLabel)) { + let colorSwatch = this._document.createElementNS(HTML_NS, "span"); + colorSwatch.className = "autocomplete-swatch autocomplete-colorswatch"; + colorSwatch.style.cssText = "background-color: " + item.postLabel; + postDesc.insertBefore(colorSwatch, postDesc.childNodes[0]); + } listItem.appendChild(postDesc); } @@ -603,6 +611,18 @@ AutocompletePopup.prototype = { this._currentTheme = newValue; }, + /** + * Determines if the specified colour object is a valid colour, and if + * it is not a "special value" + * + * @return {Boolean} + * If the object represents a proper colour or not. + */ + _isValidColor: function(color) { + let colorObj = new colorUtils.CssColor(color); + return (colorObj.valid && (!colorObj.specialValue)); + }, + /** * Used by tests. */ diff --git a/devtools/client/shared/test/browser_inplace-editor_autocomplete_css_variable.js b/devtools/client/shared/test/browser_inplace-editor_autocomplete_css_variable.js index 0885438efeaa..87fa9c1e36a9 100644 --- a/devtools/client/shared/test/browser_inplace-editor_autocomplete_css_variable.js +++ b/devtools/client/shared/test/browser_inplace-editor_autocomplete_css_variable.js @@ -15,6 +15,8 @@ loadHelperScript("helper_inplace_editor.js"); // Also using a mocked list of CSS properties to avoid autocompletion when // typing in "var" +// Used for representing the expectation of a visible color swatch +const COLORSWATCH = true; // format : // [ // what key to press, @@ -22,28 +24,40 @@ loadHelperScript("helper_inplace_editor.js"); // selected suggestion index (-1 if popup is hidden), // number of suggestions in the popup (0 if popup is hidden), // expected post label corresponding with the input box value, +// boolean representing if there should be a colour swatch visible, // ] const testData = [ - ["v", "v", -1, 0, null], - ["a", "va", -1, 0, null], - ["r", "var", -1, 0, null], - ["(", "var()", -1, 0, null], - ["-", "var(--abc)", 0, 4, "blue"], - ["VK_BACK_SPACE", "var(-)", -1, 0, null], - ["-", "var(--abc)", 0, 4, "blue"], - ["VK_DOWN", "var(--def)", 1, 4, "red"], - ["VK_DOWN", "var(--ghi)", 2, 4, "green"], - ["VK_DOWN", "var(--jkl)", 3, 4, "yellow"], - ["VK_DOWN", "var(--abc)", 0, 4, "blue"], - ["VK_DOWN", "var(--def)", 1, 4, "red"], - ["VK_LEFT", "var(--def)", -1, 0, null], + ["v", "v", -1, 0, null, !COLORSWATCH], + ["a", "va", -1, 0, null, !COLORSWATCH], + ["r", "var", -1, 0, null, !COLORSWATCH], + ["(", "var()", -1, 0, null, !COLORSWATCH], + ["-", "var(--abc)", 0, 9, "inherit", !COLORSWATCH], + ["VK_BACK_SPACE", "var(-)", -1, 0, null, !COLORSWATCH], + ["-", "var(--abc)", 0, 9, "inherit", !COLORSWATCH], + ["VK_DOWN", "var(--def)", 1, 9, "transparent", !COLORSWATCH], + ["VK_DOWN", "var(--ghi)", 2, 9, "#00FF00", COLORSWATCH], + ["VK_DOWN", "var(--jkl)", 3, 9, "rgb(255, 0, 0)", COLORSWATCH], + ["VK_DOWN", "var(--mno)", 4, 9, "hsl(120, 60%, 70%)", COLORSWATCH], + ["VK_DOWN", "var(--pqr)", 5, 9, "BlueViolet", COLORSWATCH], + ["VK_DOWN", "var(--stu)", 6, 9, "15px", !COLORSWATCH], + ["VK_DOWN", "var(--vwx)", 7, 9, "rgba(255, 0, 0, 0.4)", COLORSWATCH], + ["VK_DOWN", "var(--yz)", 8, 9, "hsla(120, 60%, 70%, 0.3)", COLORSWATCH], + ["VK_DOWN", "var(--abc)", 0, 9, "inherit", !COLORSWATCH], + ["VK_DOWN", "var(--def)", 1, 9, "transparent", !COLORSWATCH], + ["VK_DOWN", "var(--ghi)", 2, 9, "#00FF00", COLORSWATCH], + ["VK_LEFT", "var(--ghi)", -1, 0, null, !COLORSWATCH], ]; const CSS_VARIABLES = [ - ["--abc", "blue"], - ["--def", "red"], - ["--ghi", "green"], - ["--jkl", "yellow"] + ["--abc", "inherit"], + ["--def", "transparent"], + ["--ghi", "#00FF00"], + ["--jkl", "rgb(255, 0, 0)"], + ["--mno", "hsl(120, 60%, 70%)"], + ["--pqr", "BlueViolet"], + ["--stu", "15px"], + ["--vwx", "rgba(255, 0, 0, 0.4)"], + ["--yz", "hsla(120, 60%, 70%, 0.3)"], ]; const mockGetCSSValuesForPropertyName = function(propertyName) { diff --git a/devtools/client/shared/test/helper_inplace_editor.js b/devtools/client/shared/test/helper_inplace_editor.js index 09e6cf2be2d0..70a4d96e5483 100644 --- a/devtools/client/shared/test/helper_inplace_editor.js +++ b/devtools/client/shared/test/helper_inplace_editor.js @@ -11,6 +11,7 @@ const HTML_NS = "http://www.w3.org/1999/xhtml"; const { editableField } = require("devtools/client/shared/inplace-editor"); +const {colorUtils} = require("devtools/shared/css/color"); /** * Create an inplace editor linked to a span element and click on the span to @@ -73,10 +74,12 @@ function createSpan(doc) { * - {Number} index, the index of the selected suggestion in the popup * - {Number} total, the total number of suggestions in the popup * - {String} postLabel, the expected post label for the selected suggestion + * - {Boolean} colorSwatch, if there is a swatch of color expected to be visible * @param {InplaceEditor} editor * The InplaceEditor instance being tested */ -async function testCompletion([key, completion, index, total, postLabel], editor) { +async function testCompletion([key, completion, index, total, + postLabel, colorSwatch], editor) { info("Pressing key " + key); info("Expecting " + completion); @@ -112,6 +115,21 @@ async function testCompletion([key, completion, index, total, postLabel], editor let selectedElement = editor.popup.elements.get(selectedItem); ok(selectedElement.textContent.includes(postLabel), "Selected popup element contains the expected post-label"); + + // Determines if there is a color swatch attached to the label + // and if the color swatch's background color matches the post label + let swatchSpan = selectedElement.getElementsByClassName( + "autocomplete-swatch autocomplete-colorswatch"); + if (colorSwatch) { + ok(swatchSpan.length === 1, "Displayed the expected color swatch"); + let color = new colorUtils.CssColor(swatchSpan[0].style.backgroundColor); + let swatchColor = color.rgba; + color.newColor(postLabel); + let postColor = color.rgba; + ok(swatchColor == postColor, "Color swatch matches postLabel value"); + } else { + ok(swatchSpan.length === 0, "As expected no swatches were available"); + } } if (total === 0) { diff --git a/devtools/client/themes/common.css b/devtools/client/themes/common.css index 71604e9a0266..d98a50bcc212 100644 --- a/devtools/client/themes/common.css +++ b/devtools/client/themes/common.css @@ -108,6 +108,35 @@ html|button, html|select { text-align: end; } +.devtools-autocomplete-listbox .autocomplete-swatch { + cursor: pointer; + border-radius: 50%; + width: 1em; + height: 1em; + vertical-align: middle; + /* align the swatch with its value */ + margin-top: -1px; + margin-inline-end: 5px; + display: inline-block; + position: relative; +} + +.devtools-autocomplete-listbox .autocomplete-colorswatch::before { + content: ''; + background-color: #eee; + background-image: linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc), + linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc); + background-size: 12px 12px; + background-position: 0 0, 6px 6px; + position: absolute; + border-radius: 50%; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: -1; +} + /* Rest of the dark and light theme */ .devtools-autocomplete-popup,