diff --git a/includes/common.inc b/includes/common.inc index 581e115..dd4a2fd 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -6459,7 +6459,7 @@ function drupal_common_theme() { 'variables' => array('type' => MARK_NEW), ), 'item_list' => array( - 'variables' => array('items' => array(), 'title' => NULL, 'type' => 'ul', 'attributes' => array()), + 'variables' => array('items' => array(), 'title' => '', 'type' => 'ul', 'attributes' => array()), ), 'more_help_link' => array( 'variables' => array('url' => NULL), diff --git a/includes/theme.inc b/includes/theme.inc index 1f991ea..b85a35c 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -1793,12 +1793,14 @@ function theme_mark($variables) { * * @param $variables * An associative array containing: - * - items: An array of items to be displayed in the list. If an item is a - * string, then it is used as is. If an item is an array, then the "data" - * element of the array is used as the contents of the list item. If an item - * is an array with a "children" element, those children are displayed in a - * nested list. All other elements are treated as attributes of the list - * item element. + * - items: A list of items to render. String values are rendered as is. Each + * item can also be an associative array containing: + * - data: The string content of the list item. + * - children: A list of nested child items to render that behave + * identically to 'items', but any non-numeric string keys are treated as + * HTML attributes for the child list that wraps 'children'. + * Any other key/value pairs are used as HTML attributes for the list item + * in 'data'. * - title: The title of the list. * - type: The type of list to return (e.g. "ul", "ol"). * - attributes: The attributes applied to the list element. @@ -1807,51 +1809,69 @@ function theme_item_list($variables) { $items = $variables['items']; $title = $variables['title']; $type = $variables['type']; - $attributes = $variables['attributes']; + $list_attributes = $variables['attributes']; - $output = '
'; - if (isset($title)) { - $output .= '

' . $title . '

'; - } + $output = ''; + if ($items) { + $output .= '<' . $type . drupal_attributes($list_attributes) . '>'; - if (!empty($items)) { - $output .= "<$type" . drupal_attributes($attributes) . '>'; $num_items = count($items); - foreach ($items as $i => $item) { + $i = 0; + foreach ($items as $key => $item) { + $i++; $attributes = array(); - $children = array(); - $data = ''; + if (is_array($item)) { - foreach ($item as $key => $value) { - if ($key == 'data') { - $data = $value; - } - elseif ($key == 'children') { - $children = $value; - } - else { - $attributes[$key] = $value; + $value = ''; + if (isset($item['data'])) { + $value .= $item['data']; + } + $attributes = array_diff_key($item, array('data' => 0, 'children' => 0)); + + // Append nested child list, if any. + if (isset($item['children'])) { + // HTML attributes for the outer list are defined in the 'attributes' + // theme variable, but not inherited by children. For nested lists, + // all non-numeric keys in 'children' are used as list attributes. + $child_list_attributes = array(); + foreach ($item['children'] as $child_key => $child_item) { + if (is_string($child_key)) { + $child_list_attributes[$child_key] = $child_item; + unset($item['children'][$child_key]); + } } + $value .= theme('item_list', array( + 'items' => $item['children'], + 'type' => $type, + 'attributes' => $child_list_attributes, + )); } } else { - $data = $item; - } - if (count($children) > 0) { - // Render nested list. - $data .= theme_item_list(array('items' => $children, 'title' => NULL, 'type' => $type, 'attributes' => $attributes)); + $value = $item; } - if ($i == 0) { + + $attributes['class'][] = ($i % 2 ? 'odd' : 'even'); + if ($i == 1) { $attributes['class'][] = 'first'; } - if ($i == $num_items - 1) { + if ($i == $num_items) { $attributes['class'][] = 'last'; } - $output .= '' . $data . "\n"; + + $output .= '' . $value . ''; } $output .= ""; } - $output .= '
'; + + // Only output the list container and title, if there are any list items. + if ($output !== '') { + if ($title !== '') { + $title = '

' . $title . '

'; + } + $output = '
' . $title . $output . '
'; + } + return $output; } diff --git a/modules/simpletest/tests/theme.test b/modules/simpletest/tests/theme.test index 53557e3..d064359 100644 --- a/modules/simpletest/tests/theme.test +++ b/modules/simpletest/tests/theme.test @@ -164,30 +164,101 @@ class ThemeTableUnitTest extends DrupalWebTestCase { } /** - * Unit tests for theme_item_list(). + * Tests for common theme functions. */ -class ThemeItemListUnitTest extends DrupalWebTestCase { +class ThemeFunctionsTestCase extends DrupalWebTestCase { + protected $profile = 'testing'; + public static function getInfo() { return array( - 'name' => 'Theme item list', - 'description' => 'Test the theme_item_list() function.', + 'name' => 'Theme functions', + 'description' => 'Tests common theme functions.', 'group' => 'Theme', ); } /** - * Test nested list rendering. + * Tests theme_item_list(). */ - function testNestedList() { - $items = array('a', array('data' => 'b', 'children' => array('c', 'd')), 'e'); - $expected = '
'; - $output = theme('item_list', array('items' => $items)); - $this->assertIdentical($expected, $output, 'Nested list is rendered correctly.'); + function testItemList() { + // Verify that empty variables produce no output. + $variables = array(); + $expected = ''; + $this->assertThemeOutput('item_list', $variables, $expected, 'Empty %callback generates no output.'); + + $variables = array(); + $variables['title'] = 'Some title'; + $expected = ''; + $this->assertThemeOutput('item_list', $variables, $expected, 'Empty %callback with title generates no output.'); + + // Verify nested item lists. + $variables = array(); + $variables['title'] = 'Some title'; + $variables['attributes'] = array( + 'id' => 'parentlist', + ); + $variables['items'] = array( + 'a', + array( + 'data' => 'b', + 'children' => array( + 'c', + // Nested children may use additional attributes. + array( + 'data' => 'd', + 'class' => array('dee'), + ), + // Any string key is treated as child list attribute. + 'id' => 'childlist', + ), + // Any other keys are treated as item attributes. + 'id' => 'bee', + ), + array( + 'data' => 'e', + 'id' => 'E', + ), + ); + $inner = '
'; + + $expected = '
'; + $expected .= '

Some title

'; + $expected .= '
'; + + $this->assertThemeOutput('item_list', $variables, $expected); + } + + /** + * Asserts themed output. + * + * @param $callback + * The name of the theme function to invoke; e.g. 'links' for theme_links(). + * @param $variables + * An array of variables to pass to the theme function. + * @param $expected + * The expected themed output string. + * @param $message + * (optional) An assertion message. + */ + protected function assertThemeOutput($callback, array $variables = array(), $expected, $message = '') { + $output = theme($callback, $variables); + $this->verbose('Variables:' . '
' .  check_plain(var_export($variables, TRUE)) . '
' + . '
' . 'Result:' . '
' .  check_plain(var_export($output, TRUE)) . '
' + . '
' . 'Expected:' . '
' .  check_plain(var_export($expected, TRUE)) . '
' + . '
' . $output + ); + if (!$message) { + $message = '%callback rendered correctly.'; + } + $message = t($message, array('%callback' => 'theme_' . $callback . '()')); + $this->assertIdentical($output, $expected, $message); } }