Bug 1529599 - (Part 1) Add Copy Declaration to Changes panel context menu; r=gl
authorRazvan Caliman <rcaliman@mozilla.com>
Thu, 28 Feb 2019 17:30:02 +0000
changeset 519678 357281e408cf32115a1306f7ca660cf8e6445301
parent 519677 8540d6d786fd55bf7e00069d039692fc0222de9c
child 519679 e6ff2537e53c4bd7dc6578df6d803e748e0d8262
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgl
bugs1529599
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1529599 - (Part 1) Add Copy Declaration to Changes panel context menu; r=gl Adds a new option to the context menu which is visible only when it's invoked on a CSS declaration. Builds a string with the property name and value and copies to the clipboard. If the declaration is marked as removed, the string is wrapped in a comment block. Differential Revision: https://phabricator.services.mozilla.com/D21526
devtools/client/inspector/changes/ChangesContextMenu.js
devtools/client/inspector/changes/ChangesView.js
devtools/client/inspector/changes/components/CSSDeclaration.js
devtools/client/inspector/changes/test/head.js
devtools/client/locales/en-US/changes.properties
devtools/client/themes/changes.css
--- a/devtools/client/inspector/changes/ChangesContextMenu.js
+++ b/devtools/client/inspector/changes/ChangesContextMenu.js
@@ -23,16 +23,17 @@ class ChangesContextMenu {
     this.inspector = this.view.inspector;
     // Document object to which the Changes panel belongs to.
     this.document = this.view.document;
     // DOM element container for the Changes panel content.
     this.panel = this.document.getElementById("sidebar-panel-changes");
     // Window object to which the Changes panel belongs to.
     this.window = this.document.defaultView;
 
+    this._onCopyDeclaration = this.view.copyDeclaration.bind(this.view);
     this._onCopyRule = this.view.copyRule.bind(this.view);
     this._onCopySelection = this.view.copySelection.bind(this.view);
     this._onSelectAll = this._onSelectAll.bind(this);
   }
 
   show(event) {
     this._openMenu({
       target: event.target,
@@ -50,26 +51,34 @@ class ChangesContextMenu {
     const menuitemCopy = new MenuItem({
       label: getStr("changes.contextmenu.copy"),
       accesskey: getStr("changes.contextmenu.copy.accessKey"),
       click: this._onCopySelection,
       disabled: !this._hasTextSelected(),
     });
     menu.append(menuitemCopy);
 
+    const declEl = target.closest(".changes__declaration");
     const ruleEl = target.closest("[data-rule-id]");
     const ruleId = ruleEl ? ruleEl.dataset.ruleId : null;
 
-    if (ruleId) {
+    if (ruleId || declEl) {
       // Copy Rule option
       menu.append(new MenuItem({
         label: getStr("changes.contextmenu.copyRule"),
         click: () => this._onCopyRule(ruleId),
       }));
 
+      // Copy Declaration option. Visible only if there is a declaration element target.
+      menu.append(new MenuItem({
+        label: getStr("changes.contextmenu.copyDeclaration"),
+        click: () => this._onCopyDeclaration(declEl),
+        visible: !!declEl,
+      }));
+
       menu.append(new MenuItem({
         type: "separator",
       }));
     }
 
     // Select All option
     const menuitemSelectAll = new MenuItem({
       label: getStr("changes.contextmenu.selectAll"),
--- a/devtools/client/inspector/changes/ChangesView.js
+++ b/devtools/client/inspector/changes/ChangesView.js
@@ -142,16 +142,32 @@ class ChangesView {
       filter.sourceIds = [sourceId];
     }
 
     const text = getChangesStylesheet(state, filter);
     clipboardHelper.copyString(text);
   }
 
   /**
+   * Handler for the "Copy Declaration" option from the context menu.
+   * Builds a CSS declaration string with the property name and value, and copies it
+   * to the clipboard. The declaration is commented out if it is marked as removed.
+   *
+   * @param {DOMElement} element
+   *        Host element of a CSS declaration rendered the Changes panel.
+   */
+  copyDeclaration(element) {
+    const name = element.querySelector(".changes__declaration-name").textContent;
+    const value = element.querySelector(".changes__declaration-value").textContent;
+    const isRemoved = element.classList.contains("diff-remove");
+    const text = isRemoved ? `/* ${name}: ${value}; */` : `${name}: ${value};`;
+    clipboardHelper.copyString(text);
+  }
+
+  /**
    * Handler for the "Copy Rule" option from the context menu.
    * Gets the full content of the target CSS rule (including any changes applied)
    * and copies it to the clipboard.
    *
    * @param {String} ruleId
    *        Rule id of the target CSS rule.
    */
   async copyRule(ruleId) {
--- a/devtools/client/inspector/changes/components/CSSDeclaration.js
+++ b/devtools/client/inspector/changes/components/CSSDeclaration.js
@@ -23,19 +23,19 @@ class CSSDeclaration extends PureCompone
       className: "",
       marker: null,
     };
   }
 
   render() {
     const { className, marker, property, value } = this.props;
 
-    return dom.div({ className: `declaration ${className}` },
+    return dom.div({ className: `changes__declaration ${className}` },
       marker,
-      dom.span({ className: "declaration-name theme-fg-color3"}, property),
+      dom.span({ className: "changes__declaration-name theme-fg-color3"}, property),
       ": ",
-      dom.span({ className: "declaration-value theme-fg-color1"}, value),
+      dom.span({ className: "changes__declaration-value theme-fg-color1"}, value),
       ";"
     );
   }
 }
 
 module.exports = CSSDeclaration;
--- a/devtools/client/inspector/changes/test/head.js
+++ b/devtools/client/inspector/changes/test/head.js
@@ -39,26 +39,26 @@ registerCleanupFunction(() => {
  *         If omitted, all declarations will be returned.
  * @param  {DOMNode} containerNode
  *         Optional element to restrict results to declaration DOM elements which are
  *         descendants of this container node.
  *         If omitted, all declarations will be returned
  * @return {Array}
  */
 function getDeclarations(panelDoc, selector = "", containerNode = null) {
-  const els = panelDoc.querySelectorAll(`#sidebar-panel-changes .declaration${selector}`);
+  const els = panelDoc.querySelectorAll(`.changes__declaration${selector}`);
 
   return [...els]
     .filter(el => {
       return containerNode ? containerNode.contains(el) : true;
     })
     .map(el => {
       return {
-        property: el.querySelector(".declaration-name").textContent,
-        value: el.querySelector(".declaration-value").textContent,
+        property: el.querySelector(".changes__declaration-name").textContent,
+        value: el.querySelector(".changes__declaration-value").textContent,
       };
     });
 }
 
 function getAddedDeclarations(panelDoc, containerNode) {
   return getDeclarations(panelDoc, ".diff-add", containerNode);
 }
 
--- a/devtools/client/locales/en-US/changes.properties
+++ b/devtools/client/locales/en-US/changes.properties
@@ -38,16 +38,20 @@ changes.contextmenu.copy.accessKey=C
 # stylesheet
 changes.contextmenu.copyAllChanges=Copy All Changes
 
 # LOCALIZATION NOTE (changes.contextmenu.copyAllChangesDescription): Detailed explanation
 # for "Copy All Changes" option in Changes panel. Used as title attribute on "Copy All
 # Changes" button
 changes.contextmenu.copyAllChangesDescription=Copy a list of all CSS changes to clipboard.
 
+# LOCALIZATION NOTE (changes.contextmenu.copyDeclaration): Label for "Copy Declaration"
+# option in Changes panel context menu which copies the target CSS declaration.
+changes.contextmenu.copyDeclaration=Copy Declaration
+
 # LOCALIZATION NOTE (changes.contextmenu.copyRule): Label for "Copy Rule" option in
 # Changes panel context menu which copies the complete contents of a CSS rule.
 changes.contextmenu.copyRule=Copy Rule
 
 # LOCALIZATION NOTE (changes.contextmenu.copyRuleDescription): Detailed explanation for
 # "Copy Rule" option in Changes panel. Used as title attribute on "Copy Rule" button.
 changes.contextmenu.copyRuleDescription=Copy contents of this CSS rule to clipboard.
 
--- a/devtools/client/themes/changes.css
+++ b/devtools/client/themes/changes.css
@@ -152,17 +152,17 @@
 
 /* Show the Copy Rule button when hovering over the rule's selector elements */
 .changes__selector:hover + .changes__copy-rule-button,
 .changes__selector:hover + .changes__selector + .changes__copy-rule-button,
 .changes__copy-rule-button:hover {
   display: block;
 }
 
-#sidebar-panel-changes .declaration-name {
+.changes__declaration-name {
   margin-left: 10px;
 }
 
 .diff-add,
 .diff-remove {
   --diff-level-min-offset: 15px;
   position: relative;
 }