diff --git a/core/lib/Drupal/Core/Render/Element/RenderElement.php b/core/lib/Drupal/Core/Render/Element/RenderElement.php index 243a9af..e7b48dc 100644 --- a/core/lib/Drupal/Core/Render/Element/RenderElement.php +++ b/core/lib/Drupal/Core/Render/Element/RenderElement.php @@ -204,6 +204,15 @@ public static function preRenderGroup($element) { } } + // Set attributes to indicate that there is a child with an error. + if (!empty($element['#children_errors'])) { + $element['#attributes']['class'][] = 'form-wrapper--child-error'; + if ($element['#type'] === 'details') { + $element['#attributes']['class'][] = 'details--child-error'; + } + $element['#attributes']['data-children-errors-count'] = count($element['#children_errors']); + } + return $element; } diff --git a/core/misc/vertical-tabs.es6.js b/core/misc/vertical-tabs.es6.js index c7ad2fd..1c1c28d 100644 --- a/core/misc/vertical-tabs.es6.js +++ b/core/misc/vertical-tabs.es6.js @@ -60,7 +60,8 @@ var $that = $(this); var vertical_tab = new Drupal.verticalTab({ title: $that.find('> summary').text(), - details: $that + details: $that, + children_errors: $that.data('children-errors-count') }); tab_list.append(vertical_tab.item); $that @@ -246,6 +247,11 @@ .append(tab.summary = $('') ) ); + + if (settings.children_errors) { + tab.item.addClass('vertical-tab__menu-item--child-error'); + } + return tab; }; diff --git a/core/misc/vertical-tabs.js b/core/misc/vertical-tabs.js index c47a7cb..0eb23da 100644 --- a/core/misc/vertical-tabs.js +++ b/core/misc/vertical-tabs.js @@ -35,7 +35,8 @@ var $that = $(this); var vertical_tab = new Drupal.verticalTab({ title: $that.find('> summary').text(), - details: $that + details: $that, + children_errors: $that.data('children-errors-count') }); tab_list.append(vertical_tab.item); $that.removeClass('collapsed').attr('open', true).addClass('vertical-tabs__pane').data('verticalTab', vertical_tab); @@ -137,6 +138,11 @@ Drupal.theme.verticalTab = function (settings) { var tab = {}; tab.item = $('
  • ').append(tab.link = $('').append(tab.title = $('').text(settings.title)).append(tab.summary = $(''))); + + if (settings.children_errors) { + tab.item.addClass('vertical-tab__menu-item--child-error'); + } + return tab; }; })(jQuery, Drupal, drupalSettings); \ No newline at end of file diff --git a/core/modules/node/tests/src/Functional/NodeEditFormTest.php b/core/modules/node/tests/src/Functional/NodeEditFormTest.php index ce032e5..13d04a0 100644 --- a/core/modules/node/tests/src/Functional/NodeEditFormTest.php +++ b/core/modules/node/tests/src/Functional/NodeEditFormTest.php @@ -126,7 +126,7 @@ public function testNodeEdit() { $open_details_elements = count($this->cssSelect('details[open="open"]')); $this->drupalPostForm(NULL, $edit, t('Save')); // The node author details must be open. - $this->assertRaw('
    '); + $this->assertRaw('
    '); // Only one extra details element should now be open. $open_details_elements++; $this->assertEqual(count($this->cssSelect('details[open="open"]')), $open_details_elements, 'Exactly one extra open <details> element found.'); diff --git a/core/themes/bartik/bartik.libraries.yml b/core/themes/bartik/bartik.libraries.yml index 3bbecd9..bace5d0 100644 --- a/core/themes/bartik/bartik.libraries.yml +++ b/core/themes/bartik/bartik.libraries.yml @@ -10,6 +10,7 @@ global-styling: css/components/captions.css: {} css/components/comments.css: {} css/components/contextual.css: {} + css/components/details.css: {} css/components/demo-block.css: {} # @see https://www.drupal.org/node/2389735 css/components/dropbutton.component.css: {} diff --git a/core/themes/bartik/css/components/details.css b/core/themes/bartik/css/components/details.css new file mode 100644 index 0000000..2277e9d --- /dev/null +++ b/core/themes/bartik/css/components/details.css @@ -0,0 +1,33 @@ +/** + * @file + * Collapsible details. + * + * @see collapse.js + */ + +/* Errors */ +.details--child-error:not([open]) summary { + background-color: #f1f1f1; +} +.details--child-error:not([open]) summary:before { + content: ''; + display: inline-block; + height: 20px; + width: 14px; + background: url(../../../../misc/icons/e32700/error.svg) no-repeat center; + background-size: contain; + float: right; /* LTR */ +} +[dir="rtl"] .details--child-error:not([open]) summary:before { + float: left; +} +.details--child-error:not([open]) { + border: 2px solid red; + box-shadow: inset 7px 0 0 red; /* LTR */ + padding-left: 7px; /* LTR */ +} +[dir="rtl"] .details--child-error:not([open]) { + box-shadow: inset -7px 0 0 red; + padding-left: 0; + padding-right: 7px; +} diff --git a/core/themes/bartik/css/components/vertical-tabs.component.css b/core/themes/bartik/css/components/vertical-tabs.component.css index ce4d6cd..95cd421 100644 --- a/core/themes/bartik/css/components/vertical-tabs.component.css +++ b/core/themes/bartik/css/components/vertical-tabs.component.css @@ -14,3 +14,40 @@ /* This is required to win specificity over [dir="rtl"] .region-content ul */ padding: 0; } +.vertical-tab__menu-item--child-error.is-selected { + padding-top: 1px; +} +.vertical-tab__menu-item--child-error:not(.is-selected) { + border-top: 1px solid red; + background-color: #f1f1f1; + box-shadow: inset 7px 0 0 red; /* LTR */ + border-radius: 2px 0 0 2px; /* LTR */ + padding-left: 7px; /* LTR */ +} +.vertical-tab__menu-item--child-error:not(.is-selected) a { + padding-left: 7px; /* LTR */ + border-bottom: 1px solid red; + color: #e32700; +} +[dir="rtl"] .vertical-tab__menu-item--child-error:not(.is-selected) { + box-shadow: inset -7px 0 0 red; + border-radius: 0 2px 2px 0; + padding-left: 0; + padding-right: 7px; +} +[dir="rtl"] .vertical-tab__menu-item--child-error:not(.is-selected) a { + padding-left: 7px; + padding-right: 7px; +} +.vertical-tab__menu-item--child-error:not(.is-selected) .vertical-tabs__menu-item-title:before { + content: ''; + display: inline-block; + height: 20px; + width: 14px; + background: url(../../../../misc/icons/e32700/error.svg) no-repeat center; + background-size: contain; + float: right; +} +[dir="rtl"] .vertical-tab__menu-item--child-error:not(.is-selected) .vertical-tabs__menu-item-title:before { + float: left; +} diff --git a/core/themes/seven/css/components/details.css b/core/themes/seven/css/components/details.css new file mode 100644 index 0000000..07d33be --- /dev/null +++ b/core/themes/seven/css/components/details.css @@ -0,0 +1,33 @@ +/** + * @file + * Collapsible details. + * + * @see collapse.js + */ + +/* Errors */ +.details--child-error:not([open]) summary:before { + content: ''; + display: inline-block; + height: 16px; + width: 14px; + background: url(../../../../misc/icons/e32700/error.svg) no-repeat center; + background-size: contain; + float: right; /* LTR */ +} +[dir="rtl"] .details--child-error:not([open]) summary:before { + float: left; +} +.details--child-error:not([open]) { + border: 1px solid #e62600; + box-shadow: inset 7px 0 0 #e62600; /* LTR */ + border-radius: 2px 0 0 2px; /* LTR */ + padding-left: 7px; /* LTR */ + color: #e62600; +} +[dir="rtl"] .details--child-error:not([open]) { + box-shadow: inset -7px 0 0 #e62600; + border-radius: 0 2px 2px 0; + padding-left: 0; + padding-right: 7px; +} diff --git a/core/themes/seven/css/components/entity-meta.css b/core/themes/seven/css/components/entity-meta.css index 701e8dc..a0f6151 100644 --- a/core/themes/seven/css/components/entity-meta.css +++ b/core/themes/seven/css/components/entity-meta.css @@ -57,3 +57,21 @@ .entity-meta details .summary { display: none; /* Hide JS summaries. @todo Rethink summaries. */ } +.entity-meta .details--child-error:not([open]) { + padding-left: 6px; +} +.entity-meta .details--child-error:not([open]) > * { + position: relative; + left: -7px; + margin-right: -12px; +} +[dir="rtl"] .entity-meta details.error:not([open]) { + padding-left: 0; + padding-right: 7px; +} +[dir="rtl"] .entity-meta details.error:not([open]) > * { + right: -8px; + left: auto; + margin-left: -12px; + margin-right: 0; +} diff --git a/core/themes/seven/css/components/vertical-tabs.css b/core/themes/seven/css/components/vertical-tabs.css index 34b5a52..cefae6d 100644 --- a/core/themes/seven/css/components/vertical-tabs.css +++ b/core/themes/seven/css/components/vertical-tabs.css @@ -37,6 +37,44 @@ .vertical-tabs__menu-item.last { border-bottom: none; } +.vertical-tab__menu-item--child-error.is-selected { + padding-top: 1px; +} +.vertical-tab__menu-item--child-error:not(.is-selected) { + border: 1px solid #e62600; + border-right: 0; + border-bottom-color: #ccc; + box-shadow: inset 7px 0 0 #e62600; /* LTR */ + border-radius: 2px 0 0 2px; /* LTR */ + padding-left: 7px; /* LTR */ +} +.vertical-tab__menu-item--child-error:not(.is-selected) a { + padding-left: 7px; /* LTR */ + padding-right: 10px; + border-bottom: 1px solid #e62600; + color: #e62600; +} +[dir="rtl"] .vertical-tab__menu-item--child-error:not(.is-selected) { + box-shadow: inset -7px 0 0 #e62600; + border-radius: 0 2px 2px 0; + padding-left: 0; + padding-right: 7px; +} +[dir="rtl"] .vertical-tab__menu-item--child-error:not(.is-selected) a { + padding-right: 7px; +} +.vertical-tab__menu-item--child-error:not(.is-selected) .vertical-tabs__menu-item-title:before { + content: ''; + display: inline-block; + height: 14px; + width: 14px; + background: url(../../../../misc/icons/e32700/error.svg) no-repeat center; + background-size: contain; + float: right; +} +[dir="rtl"] .vertical-tab__menu-item--child-error:not(.is-selected) .vertical-tabs__menu-item-title:before { + float: left; +} [dir="rtl"] .vertical-tabs__menu-item.is-selected { border-left: 1px solid #fcfcfa; border-right: none; diff --git a/core/themes/seven/seven.libraries.yml b/core/themes/seven/seven.libraries.yml index 424e7f5..2010f6d 100644 --- a/core/themes/seven/seven.libraries.yml +++ b/core/themes/seven/seven.libraries.yml @@ -12,6 +12,7 @@ global-styling: css/components/buttons.css: {} css/components/colors.css: {} css/components/messages.css: {} + css/components/details.css: {} css/components/dropbutton.component.css: {} css/components/entity-meta.css: {} css/components/field-ui.css: {}