@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Give unit test results their own table in Differential

Summary: Ref T10457. This gives unit test results a more first-class treatment in the Differential UI, and consolidates some rendering code.

Test Plan:
Before:

{F1135536}

After:

{F1135537}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10457

Differential Revision: https://secure.phabricator.com/D15365

+365 -184
+3 -3
resources/celerity/map.php
··· 7 7 */ 8 8 return array( 9 9 'names' => array( 10 - 'core.pkg.css' => 'f7a91f6a', 10 + 'core.pkg.css' => '76a3afdf', 11 11 'core.pkg.js' => '7d8faf57', 12 12 'darkconsole.pkg.js' => 'e7393ebb', 13 13 'differential.pkg.css' => '2de124c9', ··· 25 25 'rsrc/css/aphront/notification.css' => '7f684b62', 26 26 'rsrc/css/aphront/panel-view.css' => '8427b78d', 27 27 'rsrc/css/aphront/phabricator-nav-view.css' => 'ac79a758', 28 - 'rsrc/css/aphront/table-view.css' => 'ec078a76', 28 + 'rsrc/css/aphront/table-view.css' => 'aba95954', 29 29 'rsrc/css/aphront/tokenizer.css' => '056da01b', 30 30 'rsrc/css/aphront/tooltip.css' => '1a07aea8', 31 31 'rsrc/css/aphront/typeahead-browse.css' => 'd8581d2c', ··· 523 523 'aphront-list-filter-view-css' => '5d6f0526', 524 524 'aphront-multi-column-view-css' => 'fd18389d', 525 525 'aphront-panel-view-css' => '8427b78d', 526 - 'aphront-table-view-css' => 'ec078a76', 526 + 'aphront-table-view-css' => 'aba95954', 527 527 'aphront-tokenizer-control-css' => '056da01b', 528 528 'aphront-tooltip-css' => '1a07aea8', 529 529 'aphront-typeahead-control-css' => 'd4f16145',
+3 -1
src/__phutil_library_map__.php
··· 1149 1149 'HarbormasterUnitMessageViewController' => 'applications/harbormaster/controller/HarbormasterUnitMessageViewController.php', 1150 1150 'HarbormasterUnitPropertyView' => 'applications/harbormaster/view/HarbormasterUnitPropertyView.php', 1151 1151 'HarbormasterUnitStatus' => 'applications/harbormaster/constants/HarbormasterUnitStatus.php', 1152 + 'HarbormasterUnitSummaryView' => 'applications/harbormaster/view/HarbormasterUnitSummaryView.php', 1152 1153 'HarbormasterUploadArtifactBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterUploadArtifactBuildStepImplementation.php', 1153 1154 'HarbormasterWaitForPreviousBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterWaitForPreviousBuildStepImplementation.php', 1154 1155 'HarbormasterWorker' => 'applications/harbormaster/worker/HarbormasterWorker.php', ··· 4624 4625 'DifferentialTransactionEditor' => 'PhabricatorApplicationTransactionEditor', 4625 4626 'DifferentialTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 4626 4627 'DifferentialTransactionView' => 'PhabricatorApplicationTransactionView', 4627 - 'DifferentialUnitField' => 'DifferentialHarbormasterField', 4628 + 'DifferentialUnitField' => 'DifferentialCustomField', 4628 4629 'DifferentialUnitStatus' => 'Phobject', 4629 4630 'DifferentialUnitTestResult' => 'Phobject', 4630 4631 'DifferentialUpdateRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod', ··· 5318 5319 'HarbormasterUnitMessageViewController' => 'HarbormasterController', 5319 5320 'HarbormasterUnitPropertyView' => 'AphrontView', 5320 5321 'HarbormasterUnitStatus' => 'Phobject', 5322 + 'HarbormasterUnitSummaryView' => 'AphrontView', 5321 5323 'HarbormasterUploadArtifactBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 5322 5324 'HarbormasterWaitForPreviousBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 5323 5325 'HarbormasterWorker' => 'PhabricatorWorker',
+126
src/applications/differential/controller/DifferentialController.php
··· 92 92 return $toc_view; 93 93 } 94 94 95 + protected function loadDiffProperties(array $diffs) { 96 + $diffs = mpull($diffs, null, 'getID'); 97 + 98 + $properties = id(new DifferentialDiffProperty())->loadAllWhere( 99 + 'diffID IN (%Ld)', 100 + array_keys($diffs)); 101 + $properties = mgroup($properties, 'getDiffID'); 102 + 103 + foreach ($diffs as $id => $diff) { 104 + $values = idx($properties, $id, array()); 105 + $values = mpull($values, 'getData', 'getName'); 106 + $diff->attachDiffProperties($values); 107 + } 108 + } 109 + 110 + 111 + protected function loadHarbormasterData(array $diffs) { 112 + $viewer = $this->getViewer(); 113 + 114 + $diffs = mpull($diffs, null, 'getPHID'); 115 + 116 + $buildables = id(new HarbormasterBuildableQuery()) 117 + ->setViewer($viewer) 118 + ->withBuildablePHIDs(array_keys($diffs)) 119 + ->withManualBuildables(false) 120 + ->needBuilds(true) 121 + ->needTargets(true) 122 + ->execute(); 123 + 124 + $buildables = mpull($buildables, null, 'getBuildablePHID'); 125 + foreach ($diffs as $phid => $diff) { 126 + $diff->attachBuildable(idx($buildables, $phid)); 127 + } 128 + 129 + $target_map = array(); 130 + foreach ($diffs as $phid => $diff) { 131 + $target_map[$phid] = $diff->getBuildTargetPHIDs(); 132 + } 133 + $all_target_phids = array_mergev($target_map); 134 + 135 + if ($all_target_phids) { 136 + $unit_messages = id(new HarbormasterBuildUnitMessage())->loadAllWhere( 137 + 'buildTargetPHID IN (%Ls)', 138 + $all_target_phids); 139 + $unit_messages = mgroup($unit_messages, 'getBuildTargetPHID'); 140 + } else { 141 + $unit_messages = array(); 142 + } 143 + 144 + foreach ($diffs as $phid => $diff) { 145 + $target_phids = idx($target_map, $phid, array()); 146 + $messages = array_select_keys($unit_messages, $target_phids); 147 + $messages = array_mergev($messages); 148 + $diff->attachUnitMessages($messages); 149 + } 150 + 151 + // For diffs with no messages, look for legacy unit messages stored on the 152 + // diff itself. 153 + foreach ($diffs as $phid => $diff) { 154 + if ($diff->getUnitMessages()) { 155 + continue; 156 + } 157 + 158 + if (!$diff->hasDiffProperty('arc:unit')) { 159 + continue; 160 + } 161 + 162 + $legacy_messages = $diff->getProperty('arc:unit'); 163 + if (!$legacy_messages) { 164 + continue; 165 + } 166 + 167 + // Show the top 100 legacy lint messages. Previously, we showed some 168 + // by default and let the user toggle the rest. With modern messages, 169 + // we can send the user to the Harbormaster detail page. Just show 170 + // "a lot" of messages in legacy cases to try to strike a balance 171 + // between implementation simplicitly and compatibility. 172 + $legacy_messages = array_slice($legacy_messages, 0, 100); 173 + 174 + $messages = array(); 175 + foreach ($legacy_messages as $message) { 176 + $messages[] = HarbormasterBuildUnitMessage::newFromDictionary( 177 + new HarbormasterBuildTarget(), 178 + $this->getModernUnitMessageDictionary($message)); 179 + } 180 + 181 + $diff->attachUnitMessages($messages); 182 + } 183 + } 184 + 185 + private function getModernUnitMessageDictionary(array $map) { 186 + // Strip out `null` values to satisfy stricter typechecks. 187 + foreach ($map as $key => $value) { 188 + if ($value === null) { 189 + unset($map[$key]); 190 + } 191 + } 192 + 193 + return $map; 194 + } 195 + 196 + protected function getDiffTabLabels(array $diffs) { 197 + // Make sure we're only going to render unique diffs. 198 + $diffs = mpull($diffs, null, 'getID'); 199 + $labels = array(pht('Left'), pht('Right')); 200 + 201 + $results = array(); 202 + 203 + foreach ($diffs as $diff) { 204 + if (count($diffs) == 2) { 205 + $label = array_shift($labels); 206 + $label = pht('%s (Diff %d)', $label, $diff->getID()); 207 + } else { 208 + $label = pht('Diff %d', $diff->getID()); 209 + } 210 + 211 + $results[] = array( 212 + $label, 213 + $diff, 214 + ); 215 + } 216 + 217 + return $results; 218 + } 219 + 220 + 95 221 }
+50 -26
src/applications/differential/controller/DifferentialRevisionViewController.php
··· 102 102 } 103 103 } 104 104 105 - $props = id(new DifferentialDiffProperty())->loadAllWhere( 106 - 'diffID = %d', 107 - $target_manual->getID()); 108 - $props = mpull($props, 'getData', 'getName'); 105 + $this->loadDiffProperties($diffs); 106 + $props = $target_manual->getDiffProperties(); 109 107 110 108 $object_phids = array_merge( 111 109 $revision->getReviewers(), ··· 256 254 array($diff_vs, $target->getID())); 257 255 $detail_diffs = mpull($detail_diffs, null, 'getPHID'); 258 256 259 - $buildables = id(new HarbormasterBuildableQuery()) 260 - ->setViewer($viewer) 261 - ->withBuildablePHIDs(array_keys($detail_diffs)) 262 - ->withManualBuildables(false) 263 - ->needBuilds(true) 264 - ->needTargets(true) 265 - ->execute(); 266 - $buildables = mpull($buildables, null, 'getBuildablePHID'); 267 - foreach ($detail_diffs as $diff_phid => $detail_diff) { 268 - $detail_diff->attachBuildable(idx($buildables, $diff_phid)); 269 - } 257 + $this->loadHarbormasterData($detail_diffs); 270 258 271 259 $diff_detail_box = $this->buildDiffDetailView( 272 260 $detail_diffs, 273 261 $revision, 274 262 $field_list); 263 + 264 + $unit_box = $this->buildUnitMessagesView( 265 + $target, 266 + $revision); 275 267 276 268 $comment_view = $this->buildTransactions( 277 269 $revision, ··· 469 461 $operations_box, 470 462 $revision_detail_box, 471 463 $diff_detail_box, 464 + $unit_box, 472 465 $page_pane, 473 466 ); 474 467 ··· 972 965 return null; 973 966 } 974 967 975 - // Make sure we're only going to render unique diffs. 976 - $diffs = mpull($diffs, null, 'getID'); 977 - $labels = array(pht('Left'), pht('Right')); 978 - 979 968 $property_lists = array(); 980 - foreach ($diffs as $diff) { 981 - if (count($diffs) == 2) { 982 - $label = array_shift($labels); 983 - $label = pht('%s (Diff %d)', $label, $diff->getID()); 984 - } else { 985 - $label = pht('Diff %d', $diff->getID()); 986 - } 969 + foreach ($this->getDiffTabLabels($diffs) as $tab) { 970 + list($label, $diff) = $tab; 987 971 988 972 $property_lists[] = array( 989 973 $label, ··· 1082 1066 ->setBoxView($box_view) 1083 1067 ->setOperation($operation); 1084 1068 } 1069 + 1070 + private function buildUnitMessagesView( 1071 + $diff, 1072 + DifferentialRevision $revision) { 1073 + $viewer = $this->getViewer(); 1074 + 1075 + if (!$diff->getUnitMessages()) { 1076 + return null; 1077 + } 1078 + 1079 + $interesting_messages = array(); 1080 + foreach ($diff->getUnitMessages() as $message) { 1081 + switch ($message->getResult()) { 1082 + case ArcanistUnitTestResult::RESULT_PASS: 1083 + case ArcanistUnitTestResult::RESULT_SKIP: 1084 + break; 1085 + default: 1086 + $interesting_messages[] = $message; 1087 + break; 1088 + } 1089 + } 1090 + 1091 + if (!$interesting_messages) { 1092 + return null; 1093 + } 1094 + 1095 + $excuse = null; 1096 + if ($diff->hasDiffProperty('arc:unit-excuse')) { 1097 + $excuse = $diff->getProperty('arc:unit-excuse'); 1098 + } 1099 + 1100 + return id(new HarbormasterUnitSummaryView()) 1101 + ->setUser($viewer) 1102 + ->setExcuse($excuse) 1103 + ->setBuildable($diff->getBuildable()) 1104 + ->setUnitMessages($diff->getUnitMessages()) 1105 + ->setLimit(5) 1106 + ->setShowViewAll(true); 1107 + } 1108 + 1085 1109 1086 1110 }
+3 -88
src/applications/differential/customfield/DifferentialUnitField.php
··· 1 1 <?php 2 2 3 3 final class DifferentialUnitField 4 - extends DifferentialHarbormasterField { 4 + extends DifferentialCustomField { 5 5 6 6 public function getFieldKey() { 7 7 return 'differential:unit'; ··· 31 31 return $this->getFieldName(); 32 32 } 33 33 34 - protected function getLegacyProperty() { 35 - return 'arc:unit'; 36 - } 37 - 38 - protected function getDiffPropertyKeys() { 39 - return array( 40 - 'arc:unit', 41 - 'arc:unit-excuse', 42 - ); 43 - } 44 - 45 - protected function loadHarbormasterTargetMessages(array $target_phids) { 46 - return id(new HarbormasterBuildUnitMessage())->loadAllWhere( 47 - 'buildTargetPHID IN (%Ls)', 48 - $target_phids); 49 - } 50 - 51 - protected function newModernMessage(array $message) { 52 - return HarbormasterBuildUnitMessage::newFromDictionary( 53 - new HarbormasterBuildTarget(), 54 - $this->getModernUnitMessageDictionary($message)); 55 - } 56 - 57 - protected function newHarbormasterMessageView(array $all_messages) { 58 - $messages = $all_messages; 59 - 60 - foreach ($messages as $key => $message) { 61 - switch ($message->getResult()) { 62 - case ArcanistUnitTestResult::RESULT_PASS: 63 - case ArcanistUnitTestResult::RESULT_SKIP: 64 - // Don't show "Pass" or "Skip" in the UI since they aren't very 65 - // interesting. The user can click through to the full results if 66 - // they want details. 67 - unset($messages[$key]); 68 - break; 69 - } 70 - } 71 - 72 - if (!$messages) { 73 - return null; 74 - } 75 - 76 - $table = id(new HarbormasterUnitPropertyView()) 77 - ->setLimit(5) 78 - ->setUnitMessages($all_messages); 79 - 80 - $diff = $this->getObject()->getActiveDiff(); 81 - $buildable = $diff->getBuildable(); 82 - if ($buildable) { 83 - $full_results = '/harbormaster/unit/'.$buildable->getID().'/'; 84 - $table->setFullResultsURI($full_results); 85 - } 86 - 87 - return $table; 88 - } 89 - 90 34 public function getWarningsForDetailView() { 91 35 $status = $this->getObject()->getActiveDiff()->getUnitStatus(); 92 36 ··· 105 49 return $warnings; 106 50 } 107 51 108 - protected function renderHarbormasterStatus( 109 - DifferentialDiff $diff, 110 - array $messages) { 52 + public function renderDiffPropertyViewValue(DifferentialDiff $diff) { 111 53 112 54 $colors = array( 113 55 DifferentialUnitStatus::UNIT_NONE => 'grey', ··· 121 63 122 64 $message = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage($diff); 123 65 124 - $note = array(); 125 - 126 - $excuse = $diff->getProperty('arc:unit-excuse'); 127 - if (strlen($excuse)) { 128 - $excuse = array( 129 - phutil_tag('strong', array(), pht('Excuse:')), 130 - ' ', 131 - phutil_escape_html_newlines($excuse), 132 - ); 133 - $note[] = $excuse; 134 - } 135 - 136 - $note = phutil_implode_html(" \xC2\xB7 ", $note); 137 - 138 66 $status = id(new PHUIStatusListView()) 139 67 ->addItem( 140 68 id(new PHUIStatusItemView()) 141 69 ->setIcon(PHUIStatusItemView::ICON_STAR, $icon_color) 142 - ->setTarget($message) 143 - ->setNote($note)); 70 + ->setTarget($message)); 144 71 145 72 return $status; 146 73 } 147 74 148 - private function getModernUnitMessageDictionary(array $map) { 149 - // Strip out `null` values to satisfy stricter typechecks. 150 - foreach ($map as $key => $value) { 151 - if ($value === null) { 152 - unset($map[$key]); 153 - } 154 - } 155 - 156 - // TODO: Remap more stuff here? 157 - 158 - return $map; 159 - } 160 75 161 76 162 77 }
+27
src/applications/differential/storage/DifferentialDiff.php
··· 40 40 private $properties = array(); 41 41 private $buildable = self::ATTACHABLE; 42 42 43 + private $unitMessages = self::ATTACHABLE; 44 + 43 45 protected function getConfiguration() { 44 46 return array( 45 47 self::CONFIG_AUX_PHID => true, ··· 334 336 return $this->assertAttachedKey($this->properties, $key); 335 337 } 336 338 339 + public function hasDiffProperty($key) { 340 + $properties = $this->getDiffProperties(); 341 + return array_key_exists($key, $properties); 342 + } 343 + 344 + public function attachDiffProperties(array $properties) { 345 + $this->properties = $properties; 346 + return $this; 347 + } 348 + 349 + public function getDiffProperties() { 350 + return $this->assertAttached($this->properties); 351 + } 352 + 337 353 public function attachBuildable(HarbormasterBuildable $buildable = null) { 338 354 $this->buildable = $buildable; 339 355 return $this; ··· 388 404 public function getURI() { 389 405 $id = $this->getID(); 390 406 return "/differential/diff/{$id}/"; 407 + } 408 + 409 + 410 + public function attachUnitMessages(array $unit_messages) { 411 + $this->unitMessages = $unit_messages; 412 + return $this; 413 + } 414 + 415 + 416 + public function getUnitMessages() { 417 + return $this->assertAttached($this->unitMessages); 391 418 } 392 419 393 420
+4 -35
src/applications/harbormaster/controller/HarbormasterBuildableViewController.php
··· 336 336 } 337 337 338 338 if ($unit_data) { 339 - $unit_href = $this->getApplicationURI('unit/'.$buildable->getID().'/'); 340 - 341 - $unit_table = id(new HarbormasterUnitPropertyView()) 342 - ->setUser($viewer) 343 - ->setLimit(5) 339 + $unit = id(new HarbormasterUnitSummaryView()) 340 + ->setBuildable($buildable) 344 341 ->setUnitMessages($unit_data) 345 - ->setFullResultsURI($unit_href); 346 - 347 - $unit_data = msort($unit_data, 'getSortKey'); 348 - $head_unit = head($unit_data); 349 - if ($head_unit) { 350 - $status = $head_unit->getResult(); 351 - 352 - $tag_text = HarbormasterUnitStatus::getUnitStatusLabel($status); 353 - $tag_color = HarbormasterUnitStatus::getUnitStatusColor($status); 354 - $tag_icon = HarbormasterUnitStatus::getUnitStatusIcon($status); 355 - 356 - } else { 357 - $tag_text = pht('No Unit Tests'); 358 - $tag_color = 'grey'; 359 - $tag_icon = 'fa-ban'; 360 - } 361 - 362 - $unit_header = id(new PHUIHeaderView()) 363 - ->setHeader(pht('Unit Tests')) 364 - ->setStatus($tag_icon, $tag_color, $tag_text) 365 - ->addActionLink( 366 - id(new PHUIButtonView()) 367 - ->setTag('a') 368 - ->setHref($unit_href) 369 - ->setIcon('fa-list-ul') 370 - ->setText('View All')); 371 - 372 - $unit = id(new PHUIObjectBoxView()) 373 - ->setHeader($unit_header) 374 - ->setTable($unit_table); 342 + ->setShowViewAll(true) 343 + ->setLimit(5); 375 344 } else { 376 345 $unit = null; 377 346 }
+2 -6
src/applications/harbormaster/controller/HarbormasterUnitMessageListController.php
··· 34 34 $unit_data = array(); 35 35 } 36 36 37 - $unit_table = id(new HarbormasterUnitPropertyView()) 38 - ->setUser($viewer) 37 + $unit = id(new HarbormasterUnitSummaryView()) 38 + ->setBuildable($buildable) 39 39 ->setUnitMessages($unit_data); 40 - 41 - $unit = id(new PHUIObjectBoxView()) 42 - ->setHeaderText(pht('Unit Tests')) 43 - ->setTable($unit_table); 44 40 45 41 $crumbs = $this->buildApplicationCrumbs(); 46 42 $this->addBuildableCrumb($crumbs, $buildable);
+21 -8
src/applications/harbormaster/view/HarbormasterUnitPropertyView.php
··· 6 6 private $unitMessages = array(); 7 7 private $limit; 8 8 private $fullResultsURI; 9 + private $notice; 9 10 10 11 public function setPathURIMap(array $map) { 11 12 $this->pathURIMap = $map; ··· 27 28 $this->fullResultsURI = $full_results_uri; 28 29 return $this; 29 30 } 31 + 32 + public function setNotice($notice) { 33 + $this->notice = $notice; 34 + return $this; 35 + } 36 + 30 37 31 38 public function render() { 32 39 require_celerity_resource('harbormaster-css'); ··· 68 75 $name = $message->getUnitMessageDisplayName(); 69 76 $id = $message->getID(); 70 77 71 - $name = phutil_tag( 72 - 'a', 73 - array( 74 - 'href' => "/harbormaster/unit/view/{$id}/", 75 - ), 76 - $name); 78 + if ($id) { 79 + $name = phutil_tag( 80 + 'a', 81 + array( 82 + 'href' => "/harbormaster/unit/view/{$id}/", 83 + ), 84 + $name); 85 + } 77 86 78 87 $details = $message->getUnitMessageDetails(); 79 88 if (strlen($details)) { ··· 127 136 )) 128 137 ->setColumnClasses( 129 138 array( 130 - 'top', 131 - 'top', 139 + 'top center', 140 + 'top right', 132 141 'top wide', 133 142 )) 134 143 ->setColumnWidths( ··· 141 150 true, 142 151 $any_duration, 143 152 )); 153 + 154 + if ($this->notice) { 155 + $table->setNotice($this->notice); 156 + } 144 157 145 158 return $table; 146 159 }
+104
src/applications/harbormaster/view/HarbormasterUnitSummaryView.php
··· 1 + <?php 2 + 3 + final class HarbormasterUnitSummaryView extends AphrontView { 4 + 5 + private $buildable; 6 + private $messages; 7 + private $limit; 8 + private $excuse; 9 + private $showViewAll; 10 + 11 + public function setBuildable(HarbormasterBuildable $buildable) { 12 + $this->buildable = $buildable; 13 + return $this; 14 + } 15 + 16 + public function setUnitMessages(array $messages) { 17 + $this->messages = $messages; 18 + return $this; 19 + } 20 + 21 + public function setLimit($limit) { 22 + $this->limit = $limit; 23 + return $this; 24 + } 25 + 26 + public function setExcuse($excuse) { 27 + $this->excuse = $excuse; 28 + return $this; 29 + } 30 + 31 + public function setShowViewAll($show_view_all) { 32 + $this->showViewAll = $show_view_all; 33 + return $this; 34 + } 35 + 36 + public function render() { 37 + $messages = $this->messages; 38 + $buildable = $this->buildable; 39 + 40 + $id = $buildable->getID(); 41 + $full_uri = "/harbormaster/unit/{$id}/"; 42 + 43 + $messages = msort($messages, 'getSortKey'); 44 + $head_unit = head($messages); 45 + if ($head_unit) { 46 + $status = $head_unit->getResult(); 47 + 48 + $tag_text = HarbormasterUnitStatus::getUnitStatusLabel($status); 49 + $tag_color = HarbormasterUnitStatus::getUnitStatusColor($status); 50 + $tag_icon = HarbormasterUnitStatus::getUnitStatusIcon($status); 51 + } else { 52 + $tag_text = pht('No Unit Tests'); 53 + $tag_color = 'grey'; 54 + $tag_icon = 'fa-ban'; 55 + } 56 + 57 + $header = id(new PHUIHeaderView()) 58 + ->setHeader(pht('Unit Tests')) 59 + ->setStatus($tag_icon, $tag_color, $tag_text); 60 + 61 + if ($this->showViewAll) { 62 + $view_all = id(new PHUIButtonView()) 63 + ->setTag('a') 64 + ->setHref($full_uri) 65 + ->setIcon('fa-list-ul') 66 + ->setText('View All'); 67 + $header->addActionLink($view_all); 68 + } 69 + 70 + $box = id(new PHUIObjectBoxView()) 71 + ->setHeader($header); 72 + 73 + $table = id(new HarbormasterUnitPropertyView()) 74 + ->setUnitMessages($messages); 75 + 76 + if ($this->showViewAll) { 77 + $table->setFullResultsURI($full_uri); 78 + } 79 + 80 + if ($this->limit) { 81 + $table->setLimit($this->limit); 82 + } 83 + 84 + $excuse = $this->excuse; 85 + if (strlen($excuse)) { 86 + $excuse_icon = id(new PHUIIconView()) 87 + ->setIcon('fa-commenting-o red'); 88 + 89 + $table->setNotice( 90 + array( 91 + $excuse_icon, 92 + ' ', 93 + phutil_tag('strong', array(), pht('Excuse:')), 94 + ' ', 95 + $excuse, 96 + )); 97 + } 98 + 99 + $box->setTable($table); 100 + 101 + return $box; 102 + } 103 + 104 + }
+17 -16
src/view/control/AphrontTableView.php
··· 157 157 $sort_values[] = null; 158 158 } 159 159 160 - if ($this->notice) { 161 - $colspan = max(count(array_filter($visibility)), 1); 162 - $table[] = phutil_tag( 163 - 'tr', 164 - array(), 165 - phutil_tag( 166 - 'td', 167 - array( 168 - 'colspan' => $colspan, 169 - 'class' => 'aphront-table-notice', 170 - ), 171 - $this->notice)); 172 - 173 - } 174 - 175 160 $tr = array(); 176 161 foreach ($headers as $col_num => $header) { 177 162 if (!$visibility[$col_num]) { ··· 350 335 $classes[] = 'aphront-table-view-fixed'; 351 336 } 352 337 338 + $notice = null; 339 + if ($this->notice) { 340 + $notice = phutil_tag( 341 + 'div', 342 + array( 343 + 'class' => 'aphront-table-notice', 344 + ), 345 + $this->notice); 346 + } 347 + 353 348 $html = phutil_tag( 354 349 'table', 355 350 array( 356 351 'class' => implode(' ', $classes), 357 352 ), 358 353 $table); 359 - return phutil_tag_div('aphront-table-wrap', $html); 354 + 355 + return phutil_tag_div( 356 + 'aphront-table-wrap', 357 + array( 358 + $notice, 359 + $html, 360 + )); 360 361 } 361 362 362 363 public static function renderSingleDisplayLine($line) {
+5 -1
webroot/rsrc/css/aphront/table-view.css
··· 19 19 table-layout: fixed; 20 20 } 21 21 22 - .aphront-table-view td.aphront-table-notice { 22 + .aphront-table-view-fixed th { 23 + box-sizing: border-box; 24 + } 25 + 26 + .aphront-table-notice { 23 27 padding: 12px 16px; 24 28 font-size: {$normalfontsize}; 25 29 color: {$darkbluetext};