Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix/15161 nested elements and error summary #15165

Merged
merged 11 commits into from
Jul 2, 2024
1 change: 1 addition & 0 deletions CHANGELOG-WIP.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Entry and category conditions now have a “Has Descendants” rule. ([#15276](https://github.com/craftcms/cms/discussions/15276))
- “Replace file” actions now display success notices on complete. ([#15217](https://github.com/craftcms/cms/issues/15217))
- Double-clicking on folders within asset indexes and folder selection modals now navigates the index/modal into the folder. ([#15238](https://github.com/craftcms/cms/discussions/15238))
- Matrix fields now show validation errors when nested entries don’t validate. ([#15161](https://github.com/craftcms/cms/issues/15161), [#15165](https://github.com/craftcms/cms/pull/15165))

### Accessibility
- Improved the accessibility of two-step verification setup. ([#15229](https://github.com/craftcms/cms/pull/15229))
Expand Down
12 changes: 11 additions & 1 deletion src/base/Element.php
Original file line number Diff line number Diff line change
Expand Up @@ -1180,11 +1180,12 @@ public static function indexHtml(
bool $selectable,
bool $sortable,
): string {
$request = Craft::$app->getRequest();
$variables = [
'viewMode' => $viewState['mode'],
'context' => $context,
'disabledElementIds' => $disabledElementIds,
'collapsedElementIds' => Craft::$app->getRequest()->getParam('collapsedElementIds'),
'collapsedElementIds' => $request->getParam('collapsedElementIds'),
'selectable' => $selectable,
'sortable' => $sortable,
'showHeaderColumn' => $viewState['showHeaderColumn'] ?? false,
Expand Down Expand Up @@ -1281,6 +1282,15 @@ public static function indexHtml(
// See if there are any provisional drafts we should swap these out with
ElementHelper::swapInProvisionalDrafts($elements);

if ($request->getParam('prevalidate')) {
foreach ($elements as $element) {
if ($element->enabled && $element->getEnabledForSite()) {
$element->setScenario(Element::SCENARIO_LIVE);
}
$element->validate();
}
}

$variables['elements'] = $elements;
$template = '_elements/' . $viewState['mode'] . 'view/' . ($includeContainer ? 'container' : 'elements');

Expand Down
11 changes: 0 additions & 11 deletions src/controllers/ElementsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
use craft\events\DefineElementEditorHtmlEvent;
use craft\events\DraftEvent;
use craft\fieldlayoutelements\BaseField;
use craft\fieldlayoutelements\CustomField;
use craft\fields\Matrix;
use craft\helpers\ArrayHelper;
use craft\helpers\Component;
Expand Down Expand Up @@ -1059,16 +1058,6 @@ private function _errorSummary(ElementInterface $element): string
foreach ($tab->getElements() as $layoutElement) {
if ($layoutElement instanceof BaseField && $layoutElement->attribute() === $fieldKey) {
$tabUid = $tab->uid;

if ($layoutElement instanceof CustomField) {
$field = $layoutElement->getField();
// if we're dealing with a Matrix field, we only want to show the errors
// if that field is set to inline blocks view mode;
if ($field instanceof Matrix && $field->viewMode !== $field::VIEW_MODE_BLOCKS) {
$error = null;
}
}

continue 2;
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/elements/NestedElementManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ function(string $id, array $config, $attribute, &$settings) use ($owner) {
'class' => [
'elements',
$config['showInGrid'] ? 'card-grid' : 'cards',
$config['prevalidate'] ? 'prevalidate' : '',
],
]);
}
Expand Down Expand Up @@ -529,6 +530,8 @@ function(string $id, array $config, string $attribute, array &$settings) use ($o
'fieldLayouts' => $config['fieldLayouts'],
'defaultTableColumns' => $config['defaultTableColumns'],
'registerJs' => false,
'class' => [$config['prevalidate'] ? 'prevalidate' : ''],
'prevalidate' => $config['prevalidate'] ?? false,
]);
},
);
Expand Down Expand Up @@ -598,6 +601,7 @@ private function createView(?ElementInterface $owner, array $config, string $mod
'ownerIdParam' => $this->ownerIdParam,
'fieldHandle' => $this->field?->handle,
'baseInputName' => $view->getNamespace(),
'prevalidate' => $config['prevalidate'] ?? false,
];

if (!empty($config['createAttributes'])) {
Expand Down
20 changes: 19 additions & 1 deletion src/fields/Matrix.php
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,7 @@ private function nestedElementManagerHtml(?ElementInterface $owner, bool $static
$entryTypes = $this->getEntryTypes();
$config = [
'showInGrid' => $this->showCardsInGrid,
'prevalidate' => false,
];

if (!$static) {
Expand All @@ -912,6 +913,10 @@ private function nestedElementManagerHtml(?ElementInterface $owner, bool $static
'minElements' => $this->minEntries,
'maxElements' => $this->maxEntries,
];

if ($owner->hasErrors($this->handle)) {
$config['prevalidate'] = true;
}
}

if ($this->viewMode === self::VIEW_MODE_CARDS) {
Expand Down Expand Up @@ -1005,7 +1010,20 @@ private function validateEntries(ElementInterface $element): void

if (!$entry->validate()) {
$key = $entry->uid ?? sprintf('new%s', ++$new);
$element->addModelErrors($entry, sprintf('%s[%s]', $this->handle, $key));
// we only want to show the nested entries errors when the matrix field is in blocks view mode;
if ($this->viewMode === self::VIEW_MODE_BLOCKS) {
$element->addModelErrors($entry, sprintf('%s[%s]', $this->handle, $key));
} else {
// in other modes, we want to show a top level error to let users know
// that there are validation errors in the nested element
$element->addError(
$this->handle,
Craft::t('app', 'Validation errors found in the “{title}“ entry in the *{fieldName}* field. Please fix them.', [
'title' => $entry->title,
'fieldName' => $this->getUiLabel(),
])
);
}
$allEntriesValidate = false;
}
}
Expand Down
8 changes: 7 additions & 1 deletion src/helpers/Cp.php
Original file line number Diff line number Diff line change
Expand Up @@ -548,10 +548,15 @@ public static function elementCardHtml(ElementInterface $element, array $config

$color = $element instanceof Colorable ? $element->getColor() : null;

$classes = ['card'];
if ($element->hasErrors()) {
$classes[] = 'error';
}

$attributes = ArrayHelper::merge(
self::baseElementAttributes($element, $config),
[
'class' => ['card'],
'class' => $classes,
'style' => array_filter([
'--custom-bg-color' => $color?->cssVar(50),
'--custom-text-color' => $color?->cssVar(900),
Expand Down Expand Up @@ -1208,6 +1213,7 @@ public static function elementIndexHtml(string $elementType, array $config = [])
[
'context' => $config['context'],
'namespace' => $view->getNamespace(),
'prevalidate' => $config['prevalidate'] ?? false,
],
$config['jsSettings']
),
Expand Down
1 change: 1 addition & 0 deletions src/translations/en/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -1820,6 +1820,7 @@
'Validate custom fields on public registration' => 'Validate custom fields on public registration',
'Validate related {type}' => 'Validate related {type}',
'Validation errors for site: “{siteName}“' => 'Validation errors for site: “{siteName}“',
'Validation errors found in the “{title}“ entry in the *{fieldName}* field. Please fix them.' => 'Validation errors found in the “{title}“ entry in the *{fieldName}* field. Please fix them.',
'Validation errors found in {attribute} {type}; please fix them.' => 'Validation errors found in {attribute} {type}; please fix them.',
'Value prefixed by “{prefix}”.' => 'Value prefixed by “{prefix}”.',
'Value suffixed by “{suffix}”.' => 'Value suffixed by “{suffix}”.',
Expand Down
2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/cp.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/cp.js.map

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/web/assets/cp/src/js/BaseElementIndex.js
Original file line number Diff line number Diff line change
Expand Up @@ -1561,6 +1561,7 @@ Craft.BaseElementIndex = Garnish.Base.extend(
paginated: this.paginated,
selectable: this.selectable,
sortable: this.sortable,
prevalidate: this.settings.prevalidate,
};

params.viewState.showHeaderColumn = this.settings.showHeaderColumn;
Expand Down Expand Up @@ -3761,6 +3762,7 @@ Craft.BaseElementIndex = Garnish.Base.extend(
onBeforeDeleteElements: async (selectedItems) => {},
onDeleteElements: async (selectedItems) => {},
sortable: false,
prevalidate: false,
inlineEditable: null,
actions: null,
buttonContainer: null,
Expand Down
1 change: 1 addition & 0 deletions src/web/assets/cp/src/js/ElementEditorSlideout.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Craft.ElementEditorSlideout = Craft.CpScreenSlideout.extend(
settings,
{
showHeader: true,
prevalidate: this.$element.parents('.prevalidate').length > 0,
}
);
this.base('elements/edit', settings);
Expand Down
2 changes: 2 additions & 0 deletions src/web/assets/cp/src/js/NestedElementManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ Craft.NestedElementManager = Garnish.Base.extend(
{
context: 'embedded-index',
sortable: this.settings.sortable,
prevalidate: this.settings.prevalidate,
},
this.settings.indexSettings,
{
Expand Down Expand Up @@ -529,6 +530,7 @@ Craft.NestedElementManager = Garnish.Base.extend(
baseInputName: null,
deleteLabel: null,
deleteConfirmationMessage: null,
prevalidate: false,
},
}
);
2 changes: 2 additions & 0 deletions src/web/assets/cp/src/js/UI.js
Original file line number Diff line number Diff line change
Expand Up @@ -1297,6 +1297,8 @@ Craft.ui = {
return;
}

this.clearErrorsFromField($field);

$field.addClass('has-errors');
$field.children('.input').addClass('errors');

Expand Down
Loading