diff --git a/src/applications/uiexample/examples/PhabricatorPropertyListExample.php b/src/applications/uiexample/examples/PhabricatorPropertyListExample.php
index b5ee7c2d88..68e1449aa6 100644
--- a/src/applications/uiexample/examples/PhabricatorPropertyListExample.php
+++ b/src/applications/uiexample/examples/PhabricatorPropertyListExample.php
@@ -35,6 +35,90 @@ final class PhabricatorPropertyListExample extends PhabricatorUIExample {
'viverra. Nunc tempus tempor quam id iaculis. Maecenas lectus '.
'velit, aliquam et consequat quis, tincidunt id dolor.');
- return $view;
+
+ $view->addSectionHeader('Colors of the Rainbow');
+
+ $view->addProperty('R', 'Red');
+ $view->addProperty('O', 'Orange');
+ $view->addProperty('Y', 'Yellow');
+ $view->addProperty('G', 'Green');
+ $view->addProperty('B', 'Blue');
+ $view->addProperty('I', 'Indigo');
+ $view->addProperty('V', 'Violet');
+
+ $view->addSectionHeader('Haiku About Pasta');
+
+ $view->addTextContent(
+ 'this is a pasta
'.
+ 'haiku. it is very bad.
'.
+ 'what did you expect?');
+
+ $edge_cases_header = id(new PhabricatorHeaderView())
+ ->setHeader(pht('Edge Cases'));
+
+ $edge_cases_view = new PhabricatorPropertyListView();
+
+ $edge_cases_view->addProperty(
+ pht('Description'),
+ pht('These layouts test UI edge cases in the element. This block '.
+ 'tests wrapping and overflow behavior.'));
+
+ $edge_cases_view->addProperty(
+ pht('A Very Very Very Very Very Very Very Very Very Long Property Label'),
+ pht('This property label and property value are quite long. They '.
+ 'demonstrate the wrapping behavior of the element, or lack thereof '.
+ 'if something terrible has happened.'));
+
+ $edge_cases_view->addProperty(
+ pht('AVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongUnbrokenPropertyLabel'),
+ pht('Thispropertylabelandpropertyvaluearequitelongandhave'.
+ 'nospacestheydemonstratetheoverflowbehavioroftheelement'.
+ 'orlackthereof.'));
+
+
+ $edge_cases_view->addProperty(
+ pht('Description'),
+ pht('The next section is an empty text block.'));
+
+ $edge_cases_view->addTextContent('');
+
+ $edge_cases_view->addProperty(
+ pht('Description'),
+ pht('This section should have multiple properties with the same name.'));
+
+ $edge_cases_view->addProperty(
+ pht('Joe'),
+ pht('Smith'));
+ $edge_cases_view->addProperty(
+ pht('Joe'),
+ pht('Smith'));
+ $edge_cases_view->addProperty(
+ pht('Joe'),
+ pht('Smith'));
+
+ $edge_cases_view->addProperty(
+ pht('Description'),
+ pht('The next section shows adjacent section headers.'));
+
+ $edge_cases_view->addSectionHeader('Several');
+ $edge_cases_view->addSectionHeader('Adjacent');
+ $edge_cases_view->addSectionHeader('Section');
+ $edge_cases_view->addSectionHeader('Headers');
+
+ $edge_cases_view->addProperty(
+ pht('Description'),
+ pht('The next section is several adjacent text blocks.'));
+
+ $edge_cases_view->addTextContent('Lorem');
+ $edge_cases_view->addTextContent('ipsum');
+ $edge_cases_view->addTextContent('dolor');
+ $edge_cases_view->addTextContent('sit');
+ $edge_cases_view->addTextContent('amet...');
+
+ return array(
+ $view,
+ $edge_cases_header,
+ $edge_cases_view,
+ );
}
}
diff --git a/src/view/AphrontView.php b/src/view/AphrontView.php
index d0377bc12d..b1ac91f7f0 100644
--- a/src/view/AphrontView.php
+++ b/src/view/AphrontView.php
@@ -4,7 +4,16 @@ abstract class AphrontView {
protected $children = array();
+ protected function canAppendChild() {
+ return true;
+ }
+
final public function appendChild($child) {
+ if (!$this->canAppendChild()) {
+ $class = get_class($this);
+ throw new Exception(
+ "View '{$class}' does not support children.");
+ }
$this->children[] = $child;
return $this;
}
diff --git a/src/view/layout/PhabricatorPropertyListView.php b/src/view/layout/PhabricatorPropertyListView.php
index 8e6d765870..ba3191e5ad 100644
--- a/src/view/layout/PhabricatorPropertyListView.php
+++ b/src/view/layout/PhabricatorPropertyListView.php
@@ -2,56 +2,69 @@
final class PhabricatorPropertyListView extends AphrontView {
- private $properties = array();
+ private $parts = array();
+
+ protected function canAppendChild() {
+ return false;
+ }
public function addProperty($key, $value) {
- $this->properties[$key] = $value;
+ $current = array_pop($this->parts);
+
+ if (!$current || $current['type'] != 'property') {
+ if ($current) {
+ $this->parts[] = $current;
+ }
+ $current = array(
+ 'type' => 'property',
+ 'list' => array(),
+ );
+ }
+
+ $current['list'][] = array(
+ 'key' => $key,
+ 'value' => $value,
+ );
+
+ $this->parts[] = $current;
+ return $this;
+ }
+
+ public function addSectionHeader($name) {
+ $this->parts[] = array(
+ 'type' => 'section',
+ 'name' => $name,
+ );
return $this;
}
public function addTextContent($content) {
- return $this->appendChild(
- phutil_render_tag(
- 'div',
- array(
- 'class' => 'phabricator-property-list-text-content',
- ),
- $content));
+ $this->parts[] = array(
+ 'type' => 'text',
+ 'content' => $content,
+ );
+ return $this;
}
public function render() {
require_celerity_resource('phabricator-property-list-view-css');
$items = array();
- foreach ($this->properties as $key => $value) {
- $items[] = phutil_render_tag(
- 'dt',
- array(
- 'class' => 'phabricator-property-key',
- ),
- phutil_escape_html($key));
- $items[] = phutil_render_tag(
- 'dd',
- array(
- 'class' => 'phabricator-property-value',
- ),
- $this->renderSingleView($value));
- }
-
- $list = phutil_render_tag(
- 'dl',
- array(
- ),
- $this->renderSingleView($items));
-
- $content = $this->renderChildren();
- if (strlen($content)) {
- $content = phutil_render_tag(
- 'div',
- array(
- 'class' => 'phabricator-property-list-content',
- ),
- $content);
+ foreach ($this->parts as $part) {
+ $type = $part['type'];
+ switch ($type) {
+ case 'property':
+ $items[] = $this->renderPropertyPart($part);
+ break;
+ case 'section':
+ $items[] = $this->renderSectionPart($part);
+ break;
+ case 'text':
+ $items[] = $this->renderTextPart($part);
+ break;
+ default:
+ throw new Exception("Unknown part type '{$type}'!");
+ }
}
return phutil_render_tag(
@@ -59,14 +72,61 @@ final class PhabricatorPropertyListView extends AphrontView {
array(
'class' => 'phabricator-property-list-view',
),
- $list.
- // NOTE: We need this (which is basically a "clear: both;" div) to make
- // sure the property list is taller than the action list for objects with
- // few properties but many actions. Otherwise, the action list may
- // obscure the document content.
- '