Bug 1642635 - Let text wrap in item summary table and improve alignment. r+a=darktrojan
authorPaul Morris <paul@thunderbird.net>
Wed, 03 Jun 2020 13:07:17 +0000
changeset 39331 b9ef529352b6e09d4bec1becf097718eb0654be5
parent 39330 fdb016c8e6c5c4a0e1c23f42b2976b34ed431e83
child 39332 a3d447848238a9ff547946525326a7dc23dfaa30
push id402
push userclokep@gmail.com
push dateMon, 29 Jun 2020 20:48:04 +0000
bugs1642635
Bug 1642635 - Let text wrap in item summary table and improve alignment. r+a=darktrojan For lengthy content like dates with timezones we want the content to wrap, not disappear on the right side of dialogs. Removing the HTML input elements allows us to do that. Differential Revision: https://phabricator.services.mozilla.com/D77976
calendar/base/content/widgets/calendar-item-summary.css
calendar/base/content/widgets/calendar-item-summary.js
calendar/base/themes/common/dialogs/calendar-event-dialog.css
calendar/test/browser/browser_import.js
--- a/calendar/base/content/widgets/calendar-item-summary.css
+++ b/calendar/base/content/widgets/calendar-item-summary.css
@@ -1,44 +1,45 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-th {
-  text-align: right;
-  padding-inline-end: 5px;
-  font-weight: normal;
-}
-
-td {
-  width: 100%;
-  padding-top: 0;
-}
-
-.calendar-summary-box {
-  display: flex;
-}
-
 .calendar-summary-table {
   width: 100%;
 }
 
-.calendar-summary-table input {
+.calendar-summary-table th {
+  -moz-user-select: text;
+  font-weight: normal;
+  padding-inline-end: 5px;
+  text-align: right;
+  vertical-align: baseline;
+  white-space: nowrap;
+}
+
+.calendar-summary-table td {
+  -moz-user-select: text;
+  padding-top: 0;
+  vertical-align: baseline;
   width: 100%;
 }
 
 .repeat-row > th {
   vertical-align: top;
   padding-top: 2px;
 }
 
 .repeat-details {
   width: 100%;
 }
 
-.calendar-summary-table > tr > th > label {
-  margin-inline-end: 0;
+.calendar-summary-table .item-location-link > label {
+  margin-inline: 0;
 }
 
-.calendar-summary-table > tr > td > label {
-  margin-top: 2px;
-  margin-inline-start: 4px;
+.calendar-summary-table .item-organizer-cell {
+  margin-inline: 0px;
 }
+
+.calendar-summary-table .organizer-label,
+.calendar-summary-table .attachments-label {
+  vertical-align: top;
+}
--- a/calendar/base/content/widgets/calendar-item-summary.js
+++ b/calendar/base/content/widgets/calendar-item-summary.js
@@ -36,115 +36,100 @@
       let itemDescriptionId = id + "-item-description";
       let urlLinkId = id + "-url-link";
 
       this.appendChild(
         MozXULElement.parseXULToFragment(
           `
           <vbox class="item-summary-box" flex="1">
             <!-- General -->
-            <box orient="vertical">
             <hbox class="calendar-caption" align="center">
               <label value="&read.only.general.label;" class="header"/>
               <separator class="groove" flex="1"/>
             </hbox>
-            <hbox flex="1" class="calendar-summary-box">
             <html:table class="calendar-summary-table">
               <html:tr>
                 <html:th>
                   &read.only.title.label;
                 </html:th>
-                <html:td>
-                  <html:input class="item-title selectable-label plain input-inline"
-                              readonly="readonly"/>
+                <html:td class="item-title">
                 </html:td>
               </html:tr>
               <html:tr class="calendar-row" hidden="hidden">
                 <html:th>
                   &read.only.calendar.label;
                 </html:th>
-                <html:td>
-                  <html:input class="item-calendar selectable-label plain input-inline"
-                              readonly="readonly"/>
+                <html:td class="item-calendar">
                 </html:td>
               </html:tr>
               <html:tr class="item-date-row">
-                <html:th>
-                  <label class="item-start-row-label"
+                <html:th class="item-start-row-label"
                          taskStartLabel="&read.only.task.start.label;"
-                         eventStartLabel="&read.only.event.start.label;"/>
+                         eventStartLabel="&read.only.event.start.label;">
                 </html:th>
-                <html:td>
-                  <html:input class="item-date-row-start-date selectable-label plain input-inline"
-                              readonly="readonly"/>
+                <html:td class="item-date-row-start-date">
                 </html:td>
               </html:tr>
               <html:tr class="item-date-row">
-                <html:th>
-                  <label class="item-due-row-label"
+                <html:th class="item-due-row-label"
                          taskDueLabel="&read.only.task.due.label;"
-                         eventEndLabel="&read.only.event.end.label;"/>
+                         eventEndLabel="&read.only.event.end.label;">
                 </html:th>
-                <html:td>
-                  <html:input class="item-date-row-end-date selectable-label plain input-inline"
-                              readonly="readonly"/>
+                <html:td class="item-date-row-end-date">
                 </html:td>
               </html:tr>
               <html:tr class="repeat-row" hidden="hidden">
                 <html:th>
                   &read.only.repeat.label;
                 </html:th>
                 <html:td>
                   <box class="repeat-details" orient="vertical">
-                    <html:input readonly="readonly" class="selectable-label plain input-inline"/>
+                    <html:div/>
                   </box>
                 </html:td>
               </html:tr>
               <html:tr class="location-row" hidden="hidden">
                 <html:th>
                   &read.only.location.label;
                 </html:th>
-                <html:td>
-                  <html:input class="item-location selectable-label plain input-inline"
-                              readonly="readonly"/>
+                <html:td class="item-location">
                 </html:td>
               </html:tr>
               <html:tr class="category-row" hidden="hidden">
                 <html:th>
                   &read.only.category.label;
                 </html:th>
-                <html:td>
-                  <label class="item-category"/>
+                <html:td class="item-category">
                 </html:td>
               </html:tr>
               <html:tr class="organizer-row item-attendees-row" hidden="hidden">
-                <html:th>
+                <html:th class="organizer-label">
                   &read.only.organizer.label;
                 </html:th>
                 <html:td>
                   <hbox class="item-organizer-cell">
                     <img class="itip-icon"/>
                     <label class="item-organizer-label text-link item-attendees-cell-label"
                            crop="end"/>
                     <spacer flex="1"/>
                   </hbox>
                 </html:td>
               </html:tr>
               <html:tr class="status-row" hidden="hidden">
                 <html:th>
                   &task.status.label;
                 </html:th>
                 <html:td class="status-row-td">
-                  <label value="&newevent.status.tentative.label;" hidden="true" status="TENTATIVE"/>
-                  <label value="&newevent.status.confirmed.label;" hidden="true" status="CONFIRMED"/>
-                  <label value="&newevent.eventStatus.cancelled.label;" hidden="true" status="CANCELLED"/>
-                  <label value="&newevent.todoStatus.cancelled.label;" hidden="true" status="CANCELLED"/>
-                  <label value="&newevent.status.needsaction.label;" hidden="true" status="NEEDS-ACTION"/>
-                  <label value="&newevent.status.inprogress.label;" hidden="true" status="IN-PROCESS"/>
-                  <label value="&newevent.status.completed.label;" hidden="true" status="COMPLETED"/>
+                  <html:div hidden="true" status="TENTATIVE">&newevent.status.tentative.label;</html:div>
+                  <html:div hidden="true" status="CONFIRMED">&newevent.status.confirmed.label;</html:div>
+                  <html:div hidden="true" status="CANCELLED">&newevent.eventStatus.cancelled.label;</html:div>
+                  <html:div hidden="true" status="CANCELLED">&newevent.todoStatus.cancelled.label;</html:div>
+                  <html:div hidden="true" status="NEEDS-ACTION">&newevent.status.needsaction.label;</html:div>
+                  <html:div hidden="true" status="IN-PROCESS">&newevent.status.inprogress.label;</html:div>
+                  <html:div hidden="true" status="COMPLETED">&newevent.status.completed.label;</html:div>
                 </html:td>
               </html:tr>
               <separator class="groove" flex="1" hidden="true"/>
               <html:tr class="reminder-row" hidden="hidden">
                 <html:th>
                   &read.only.reminder.label;
                 </html:th>
                 <html:td>
@@ -229,17 +214,17 @@
                              disable-on-readonly="true"
                              flex="1"
                              hyperlink="true"/>
                     </hbox>
                   </hbox>
                 </html:td>
               </html:tr>
               <html:tr class="attachments-row item-attachments-row" hidden="hidden" >
-                <html:th>
+                <html:th class="attachments-label">
                   &read.only.attachments.label;
                 </html:th>
                 <html:td>
                   <vbox class="item-attachment-cell">
                     <!-- attachment box template -->
                     <hbox class="attachment-template"
                           hidden="true"
                           align="center"
@@ -248,18 +233,16 @@
                       <label class="text-link item-attachment-cell-label"
                              crop="end"
                              flex="1" />
                     </hbox>
                   </vbox>
                 </html:td>
               </html:tr>
             </html:table>
-            </hbox>
-            </box>
 
             <!-- attendee box template -->
             <vbox class="item-attendees-box-template">
               <hbox flex="1" class="item-attendees-row" equalsize="always" hidden="true">
                 <box class="item-attendees-cell" hidden="true" flex="1">
                   <img class="itip-icon"/>
                   <label class="item-attendees-cell-label" crop="end" flex="1"/>
                 </box>
@@ -394,55 +377,53 @@
     updateItemDetails() {
       if (!this.item) {
         // Setup not complete, do nothing for now.
         return;
       }
       let item = this.item;
       let isToDoItem = this.mIsToDoItem;
 
-      this.querySelector(".item-title").value = item.title;
+      this.querySelector(".item-title").textContent = item.title;
 
       if (this.calendar) {
         this.querySelector(".calendar-row").removeAttribute("hidden");
-        this.querySelector(".item-calendar").value = this.calendar.name;
+        this.querySelector(".item-calendar").textContent = this.calendar.name;
       }
 
       // Show start date.
       let itemStartDate = item[cal.dtz.startDateProp(item)];
 
       let itemStartRowLabel = this.querySelector(".item-start-row-label");
       let itemDateRowStartDate = this.querySelector(".item-date-row-start-date");
 
       itemStartRowLabel.style.visibility = itemStartDate ? "visible" : "collapse";
       itemDateRowStartDate.style.visibility = itemStartDate ? "visible" : "collapse";
 
       if (itemStartDate) {
-        let itemStartLabelValue = itemStartRowLabel.getAttribute(
+        itemStartRowLabel.textContent = itemStartRowLabel.getAttribute(
           isToDoItem ? "taskStartLabel" : "eventStartLabel"
         );
-        itemStartRowLabel.setAttribute("value", itemStartLabelValue);
-        itemDateRowStartDate.value = cal.dtz.getStringForDateTime(itemStartDate);
+        itemDateRowStartDate.textContent = cal.dtz.getStringForDateTime(itemStartDate);
       }
 
       // Show due date / end date.
       let itemDueDate = item[cal.dtz.endDateProp(item)];
 
       let itemDueRowLabel = this.querySelector(".item-due-row-label");
       let itemDateRowEndDate = this.querySelector(".item-date-row-end-date");
 
       itemDueRowLabel.style.visibility = itemDueDate ? "visible" : "collapse";
       itemDateRowEndDate.style.visibility = itemDueDate ? "visible" : "collapse";
 
       if (itemDueDate) {
-        let itemDueLabelValue = itemDueRowLabel.getAttribute(
+        itemDueRowLabel.textContent = itemDueRowLabel.getAttribute(
           isToDoItem ? "taskDueLabel" : "eventEndLabel"
         );
-        itemDueRowLabel.setAttribute("value", itemDueLabelValue);
-        itemDateRowEndDate.value = cal.dtz.getStringForDateTime(itemDueDate);
+        itemDateRowEndDate.textContent = cal.dtz.getStringForDateTime(itemDueDate);
       }
 
       // Show reminder if this item is *not* readonly.
       // This case happens for example if this is an invitation.
       if (!this.readOnly) {
         let argCalendar = item.calendar;
 
         let supportsReminders =
@@ -472,17 +453,17 @@
       if (location) {
         this.updateLocation(location);
       }
 
       let categories = item.getCategories();
       if (categories.length > 0) {
         this.querySelector(".category-row").removeAttribute("hidden");
         // TODO: this join is unfriendly for l10n (categories.join(", ")).
-        this.querySelector(".item-category").value = categories.join(", ");
+        this.querySelector(".item-category").textContent = categories.join(", ");
       }
 
       if (item.organizer && item.organizer.id) {
         this.updateOrganizer(item.organizer);
       }
 
       let status = item.getProperty("STATUS");
       if (status && status.length) {
@@ -547,17 +528,17 @@
 
       while (repeatDetails.children.length > lines.length) {
         repeatDetails.lastChild.remove();
       }
       while (repeatDetails.children.length < lines.length) {
         repeatDetails.appendChild(repeatDetails.firstElementChild.cloneNode(true));
       }
       for (let i = 0; i < lines.length; i++) {
-        repeatDetails.children[i].value = lines[i];
+        repeatDetails.children[i].textContent = lines[i];
         repeatDetails.children[i].setAttribute("tooltiptext", details);
       }
     }
 
     /**
      * Updates the attendee listbox, displaying all attendees invited to the item.
      */
     updateAttendees(item) {
@@ -596,17 +577,17 @@
 
         let label = document.createXULElement("label");
         label.setAttribute("context", "location-link-context-menu");
         label.textContent = location;
         link.appendChild(label);
 
         itemLocation.replaceWith(link);
       } else {
-        itemLocation.value = location;
+        itemLocation.textContent = location;
       }
     }
 
     /**
      * Handle window resize event. Rearrange attendees.
      */
     onWindowResize() {
       let { attendeesInRow, maxLabelWidth } = rearrangeAttendees(
@@ -668,17 +649,17 @@
       let statusRow = this.querySelector(".status-row");
       let statusRowData = this.querySelector(".status-row-td");
 
       for (let i = 0; i < statusRowData.children.length; i++) {
         if (statusRowData.children[i].getAttribute("status") == status) {
           statusRow.removeAttribute("hidden");
 
           if (status == "CANCELLED" && isToDoItem) {
-            // There are two labels for CANCELLED, the second one is for
+            // There are two status elements for CANCELLED, the second one is for
             // todo items. Increment the counter here.
             i++;
           }
           statusRowData.children[i].removeAttribute("hidden");
           break;
         }
       }
     }
--- a/calendar/base/themes/common/dialogs/calendar-event-dialog.css
+++ b/calendar/base/themes/common/dialogs/calendar-event-dialog.css
@@ -210,17 +210,17 @@ label.label {
   margin-inline-start: 0;
   padding-inline-start: 4px;
 }
 
 .item-location-link {
   padding-inline-start: 0;
 }
 
-.item-location-link > label{
+.item-location-link > label {
   cursor: pointer;
 }
 
 #todo-status,
 #item-repeat,
 .item-alarm {
   margin: 0;
 }
@@ -814,28 +814,39 @@ calendar-event-freebusy-day > box {
 
 #calendar-summary-dialog,
 #calendar-event-summary-dialog,
 #calendar-task-summary-dialog,
 #calendar-ics-file-dialog {
   min-width: 35em;
 }
 
+#calendar-summary-dialog .item-location,
+#calendar-summary-dialog .item-title,
+#calendar-event-summary-dialog .item-location,
+#calendar-event-summary-dialog .item-title,
+#calendar-task-summary-dialog .item-location,
+#calendar-task-summary-dialog .item-title,
+#calendar-ics-file-dialog .item-location,
+#calendar-ics-file-dialog .item-title {
+  padding-inline-start: 1px;
+}
+
 #calendar-summary-dialog .item-attachment-cell,
 #calendar-event-summary-dialog .item-attachment-cell,
 #calendar-task-summary-dialog .item-attachment-cell,
 #calendar-ics-file-dialog .item-attachment-cell {
-  margin-left: 6px;
+  margin-left: 0px;
 }
 
 #calendar-summary-dialog .item-attachment-cell-label,
 #calendar-event-summary-dialog .item-attachment-cell-label,
 #calendar-task-summary-dialog .item-attachment-cell-label,
 #calendar-ics-file-dialog .item-attachment-cell-label {
-  margin-left: 3px;
+  margin-left: 0px;
 }
 
 #calendar-summary-dialog .item-description-wrapper,
 #calendar-event-summary-dialog .item-description-wrapper,
 #calendar-task-summary-dialog .item-description-wrapper,
 #calendar-ics-file-dialog .item-description-wrapper {
   display: flex;
 }
@@ -845,24 +856,16 @@ calendar-event-freebusy-day > box {
 #calendar-task-summary-dialog .item-description,
 #calendar-ics-file-dialog .item-description {
   width: 100%;
   box-sizing: border-box;
   min-height: 54px;
   margin: 2px 4px 0;
 }
 
-#calendar-summary-dialog .selectable-label,
-#calendar-event-summary-dialog .selectable-label,
-#calendar-task-summary-dialog .selectable-label,
-#calendar-ics-file-dialog .selectable-label {
-  background-color: inherit;
-  color: inherit;
-}
-
 #calendar-summary-dialog #item-start-row .headline,
 #calendar-event-summary-dialog #item-start-row .headline,
 #calendar-task-summary-dialog #item-start-row .headline,
 #calendar-ics-file-dialog #item-start-row .headline,
 #calendar-summary-dialog #item-end-row .headline,
 #calendar-event-summary-dialog #item-end-row .headline,
 #calendar-task-summary-dialog #item-end-row .headline,
 #calendar-ics-file-dialog #item-end-row .headline {
--- a/calendar/test/browser/browser_import.js
+++ b/calendar/test/browser/browser_import.js
@@ -57,73 +57,89 @@ add_task(async () => {
       let calendarMenu = doc.querySelector("#calendar-ics-file-dialog-calendar-menu");
       let calendarMenuItems = calendarMenu.querySelectorAll("menuitem");
       is(calendarMenu.value, "Mozmill", "correct calendar name is selected");
       is(calendarMenuItems.length, 1, "exactly one calendar is in the calendars menu");
       is(calendarMenuItems[0].selected, true, "calendar menu item is selected");
 
       let items = doc.querySelectorAll(".calendar-ics-file-dialog-item-frame");
       is(items.length, 4, "four calendar items are displayed");
-      is(items[0].querySelector(".item-title").value, "Event One", "event 1 title is correct");
-      is(items[1].querySelector(".item-title").value, "Event Two", "event 2 title is correct");
-      is(items[2].querySelector(".item-title").value, "Event Three", "event 3 title is correct");
-      is(items[3].querySelector(".item-title").value, "Event Four", "event 4 title is correct");
       is(
-        items[0].querySelector(".item-date-row-start-date").value,
-        cal.dtz.formatter.formatDateTime(cal.createDateTime("20190101T150000")),
-        "event 1 start date is correct"
+        items[0].querySelector(".item-title").textContent,
+        "Event One",
+        "event 1 title should be correct"
+      );
+      is(
+        items[1].querySelector(".item-title").textContent,
+        "Event Two",
+        "event 2 title should be correct"
       );
       is(
-        items[0].querySelector(".item-date-row-end-date").value,
-        cal.dtz.formatter.formatDateTime(cal.createDateTime("20190101T160000")),
-        "event 1 end date is correct"
+        items[2].querySelector(".item-title").textContent,
+        "Event Three",
+        "event 3 title should be correct"
       );
       is(
-        items[1].querySelector(".item-date-row-start-date").value,
-        cal.dtz.formatter.formatDateTime(cal.createDateTime("20190101T160000")),
-        "event 2 start date is correct"
+        items[3].querySelector(".item-title").textContent,
+        "Event Four",
+        "event 4 title should be correct"
+      );
+      is(
+        items[0].querySelector(".item-date-row-start-date").textContent,
+        cal.dtz.formatter.formatDateTime(cal.createDateTime("20190101T150000")),
+        "event 1 start date should be correct"
       );
       is(
-        items[1].querySelector(".item-date-row-end-date").value,
-        cal.dtz.formatter.formatDateTime(cal.createDateTime("20190101T170000")),
-        "event 2 end date is correct"
+        items[0].querySelector(".item-date-row-end-date").textContent,
+        cal.dtz.formatter.formatDateTime(cal.createDateTime("20190101T160000")),
+        "event 1 end date should be correct"
       );
       is(
-        items[2].querySelector(".item-date-row-start-date").value,
+        items[1].querySelector(".item-date-row-start-date").textContent,
+        cal.dtz.formatter.formatDateTime(cal.createDateTime("20190101T160000")),
+        "event 2 start date should be correct"
+      );
+      is(
+        items[1].querySelector(".item-date-row-end-date").textContent,
         cal.dtz.formatter.formatDateTime(cal.createDateTime("20190101T170000")),
-        "event 3 start date is correct"
+        "event 2 end date should be correct"
       );
       is(
-        items[2].querySelector(".item-date-row-end-date").value,
-        cal.dtz.formatter.formatDateTime(cal.createDateTime("20190101T180000")),
-        "event 3 end date is correct"
+        items[2].querySelector(".item-date-row-start-date").textContent,
+        cal.dtz.formatter.formatDateTime(cal.createDateTime("20190101T170000")),
+        "event 3 start date should be correct"
       );
       is(
-        items[3].querySelector(".item-date-row-start-date").value,
+        items[2].querySelector(".item-date-row-end-date").textContent,
         cal.dtz.formatter.formatDateTime(cal.createDateTime("20190101T180000")),
-        "event 4 start date is correct"
+        "event 3 end date should be correct"
       );
       is(
-        items[3].querySelector(".item-date-row-end-date").value,
+        items[3].querySelector(".item-date-row-start-date").textContent,
+        cal.dtz.formatter.formatDateTime(cal.createDateTime("20190101T180000")),
+        "event 4 start date should be correct"
+      );
+      is(
+        items[3].querySelector(".item-date-row-end-date").textContent,
         cal.dtz.formatter.formatDateTime(cal.createDateTime("20190101T190000")),
-        "event 4 end date is correct"
+        "event 4 end date should be correct"
       );
 
       // Import just the first item, and check that the correct number of items remains.
       let firstItemImportButton = items[0].querySelector(
         ".calendar-ics-file-dialog-item-import-button"
       );
       EventUtils.synthesizeMouseAtCenter(firstItemImportButton, { clickCount: 1 }, dialogWindow);
 
       let remainingItems = doc.querySelectorAll(".calendar-ics-file-dialog-item-frame");
       is(remainingItems.length, 3, "three items remain after importing the first item");
       is(
-        remainingItems[0].querySelector(".item-title").value,
+        remainingItems[0].querySelector(".item-title").textContent,
         "Event Two",
-        "'Event Two' is now in the first item in the dialog"
+        "'Event Two' should now be the first item in the dialog"
       );
 
       let messageElement = doc.querySelector("#calendar-ics-file-dialog-message");
 
       // Set up an observer to wait for the import success message to appear,
       // before clicking the accept button again to close the dialog window.
       let observer = new MutationObserver(mutationList => {
         mutationList.forEach(async mutation => {