diff --git a/modules/ui_patterns_devel/src/Component/ComponentValidatorSilencer.php b/modules/ui_patterns_devel/src/Component/ComponentValidatorSilencer.php index 19f2d2bb0f9bd58e1cfd7e447835b87718943814..c557c6f13692943dd8474fd18668c6e313f2c1ec 100644 --- a/modules/ui_patterns_devel/src/Component/ComponentValidatorSilencer.php +++ b/modules/ui_patterns_devel/src/Component/ComponentValidatorSilencer.php @@ -22,7 +22,7 @@ final class ComponentValidatorSilencer extends ComponentValidator { * TRUE if schema definitions are mandatory. * * @return bool - * Always TRUE to display the error as a message. + * Always TRUE to bypass the Exception. */ public function validateDefinition(array $definition, bool $enforce_schemas): bool { try { @@ -44,7 +44,7 @@ final class ComponentValidatorSilencer extends ComponentValidator { * The component to validate the props against. * * @return bool - * Always TRUE to display the error as a message. + * Always TRUE to bypass the Exception. */ public function validateProps(array $context, Component $component): bool { try { diff --git a/modules/ui_patterns_devel/src/Controller/ComponentValidatorOverview.php b/modules/ui_patterns_devel/src/Controller/ComponentValidatorOverview.php index 75f9cf196a4fcce4a26e8dfcab23bc8afadd6e8c..b04ec9d31a5c0e000002fd48e67c2f247449bec4 100644 --- a/modules/ui_patterns_devel/src/Controller/ComponentValidatorOverview.php +++ b/modules/ui_patterns_devel/src/Controller/ComponentValidatorOverview.php @@ -32,6 +32,11 @@ final class ComponentValidatorOverview extends ControllerBase { */ private array $countMessages = []; + /** + * Track component with critical error to avoid rendering. + */ + private array $componentHasCritical = []; + /** * Current minimum severity. */ @@ -108,7 +113,7 @@ final class ComponentValidatorOverview extends ControllerBase { foreach ($components as $component) { $component_id = $component->getPluginId(); $messages = $this->getComponentMessages($component_id, $component); - $rows = \array_merge($rows, $this->buildOnMessageRows($messages, $component_id)); + $rows = \array_merge($rows, $this->buildOnMessageRows($messages, $component_id, TRUE)); } $build['summary'] = $this->buildMessagesSummary(); @@ -223,7 +228,7 @@ final class ComponentValidatorOverview extends ControllerBase { private function buildOnMessage(string $component_id, Component $component, array $messages): array { $build = []; - $rows = $this->buildOnMessageRows($messages); + $rows = $this->buildOnMessageRows($messages, $component_id); if ($this->singleDisplay) { $build['summary'] = $this->buildMessagesSummary(); @@ -271,13 +276,15 @@ final class ComponentValidatorOverview extends ControllerBase { * * @param \Drupal\ui_patterns_devel\ValidatorMessage[] $messages * The component messages. - * @param string|null $component_id + * @param string $component_id * If included will add the component id as first column. + * @param bool $with_name + * Flag to add the component id as first column. * * @return array * The rows to be used in a table. */ - private function buildOnMessageRows(array $messages, ?string $component_id = NULL): array { + private function buildOnMessageRows(array $messages, string $component_id, bool $with_name = FALSE): array { $levels = RfcLogLevel::getLevels(); $this->hasMessagesSeverity = FALSE; @@ -295,6 +302,10 @@ final class ComponentValidatorOverview extends ControllerBase { $this->countMessages[$message->level()] = ($this->countMessages[$message->level()] ?? 0) + 1; } + if ($message->level() <= RfcLogLevel::CRITICAL) { + $this->componentHasCritical[$component_id] = TRUE; + } + if ($message->level() > $this->minimumSeverity) { continue; } @@ -304,7 +315,7 @@ final class ComponentValidatorOverview extends ControllerBase { $line = (0 === $message->line()) ? '-' : $message->line(); $source = new FormattableMarkup('<pre><code>@code</code></pre>', ['@code' => $message->getSourceCode()]); $data = []; - if ($component_id) { + if ($with_name) { $data = [ Link::createFromRoute($component_id, 'ui_patterns_devel.twig_validator.component', ['component_id' => $component_id]), $level, @@ -489,6 +500,10 @@ final class ComponentValidatorOverview extends ControllerBase { return ['#markup' => $this->t('<small><i>Note</i>: Add `stories` to this component to enable render preview.</small>')]; } + if (isset($this->componentHasCritical[$component_id])) { + return ['#markup' => $this->t('<small><b>Error</b>: No render available as there is a Runtime error with this component, see critical above!</small>')]; + } + foreach (\array_keys($definition['stories']) as $story_id) { $build[$story_id] = ['#weight' => 4]; $element = [ diff --git a/modules/ui_patterns_devel/src/DefinitionValidator.php b/modules/ui_patterns_devel/src/DefinitionValidator.php index 2fee0b96e77fb945cbac3ca782eea462dd31f1e3..247cd07160b954d4648adedaba615a6ff531d21f 100644 --- a/modules/ui_patterns_devel/src/DefinitionValidator.php +++ b/modules/ui_patterns_devel/src/DefinitionValidator.php @@ -41,15 +41,10 @@ final class DefinitionValidator extends ValidatorBase { * The component definition. * @param string $source * The component definition source (yaml file). + * + * @todo validate with ComponentValidator to get hidden messages. */ private function validateDefinition(string $id, array $definition, string $source): void { - try { - $this->componentValidator->validateDefinition($definition, FALSE); - } - catch (InvalidComponentException $error) { - $this->addMessage(ValidatorMessage::createForInvalidComponentException($id, $error)); - } - $source = \explode("\n", $source); // Rule only one variant, then not needed. @@ -60,7 +55,7 @@ final class DefinitionValidator extends ValidatorBase { $this->validateRequiredFields($id, $definition['slots'] ?? [], $source, new TranslatableMarkup('Required slots are not recommended.')); if (isset($definition['props']['properties'])) { - $this->validateRequiredFields($id, $definition['props']['properties'], $source, new TranslatableMarkup('Required props are not recommended.')); + $this->validateRequiredFields($id, $definition['props']['properties'], $source, new TranslatableMarkup('Required props are not recommended. Use default values instead.')); $this->checkEnumDefault($id, $definition['props']['properties'], $source); $this->checkEmptyArrayObject($id, $definition['props']['properties'], $source); } @@ -131,7 +126,7 @@ final class DefinitionValidator extends ValidatorBase { $this->componentValidator->validateProps($context, $component); } catch (InvalidComponentException $error) { - $this->addValidationMessage($id, 'stories:', \explode("\n", $source), new TranslatableMarkup('Invalid component exception.')); + $this->addValidationMessage($id, 'stories:', \explode("\n", $source), new TranslatableMarkup('@message', ['@message' => $error->getMessage()])); } } @@ -173,13 +168,16 @@ final class DefinitionValidator extends ValidatorBase { continue; } - if (!isset($prop['items']['type'])) { + if (!isset($prop['items']['type']) && !isset($prop['items']['enum'])) { $this->addValidationMessage($id, $name . ':', $source, new TranslatableMarkup('Missing type for this property.')); continue; } - // @phpstan-ignore-next-line - if ('object' === $prop['items']['type'] && !isset($prop['items']['type']['properties']) && !isset($prop['items']['type']['patternProperties'])) { + if (!isset($prop['items']['type'])) { + continue; + } + + if ('object' === $prop['items']['type'] && !isset($prop['items']['properties']) && !isset($prop['items']['patternProperties'])) { $this->addValidationMessage($id, $name . ':', $source, new TranslatableMarkup('Array of empty object.')); } } diff --git a/modules/ui_patterns_devel/src/Drush/Commands/UiPatternsDevelCommands.php b/modules/ui_patterns_devel/src/Drush/Commands/UiPatternsDevelCommands.php index 51b0e267c369753df41bf53240512afc579d3396..86f3ab57221328218870f92df8fa2ea5374c6689 100644 --- a/modules/ui_patterns_devel/src/Drush/Commands/UiPatternsDevelCommands.php +++ b/modules/ui_patterns_devel/src/Drush/Commands/UiPatternsDevelCommands.php @@ -158,7 +158,7 @@ final class UiPatternsDevelCommands extends DrushCommands { continue; } - $this->validator->validate($component_id, $component, FALSE); + $this->validator->validate($component_id, $component); $messages = $this->validator->getMessagesSortedByGroupAndLine(); $this->validator->resetMessages(); diff --git a/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleFilter.php b/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleFilter.php index 27256761a5606a7ee2a56eeb1bcf0aa8d5e31416..f90f368716a31ba9d3d55d144ead1a3204a414df 100644 --- a/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleFilter.php +++ b/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleFilter.php @@ -42,6 +42,7 @@ use Twig\Node\Node; 'has_class', 'items', 'join', + 'keys', 'last', 'length', 'lower', @@ -57,7 +58,9 @@ use Twig\Node\Node; 'set_attribute', 'slice', 'split', + 'striptags', 't', + 'trans', 'title', 'trim', 'upper', @@ -72,15 +75,14 @@ use Twig\Node\Node; // Can not work because replaced by render_var. // 'raw' => 'May be dangerous, data must be escaped.',. 'reduce' => 'Functional programming may be overkill.', - 'trans' => 'Stateful. Calls to database. But useful.', ], self::RULE_NAME_FORBID => [ 'placeholder' => 'Forbidden Twig filter: `placeholder`. Keep components sandboxed by avoiding functions calling Drupal application.', - 'render' => 'Forbidden Twig filter: `render`. Please ensure you are not rendering content too early.', + 'render' => 'Please ensure you are not rendering content too early.', 'without' => 'Avoid `without` filter on slots, which must stay opaque. Allowed with attributes objects until #3296456 is fixed.', 'add_suggestion' => 'Forbidden Twig filter: `add_suggestion`. Keep components sandboxed by avoiding functions calling Drupal application.', - 'date' => 'Manipulate objects is not compatible with Design system.', - 'date_modify' => 'Manipulate objects is not compatible with Design system.', + 'date' => 'PHP object manipulation must be avoided.', + 'date_modify' => 'PHP object manipulation must be avoided.', 'format_date' => 'Business related. Load config entities.', // Contrib modules filters. 'country_name' => 'Needs specific Twig extension. Business usage, not compatible with Design System principles.', @@ -270,7 +272,43 @@ final class TwigValidatorRuleFilter extends TwigValidatorRulePluginBase { continue; } - $errors[] = ValidatorMessage::createForNode($id, $node, new TranslatableMarkup('Filter `default` is not for booleans, your prop is a boolean!')); + $errors[] = ValidatorMessage::createForNode($id, $node, new TranslatableMarkup("Don't use `default` filter on boolean.")); + } + } + + if ('Twig\Node\Expression\Filter\DefaultFilter' === get_class($node)) { + $target = NULL; + if ($parent->hasNode('expr1')) { + $expr1 = $parent->getNode('expr1')->getNode('node'); + if ($expr1->hasAttribute('name')) { + $target = $expr1->getAttribute('name'); + } + } + $args = $node->getNode('arguments'); + foreach ($args->getIterator() as $arg) { + // Detect {{ foo | default(foo) }} case. + if (!$arg instanceof Node) { + continue; + } + if ($arg->hasNode('expr') && $node->hasNode('node')) { + $expr = $arg->getNode('expr'); + if (\is_a($expr, 'Twig\Node\Expression\NameExpression') && $expr->hasAttribute('name')) { + $name = $expr->getAttribute('name'); + if ($target === $name) { + $errors[] = ValidatorMessage::createForNode($id, $node, new TranslatableMarkup('Filter `default` return the value itself!'), RfcLogLevel::WARNING); + break; + } + } + break; + } + // Detect {{ foo | default(false) }} or {{ foo | default(true) }} case. + if (\is_a($arg, 'Twig\Node\Expression\ConstantExpression')) { + $value = $arg->getAttribute('value'); + if (is_bool($value)) { + $errors[] = ValidatorMessage::createForNode($id, $node, new TranslatableMarkup("Don't use `default` filter with boolean."), RfcLogLevel::WARNING); + break; + } + } } } @@ -391,7 +429,7 @@ final class TwigValidatorRuleFilter extends TwigValidatorRulePluginBase { if (\is_a($parent, 'Twig\Node\CheckToStringNode')) { if ($parent->hasNode('expr')) { - $errors[] = ValidatorMessage::createForNode($id, $node, new TranslatableMarkup('Filter `t` unsafe translation, do not translate variables!'), RfcLogLevel::NOTICE); + $errors[] = ValidatorMessage::createForNode($id, $node, new TranslatableMarkup('Filter `trans` or `t` unsafe translation, do not translate variables!'), RfcLogLevel::NOTICE); } } @@ -400,13 +438,13 @@ final class TwigValidatorRuleFilter extends TwigValidatorRulePluginBase { $value = $parent->getAttribute('value'); if (empty($value)) { - $errors[] = ValidatorMessage::createForNode($id, $node, new TranslatableMarkup('Filter `t` is applied on an empty string'), RfcLogLevel::NOTICE); + $errors[] = ValidatorMessage::createForNode($id, $node, new TranslatableMarkup('Filter `trans` or `t` is applied on an empty string'), RfcLogLevel::NOTICE); } } } if (\is_a($parent, 'Twig\Node\Expression\ArrayExpression')) { - $errors[] = ValidatorMessage::createForNode($id, $node, new TranslatableMarkup('Filter `t` can only be applied on string!')); + $errors[] = ValidatorMessage::createForNode($id, $node, new TranslatableMarkup('Filter `trans` or `t` can only be applied on string!')); } return $errors; diff --git a/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleFunction.php b/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleFunction.php index a432745b38eac4c626d21ca75867b1cb919f6e98..bd9b3abb4a0a35738945923ded7557a6ed2edbdc 100644 --- a/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleFunction.php +++ b/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleFunction.php @@ -36,6 +36,7 @@ use Twig\Node\Node; self::RULE_NAME_WARN => [ 'source' => 'Bad architecture, but sometimes needed for shared static files.', 'component_story' => 'Not expected in real usage components.', + 'include' => 'Use slots instead of hard embedding a component in the template with `include`.', ], self::RULE_NAME_FORBID => [ 'active_theme' => 'Keep components sandboxed by avoiding functions calling Drupal application.', @@ -48,9 +49,8 @@ use Twig\Node\Node; 'link' => 'PHP URL object, or useless if URL string.', 'path' => 'Keep components sandboxed by avoiding functions calling Drupal application.', 'url' => 'Keep components sandboxed by avoiding functions calling Drupal application.', - 'block' => 'Use slots instead of hard embedding a component in the template.', - 'include' => 'Use slots instead of hard embedding a component in the template.', - 'parent' => 'Use slots instead of hard embedding a component in the template.', + 'block' => 'Use slots instead of hard embedding a component in the template with `block`.', + 'parent' => 'Use slots instead of hard embedding a component in the template with `parent`.', 'pattern_preview' => 'Legacy UI Patterns 1, not expected in real usage components.', 'help_route_link' => 'Bad architecture: Help Drupal module only.', 'help_topic_link' => 'Bad architecture: Help Drupal module only.', diff --git a/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleInclude.php b/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleInclude.php index 236cb1c803ab60b03497dad919b4d6f8eb07a27b..2cfa67b2db0efd4f1c16bf9042a0e20aa318b868 100644 --- a/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleInclude.php +++ b/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleInclude.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Drupal\ui_patterns_devel\Plugin\TwigValidatorRule; +use Drupal\Core\Logger\RfcLogLevel; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\ui_patterns_devel\Attribute\TwigValidatorRule; use Drupal\ui_patterns_devel\TwigValidatorRulePluginBase; @@ -26,8 +27,12 @@ final class TwigValidatorRuleInclude extends TwigValidatorRulePluginBase { * {@inheritdoc} */ public function processNode(string $id, Node $node, array $definition, array $variableSet): array { - $message = new TranslatableMarkup('Use slots instead of hard embedding a component in the template.'); - return [ValidatorMessage::createForNode($id, $node, $message)]; + $message = new TranslatableMarkup('Use slots instead of hard embedding a component in the template with `@name`.', ['@name' => $node->getNodeTag()]); + $level = RfcLogLevel::ERROR; + if ('include' === $node->getNodeTag()) { + $level = RfcLogLevel::WARNING; + } + return [ValidatorMessage::createForNode($id, $node, $message, $level)]; } } diff --git a/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleParent.php b/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleParent.php index 4d77ac93af32ad28866e322c8511ed3677fb2859..486959d61af5754a56dbf0e4a11edb3b2a15603f 100644 --- a/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleParent.php +++ b/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleParent.php @@ -26,7 +26,7 @@ final class TwigValidatorRuleParent extends TwigValidatorRulePluginBase { * {@inheritdoc} */ public function processNode(string $id, Node $node, array $definition, array $variableSet): array { - $message = new TranslatableMarkup('Bad architecture for parent: Component calling components.'); + $message = new TranslatableMarkup('Bad architecture for parent: Component calling components with `parent`.'); return [ValidatorMessage::createForNode($id, $node, $message)]; } diff --git a/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleTest.php b/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleTest.php index e8d7cb872250e8ba24d0b651c51164bd5ab23084..6f2e26605ae5bcba90f47fde0f91c615402d7bdf 100644 --- a/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleTest.php +++ b/modules/ui_patterns_devel/src/Plugin/TwigValidatorRule/TwigValidatorRuleTest.php @@ -36,8 +36,18 @@ final class TwigValidatorRuleTest extends TwigValidatorRulePluginBase { return [ValidatorMessage::createForNode($id, $node, $message)]; case 'Twig\Node\Expression\Test\NullTest': - $message = new TranslatableMarkup('Careful with null test.'); - return [ValidatorMessage::createForNode($id, $node, $message, RfcLogLevel::WARNING)]; + if ($node->hasAttribute('parent')) { + $parent = $node->getAttribute('parent'); + if (\is_a($parent, 'Twig\Node\Expression\Unary\NotUnary')) { + $message = new TranslatableMarkup('Use `|default(foo)` filter instead of null ternary `??`.'); + return [ValidatorMessage::createForNode($id, $node, $message, RfcLogLevel::WARNING)]; + } + else { + $message = new TranslatableMarkup('Not needed in Drupal because strict_variables=false.'); + return [ValidatorMessage::createForNode($id, $node, $message, RfcLogLevel::WARNING)]; + } + } + break; case 'Twig\Node\Expression\Test\SameasTest': $message = new TranslatableMarkup('Equivalent to strict comparison in PHP, often too strict.'); @@ -50,9 +60,15 @@ final class TwigValidatorRuleTest extends TwigValidatorRulePluginBase { ); if (!$inDefaultFilter) { - $message = new TranslatableMarkup('Not needed in Drupal because strict_variables=false.'); - return [ValidatorMessage::createForNode($id, $node, $message)]; + if ($node->hasAttribute('parent')) { + $parent = $node->getAttribute('parent'); + if (!\is_a($parent, 'Twig\Node\Expression\Binary\AndBinary')) { + $message = new TranslatableMarkup('Not needed in Drupal because strict_variables=false.'); + return [ValidatorMessage::createForNode($id, $node, $message)]; + } + } } + break; case 'Twig\Node\Expression\TestExpression': if (!$node->hasAttribute('name')) { @@ -64,10 +80,14 @@ final class TwigValidatorRuleTest extends TwigValidatorRulePluginBase { $message = new TranslatableMarkup('The exact same as just testing the variable, empty is not needed.'); return [ValidatorMessage::createForNode($id, $node, $message)]; - case 'iterable': - $message = new TranslatableMarkup('Return true for mapping and sequence.'); - return [ValidatorMessage::createForNode($id, $node, $message, RfcLogLevel::WARNING)]; + // @phpcs:disable + // @todo enable when Twig 3.11 with better iterable. + // case 'iterable': + // $message = new TranslatableMarkup('Return true for mapping and sequence.'); + // return [ValidatorMessage::createForNode($id, $node, $message, RfcLogLevel::WARNING)]; + // @phpcs:enable } + break; } return []; diff --git a/modules/ui_patterns_devel/src/TwigValidator/TwigValidator.php b/modules/ui_patterns_devel/src/TwigValidator/TwigValidator.php index 0d3309cbd5a7fbd7b01e810927453fdbfbf8af9b..9a8a3dbba6d08c8d155ba400988bb56dc541d993 100644 --- a/modules/ui_patterns_devel/src/TwigValidator/TwigValidator.php +++ b/modules/ui_patterns_devel/src/TwigValidator/TwigValidator.php @@ -33,11 +33,6 @@ final class TwigValidator extends ValidatorBase { */ private ?TemplateWrapper $template = NULL; - /** - * Flag to render the template or source. - */ - private bool $render = TRUE; - /** * Constructs a new TwigValidator object. * @@ -75,21 +70,10 @@ final class TwigValidator extends ValidatorBase { $this->addMessage(ValidatorMessage::createFromTwigError($id, $error)); } - $this->checkTemplate($id); $source = $this->twig->getLoader()->getSourceContext($path); $this->processSource($id, $source); } - /** - * Set render option boolean. - * - * @param bool $option - * The option value. - */ - public function setOption(bool $option): void { - $this->render = $option; - } - /** * Validates a source and processes the source for errors. * @@ -106,33 +90,10 @@ final class TwigValidator extends ValidatorBase { $this->addMessage(ValidatorMessage::createFromTwigError('', $error)); } - $this->checkTemplate(''); - if (!$this->template) { - return; - } $source = $this->template->getSourceContext(); $this->processSource('', $source); } - /** - * Renders the template and catches any Twig errors. - * - * @param string $id - * The ID of the component. - */ - private function checkTemplate(string $id): void { - if (!$this->template || FALSE === $this->render) { - return; - } - - try { - $this->template->render(); - } - catch (Error $error) { - $this->addMessage(ValidatorMessage::createFromTwigError($id, $error)); - } - } - /** * Validator entrypoint to collect errors from Twig source. * diff --git a/modules/ui_patterns_devel/src/Validator.php b/modules/ui_patterns_devel/src/Validator.php index 5daa6bea49813a0e752670cd4063c4cc6c2d88e3..f32ab72316b47a264b7d9e43caf14d80e8ee26d4 100644 --- a/modules/ui_patterns_devel/src/Validator.php +++ b/modules/ui_patterns_devel/src/Validator.php @@ -32,11 +32,8 @@ final class Validator extends ValidatorBase { * The ID of the component. * @param \Drupal\Core\Plugin\Component $component * The component to validate. - * @param bool $render - * Option to render to component when validating. */ - public function validate(string $id, Component $component, bool $render = TRUE): void { - $this->twigValidator->setOption($render); + public function validate(string $id, Component $component): void { $this->validateComponent($id, $component); } @@ -44,10 +41,12 @@ final class Validator extends ValidatorBase { * {@inheritdoc} */ public function validateComponent(string $id, Component $component): void { + $this->twigValidator->resetMessages(); $this->twigValidator->validateComponent($id, $component); $this->addMessages($this->twigValidator->getMessages()); $this->twigValidator->resetMessages(); + $this->definitionValidator->resetMessages(); $this->definitionValidator->validateComponent($id, $component); $this->addMessages($this->definitionValidator->getMessages()); $this->definitionValidator->resetMessages(); diff --git a/modules/ui_patterns_devel/tests/modules/ui_patterns_devel_module_test/ui_patterns_devel_module_test.info.yml b/modules/ui_patterns_devel/tests/modules/ui_patterns_devel_module_test/ui_patterns_devel_module_test.info.yml index 1141adb9a42525cd3c7e29cec650ae1f010d71ca..6913409bccf9db518dff63e05a788844638b558c 100644 --- a/modules/ui_patterns_devel/tests/modules/ui_patterns_devel_module_test/ui_patterns_devel_module_test.info.yml +++ b/modules/ui_patterns_devel/tests/modules/ui_patterns_devel_module_test/ui_patterns_devel_module_test.info.yml @@ -1,6 +1,6 @@ -name: "UI Patterns Devel test" +name: 'UI Patterns Devel test' type: module -description: "Provides some Twig override for tests." +description: 'Provides some Twig override for tests.' package: Testing dependencies: - ui_patterns:ui_patterns diff --git a/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleConditionalTest.php b/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleConditionalTest.php index 590537d7af91b2974ed05402bdaf669f1553a4f1..b156297398fc28a4aa774bed50be15fb68bcfb7d 100644 --- a/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleConditionalTest.php +++ b/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleConditionalTest.php @@ -35,7 +35,6 @@ final class TwigValidatorRuleConditionalTest extends TwigValidatorTestBase { {% set foo = false %}{% set bar = 'bar' %} {{ foo ? foo : bar }} {{ foo ?: bar }} - {{ foo|default(bar) }} {{ qux == 'bar' ? true : false }} ", [ @@ -44,7 +43,7 @@ final class TwigValidatorRuleConditionalTest extends TwigValidatorTestBase { [3, 'No chained ternary', RfcLogLevel::ERROR], [7, 'Use `|default(foo)` filter instead of shorthand ternary `?:`', RfcLogLevel::WARNING], [8, 'Use `|default(foo)` filter instead of shorthand ternary `?:`', RfcLogLevel::WARNING], - [10, 'Ternary test with boolean result', RfcLogLevel::NOTICE], + [9, 'Ternary test with boolean result', RfcLogLevel::NOTICE], ], ], ]; diff --git a/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleFilterTest.php b/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleFilterTest.php index 4817b7be1bdddf570fd52d960d01e30ee2dc08ec..ca2bd6862d4f8dab714bb34dbc525f6a2041e199 100644 --- a/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleFilterTest.php +++ b/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleFilterTest.php @@ -70,6 +70,10 @@ final class TwigValidatorRuleFilterTest extends TwigValidatorTestBase { {{ true | default('error') }} {{ false | default('error') }} {{ null | default('error') }} + {% set foo = 'foo' %}{{ foo | default(false) }} + {{ foo | default(true) }} + {{ foo | default(foo) }} + {% set bar = 'bar' %}{{ foo | default(bar) }} {% set my_var = 'foo' %}{{ my_var | t }} {% trans %}Submitted on {{ my_var | placeholder }}{% endtrans %} {{ 'foo'|convert_encoding('UTF-8', 'iso-2022-jp') }} @@ -79,9 +83,8 @@ final class TwigValidatorRuleFilterTest extends TwigValidatorTestBase { [2, 'Filter `set_attribute` second argument can not be a mapping!', RfcLogLevel::ERROR], [3, 'Filter `set_attribute` second argument can not be null!', RfcLogLevel::ERROR], [4, 'Filter `add_class` can not be used on `string`, only `mapping`!', RfcLogLevel::ERROR], - [5, 'Filter `t` is applied on an empty string', RfcLogLevel::NOTICE], - [6, 'Careful with Twig filter: `trans`. Stateful. Calls to database. But useful.', RfcLogLevel::WARNING], - [6, 'Filter `t` is applied on an empty string', RfcLogLevel::NOTICE], + [5, 'Filter `trans` or `t` is applied on an empty string', RfcLogLevel::NOTICE], + [6, 'Filter `trans` or `t` is applied on an empty string', RfcLogLevel::NOTICE], [7, 'Filter `abs` can only be applied on number, boolean found!', RfcLogLevel::ERROR], [9, 'Careful with Twig filter: `clean_unique_id`. ', RfcLogLevel::WARNING], [11, 'Careful with Twig filter: `filter`. Functional programming may be overkill.', RfcLogLevel::WARNING], @@ -91,30 +94,31 @@ final class TwigValidatorRuleFilterTest extends TwigValidatorTestBase { [15, 'Filter `clean_id` can only be applied on string!', RfcLogLevel::ERROR], [16, 'Filter `clean_id` can only be applied on string!', RfcLogLevel::ERROR], [17, 'Filter `clean_id` can only be applied on string!', RfcLogLevel::ERROR], - [18, 'Forbidden Twig filter: `render`. Forbidden Twig filter: `render`. Please ensure you are not rendering content too early.', RfcLogLevel::ERROR], + [18, 'Forbidden Twig filter: `render`. Please ensure you are not rendering content too early.', RfcLogLevel::ERROR], [19, 'Forbidden Twig filter: `without`. Avoid `without` filter on slots, which must stay opaque. Allowed with attributes objects until #3296456 is fixed.', RfcLogLevel::ERROR], - [20, 'Forbidden Twig filter: `date`. Manipulate objects is not compatible with Design system.', RfcLogLevel::ERROR], - [20, 'Forbidden Twig filter: `date_modify`. Manipulate objects is not compatible with Design system.', RfcLogLevel::ERROR], + [20, 'Forbidden Twig filter: `date`. PHP object manipulation must be avoided.', RfcLogLevel::ERROR], + [20, 'Forbidden Twig filter: `date_modify`. PHP object manipulation must be avoided.', RfcLogLevel::ERROR], [21, 'Forbidden Twig filter: `format_date`. Business related. Load config entities.', RfcLogLevel::ERROR], [22, 'Filter `default` is not for booleans or null!', RfcLogLevel::ERROR], [23, 'Filter `default` is not for booleans or null!', RfcLogLevel::ERROR], [24, 'Filter `default` is not for booleans or null!', RfcLogLevel::ERROR], - [25, 'Filter `t` unsafe translation, do not translate variables!', RfcLogLevel::NOTICE], - [26, 'Forbidden Twig filter: `placeholder`. Forbidden Twig filter: `placeholder`. Keep components sandboxed by avoiding functions calling Drupal application.', RfcLogLevel::ERROR], - [27, 'Careful with Twig filter: `convert_encoding`. Needs specific PHP extension.', RfcLogLevel::WARNING], + [25, "Don't use `default` filter with boolean.", RfcLogLevel::WARNING], + [26, "Don't use `default` filter with boolean.", RfcLogLevel::WARNING], + [27, 'Filter `default` return the value itself!', RfcLogLevel::WARNING], + [29, 'Filter `trans` or `t` unsafe translation, do not translate variables!', RfcLogLevel::NOTICE], + [30, 'Forbidden Twig filter: `placeholder`. Forbidden Twig filter: `placeholder`. Keep components sandboxed by avoiding functions calling Drupal application.', RfcLogLevel::ERROR], + [31, 'Careful with Twig filter: `convert_encoding`. Needs specific PHP extension.', RfcLogLevel::WARNING], ], ], [ "{{ {'#theme': 'foo'} | add_suggestion('bar') }}", [ - [1, 'An exception has been thrown during the rendering of a template', RfcLogLevel::CRITICAL], [1, 'Forbidden Twig filter: `add_suggestion`. Keep components sandboxed by avoiding functions calling Drupal application.', RfcLogLevel::ERROR], ], ], [ "{{ 'foo' | abs }}", [ - [1, 'An exception has been thrown during the rendering of a template', RfcLogLevel::CRITICAL], [1, 'Filter `abs` can only be applied on number, string found!', RfcLogLevel::ERROR], ], ], @@ -122,14 +126,12 @@ final class TwigValidatorRuleFilterTest extends TwigValidatorTestBase { "{{ ['foo'] | t }} {{ ['foo'] | trans }}", [ - [1, 'An exception has been thrown during the rendering of a template', RfcLogLevel::CRITICAL], - [1, 'Filter `t` can only be applied on string!', RfcLogLevel::ERROR], - [2, 'Careful with Twig filter: `trans`. Stateful. Calls to database. But useful.', RfcLogLevel::WARNING], - [2, 'Filter `t` can only be applied on string!', RfcLogLevel::ERROR], + [1, 'Filter `trans` or `t` can only be applied on string!', RfcLogLevel::ERROR], + [2, 'Filter `trans` or `t` can only be applied on string!', RfcLogLevel::ERROR], ], ], - // // Can not run for now. - // // Reason: PHP deprecation notice, always fail for PHPunit 9+. + // Can not run for now. + // Reason: PHP deprecation notice, always fail for PHPunit 9+. // [ // "{{ null | abs }}", // [ @@ -141,7 +143,7 @@ final class TwigValidatorRuleFilterTest extends TwigValidatorTestBase { // "{{ true | abs }}", // [1, 'Filter `abs` can only be applied on number, string found!', RfcLogLevel::ERROR], // [1, 'An exception has been thrown during the rendering of a template', RfcLogLevel::CRITICAL], - // ], + // ],. ]; } diff --git a/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleFunctionTest.php b/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleFunctionTest.php index 6f23704b3f5bd7e20f7b2e6335e27bb2174bf5e4..b70bfa15134f42d645b7d3aff435af3907c03304 100644 --- a/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleFunctionTest.php +++ b/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleFunctionTest.php @@ -46,14 +46,12 @@ final class TwigValidatorRuleFunctionTest extends TwigValidatorTestBase { [ "{{ component_story('ui_patterns_devel_theme_test:foo', 'preview') }}", [ - [1, 'An exception has been thrown during the rendering of a template', RfcLogLevel::CRITICAL], [1, 'Careful with Twig function: `component_story`. Not expected in real usage components.', RfcLogLevel::WARNING], ], ], [ "{{ pattern('ui_patterns_devel_theme_test:foo') }}", [ - [1, 'An exception has been thrown during the rendering of a template', RfcLogLevel::CRITICAL], [1, 'Deprecated Twig function: `pattern`. Replace with Twig function component().', RfcLogLevel::WARNING], ], ], @@ -86,42 +84,36 @@ final class TwigValidatorRuleFunctionTest extends TwigValidatorTestBase { [ "{{ help_route_link('foo') }}", [ - [1, 'An exception has been thrown during the rendering of a template', RfcLogLevel::CRITICAL], [1, 'Forbidden Twig function: `help_route_link`. Bad architecture: Help Drupal module only.', RfcLogLevel::ERROR], ], ], [ "{{ help_topic_link('foo') }}", [ - [1, 'An exception has been thrown during the rendering of a template', RfcLogLevel::CRITICAL], [1, 'Forbidden Twig function: `help_topic_link`. Bad architecture: Help Drupal module only.', RfcLogLevel::ERROR], ], ], [ "{{ attach_library('system/maintenance') }}", [ - [1, 'An exception has been thrown during the rendering of a template', RfcLogLevel::CRITICAL], [1, 'The asset library attachment would be more discoverable if declared in the component definition.', RfcLogLevel::ERROR], ], ], [ "{{ pattern_preview('ui_patterns_devel_theme_test:foo', 'preview') }}", [ - [1, 'An exception has been thrown during the rendering of a template', RfcLogLevel::CRITICAL], [1, 'Forbidden Twig function: `pattern_preview`. Legacy UI Patterns 1, not expected in real usage components.', RfcLogLevel::ERROR], ], ], [ "{{ url('<front>') }}", [ - [1, 'An exception has been thrown during the rendering of a template', RfcLogLevel::CRITICAL], [1, 'Forbidden Twig function: `url`. Keep components sandboxed by avoiding functions calling Drupal application.', RfcLogLevel::ERROR], ], ], [ "{{ link('foo', 'http://foo.bar') }}", [ - [1, 'An exception has been thrown during the rendering of a template', RfcLogLevel::CRITICAL], [1, 'Forbidden Twig function: `link`. PHP URL object, or useless if URL string.', RfcLogLevel::ERROR], ], ], @@ -175,7 +167,6 @@ final class TwigValidatorRuleFunctionTest extends TwigValidatorTestBase { {{ query_executable() }} ", [ - [1, 'An exception has been thrown during the rendering of a template', RfcLogLevel::CRITICAL], [1, 'Forbidden Twig function: `help_route_link`. Bad architecture: Help Drupal module only.', RfcLogLevel::ERROR], [2, 'Forbidden Twig function: `help_topic_link`. Bad architecture: Help Drupal module only.', RfcLogLevel::ERROR], [3, 'Deprecated Twig function: `pattern`. Replace with Twig function component().', RfcLogLevel::WARNING], diff --git a/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleIncludeTest.php b/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleIncludeTest.php index 4caf0d6abb80c91002c742cd6018e9fc91ff475e..82ed0620b4d395c4be7616cc623351348df32616 100644 --- a/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleIncludeTest.php +++ b/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleIncludeTest.php @@ -28,11 +28,13 @@ final class TwigValidatorRuleIncludeTest extends TwigValidatorTestBase { return [ [ "{% include 'links.html.twig' %} + {% include('links.html.twig') %} {% embed 'links.html.twig' %}{% endembed %} ", [ - [1, 'Use slots instead of hard embedding a component in the template.', RfcLogLevel::ERROR], - [2, 'Use slots instead of hard embedding a component in the template.', RfcLogLevel::ERROR], + [1, 'Use slots instead of hard embedding a component in the template with `include`.', RfcLogLevel::WARNING], + [2, 'Use slots instead of hard embedding a component in the template with `include`.', RfcLogLevel::WARNING], + [3, 'Use slots instead of hard embedding a component in the template with `embed`.', RfcLogLevel::ERROR], ], ], ]; diff --git a/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleParentTest.php b/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleParentTest.php index d09b1e55e03bf225c7df12ac6cbcfc91fa689918..9ced17289984b592dacea1acbd109c67cc21205d 100644 --- a/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleParentTest.php +++ b/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleParentTest.php @@ -27,10 +27,13 @@ final class TwigValidatorRuleParentTest extends TwigValidatorTestBase { public static function providerTestTwigValidatorParent() { return [ [ - "{% extends 'links.html.twig' %}{% block foo %}{{ parent('bar') }}{% endblock %}", + "{% extends 'links.html.twig' %} + {% block foo %} + {{ parent('bar') }} + {% endblock %}", [ - [1, 'Bad architecture for parent: Component calling components.', RfcLogLevel::ERROR], [1, 'Use slots instead of hard embedding a component in the template.', RfcLogLevel::ERROR], + [3, 'Bad architecture for parent: Component calling components with `parent`.', RfcLogLevel::ERROR], ], ], ]; diff --git a/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleTestTest.php b/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleTestTest.php index 79bb3d7095121b0ed74a566bb196a2c5e4775e1f..92314fd280dc43010ebe041d638c26887d75e408 100644 --- a/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleTestTest.php +++ b/modules/ui_patterns_devel/tests/src/Kernel/TwigValidatorRule/TwigValidatorRuleTestTest.php @@ -30,15 +30,15 @@ final class TwigValidatorRuleTestTest extends TwigValidatorTestBase { "{% set foo = 'foo' %} {% if foo is defined %}{% endif %} {% if foo is empty %}{% endif %} - {% if foo is iterable %}{% endif %} {{ foo is null }} + {{ foo ?? 'bar' }} {% if foo is same as(false) %}{% endif %} ", [ [2, 'Not needed in Drupal because strict_variables=false.', RfcLogLevel::ERROR], [3, 'The exact same as just testing the variable, empty is not needed.', RfcLogLevel::ERROR], - [4, 'Return true for mapping and sequence.', RfcLogLevel::WARNING], - [5, 'Careful with null test.', RfcLogLevel::WARNING], + [4, 'Not needed in Drupal because strict_variables=false.', RfcLogLevel::WARNING], + [5, 'Use `|default(foo)` filter instead of null ternary `??`.', RfcLogLevel::WARNING], [6, 'Equivalent to strict comparison in PHP, often too strict.', RfcLogLevel::ERROR], ], ], diff --git a/modules/ui_patterns_devel/tests/src/Kernel/ValidatorTest.php b/modules/ui_patterns_devel/tests/src/Kernel/ValidatorTest.php index a15d10a31e9c5c39d93f0ab765b4c985872e9356..b189cd04a5b216bbc7f1471acc464c3e5a3d26e4 100644 --- a/modules/ui_patterns_devel/tests/src/Kernel/ValidatorTest.php +++ b/modules/ui_patterns_devel/tests/src/Kernel/ValidatorTest.php @@ -90,21 +90,19 @@ class ValidatorTest extends ComponentKernelTestBase { $errors = $this->validator->getMessages(); $expected = [ - 'Unused variables: test_slot_string, nothing, object, array, array_object, test_prop_string, test_enum_string', - 'Filter `t` unsafe translation, do not translate variables!', - 'Filter `default` is not for booleans, your prop is a boolean!', - '[props.properties.test_prop_string.required] Boolean value found, but an array is required', + 'Unused variables: test_slot_string, nothing, object, array, array_object, test_prop_string_error, test_enum_string, test_prop_links, test_enum_items', + 'Filter `trans` or `t` unsafe translation, do not translate variables!', + "Don't use `default` filter on boolean.", 'A single variant do not need to be declared.', 'Required slots are not recommended.', - 'Required props are not recommended.', + 'Required props are not recommended. Use default values instead.', 'Default value must be in the enum.', 'Missing type for this property.', 'Empty object.', 'Empty array.', 'Array of empty object.', - 'Invalid component exception.', + '[variant] Does not have a value in the enumeration ["default"]', ]; - $this->assertCount(\count($expected), $errors); foreach ($expected as $key => $message) { @@ -125,12 +123,19 @@ class ValidatorTest extends ComponentKernelTestBase { $component_id = 'ui_patterns_devel_theme_test:fail'; $component = $this->componentPluginManager->find($component_id); - $this->validator->validate($component_id, $component, FALSE); + $this->validator->validate($component_id, $component); $errors = $this->validator->getMessages(); + $expected = [ + 'An exception has been thrown during the rendering of a template', + 'Unexpected "}".', + 'You should declare a story in the definition.', + ]; + $this->assertCount(3, $errors); - $this->assertSame('Unexpected "}".', (string) $errors[1]->message()); - $this->assertSame('You should declare a story in the definition.', (string) $errors[2]->message()); + $this->assertStringContainsString($expected[0], (string) $errors[0]->message()); + $this->assertSame($expected[1], (string) $errors[1]->message()); + $this->assertSame($expected[2], (string) $errors[2]->message()); } } diff --git a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/all_errors/all_errors.component.yml b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/all_errors/all_errors.component.yml index d8a410b2d262bc6390436f77e89b35439ec231dd..13971d3a6cc53979f067c6b88c89d11d5da4057b 100644 --- a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/all_errors/all_errors.component.yml +++ b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/all_errors/all_errors.component.yml @@ -10,7 +10,7 @@ variants: slots: test_slot_content: title: Test renderable - required: true + # required: true test_slot_string: title: Test string type: string @@ -19,19 +19,32 @@ props: properties: # From ui_patterns_test/components/prop_types_tests/prop_types_tests.component.yml nothing: - title: "Unknown (nothing)" + title: 'Unknown (nothing)' object: - title: "Unknown (empty object)" - type: "object" + title: 'Unknown (empty object)' + type: 'object' array: - title: "Unknown (empty array)" - type: "array" + title: 'Unknown (empty array)' + type: 'array' array_object: - title: "Unknown (array of empty objects)" + title: 'Unknown (array of empty objects)' type: array items: type: object - test_prop_string: + array_object_valid: + title: Array of links valid + $ref: 'ui-patterns://links' + missing_type_enum_valid: + title: Missing type with enum valid + $ref: 'ui-patterns://enum_list' + items: + enum: + - foo + - bar + 'meta:enum': + foo: 'Foo' + bar: 'Bar' + test_prop_string_all_errors: title: Test string required: true type: string @@ -239,11 +252,18 @@ stories: test_slot_string: Baz props: variant: other - test_prop_string: Qux + array_object_valid: + - title: Foo + url: / + - title: Bar + url: '#' + missing_type_enum_valid: + - bar + test_prop_string_all_errors: Qux test_prop_string_default: Quux test_prop_string_enum: bottom test_prop_string_enum_default: middle - test_prop_string_length: "FooBar" + test_prop_string_length: 'FooBar' test_prop_boolean: true test_prop_integer: 6 test_prop_number_1: 6.33 @@ -261,9 +281,9 @@ stories: test_prop_url_1: http://foo.bar test_prop_url_2: <front> test_prop_url_3: mailto:foo@bar.baz - test_prop_color: "#123" - test_prop_color_3: "#456" - test_prop_color_6: "#123456" + test_prop_color: '#123' + test_prop_color_3: '#456' + test_prop_color_6: '#123456' test_prop_array: - foo - bar @@ -285,14 +305,14 @@ stories: - foo - bar below: - - title: "Foo 1" - url: "#foo1" + - title: 'Foo 1' + url: '#foo1' attributes: data-test: test - - title: "Foo 2" - url: "#foo2" + - title: 'Foo 2' + url: '#foo2' - title: Bar - url: "#bar" + url: '#bar' test_prop_links_2: - title: Foo attributes: @@ -301,21 +321,16 @@ stories: - bar extra: Extra below: - - title: "Foo 1" - url: "#foo1" + - title: 'Foo 1' + url: '#foo1' attributes: data-test: test - - title: "Foo 2" - url: "#foo2" + - title: 'Foo 2' + url: '#foo2' - title: Bar - url: "#bar" + url: '#bar' extra: Extra test_prop_machine_name: foo_bar test_prop_list: - Foo - Bar - # test_prop_bool: true - # test_prop_number: 5 - # test_prop_number_negative: -5 - # test_prop_number_float: 5.33 - # test_prop_string: foo diff --git a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/all_errors/all_errors.twig b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/all_errors/all_errors.twig index acb11bc4d9b0e1242e28e73077cf106f988ed4e4..cd4f4c062d7d6d4e2f6693d20f1ac548465115be 100644 --- a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/all_errors/all_errors.twig +++ b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/all_errors/all_errors.twig @@ -1,3 +1,9 @@ +--- Schema --- +{% for link in array_object_valid %} + {{ link.title }} : {{ link.url }} +{% endfor %} +{{ missing_type_enum_valid | join(', ') }} + --- Injected --- attributes: {{ attributes.addClass('foo') }} variant: {{ variant}} @@ -5,7 +11,7 @@ variant: {{ variant}} --- Story --- test_slot_content: {{ test_slot_content }} test_slot_string: {{ test_slot_string }} -test_prop_string: {{ test_prop_string }} +test_prop_string_1: {{ test_prop_string }} test_prop_string_default: {{ test_prop_string_default }} test_prop_string_enum: {{ test_prop_string_enum }} test_prop_string_enum_default: {{ test_prop_string_enum_default }} @@ -124,6 +130,7 @@ file_url ERROR: {{ file_url('public://foo.txt') }} path ERROR: {{ path('<front>') }} include ERROR: {% include 'links.html.twig' %} +include fn ERROR: {% include('links.html.twig') %} embed ERROR: {% embed "links.html.twig" %}{% endembed %} block ERROR: {% block foo %}{% endblock %}{{ block('foo') }} import ERROR: {% from 'block--local-actions-block.html.twig' import input as foo %} @@ -157,6 +164,7 @@ defined ERROR: {% if foo_tests is defined %}{% endif %} empty ERROR: {% if foo_tests is empty %}{% endif %} iterable WARNING: {% if foo_tests is iterable %}{% endif %} null WARNING: {{ foo_tests is null }} +null coalescing WARNING: {{ foo_tests ?? 'foo' }} same as ERROR: {% if foo_tests is same as(false) %}{% endif %} --- TESTS GETATTR --- @@ -182,6 +190,9 @@ No chained ternary OK: {{ foo ? baz }} {% set foo = false %}{% set bar = 'bar' %} Use |default WARNING: {{ foo ? foo : bar }} Use |default WARNING: {{ foo ?: bar }} +Use |default WARNING: {{ foo | default(false) }} +Use |default WARNING: {{ foo | default(true) }} +Use |default WARNING: {{ foo | default(foo) }} Use |default OK: {{ foo|default(bar) }} ternary test with boolean NOTICE: {{ qux == 'bar' ? true : false }} diff --git a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/collector/collector.component.yml b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/collector/collector.component.yml index 723a3d4c1660e3f878e1771daec6f448543a3404..1c833a9a47f73af6b67d934f6a769e4f07de3cb8 100644 --- a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/collector/collector.component.yml +++ b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/collector/collector.component.yml @@ -16,7 +16,7 @@ slots: props: type: object properties: - test_prop_string: + test_prop_string_collector: title: Test string type: string test_prop_bool: @@ -32,7 +32,7 @@ stories: test_slot_string: Baz props: variant: test - test_prop_string: Qux + test_prop_string_collector: Qux test_prop_array: - foo - bar diff --git a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/collector/collector.twig b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/collector/collector.twig index eb2637365cb8acdd0eff88543c192b8ee2e160bb..a8a0c8848475cbe5af1802c74669e89a16f9d9a3 100644 --- a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/collector/collector.twig +++ b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/collector/collector.twig @@ -10,5 +10,5 @@ {% for item_unused in test_prop_array %} {{ foo }} {% endfor %} -{{ test_prop_string }} +{{ test_prop_string_collector }} {% set my_attributes = create_attribute() %} diff --git a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/error/error.component.yml b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/error/error.component.yml index 995404975b1c60c009f954482125cbf920537c83..9a95e9995fd2c2889411a5c913985ab911bd46de 100644 --- a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/error/error.component.yml +++ b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/error/error.component.yml @@ -16,19 +16,19 @@ props: type: object properties: nothing: - title: "Unknown (nothing)" + title: 'Unknown (nothing)' object: - title: "Unknown (empty object)" - type: "object" + title: 'Unknown (empty object)' + type: 'object' array: - title: "Unknown (empty array)" - type: "array" + title: 'Unknown (empty array)' + type: 'array' array_object: - title: "Unknown (array of empty objects)" + title: 'Unknown (array of empty objects)' type: array items: type: object - test_prop_string: + test_prop_string_error: title: Test string required: true type: string @@ -43,6 +43,19 @@ props: test_prop_bool: title: Test bool type: boolean + test_prop_links: + title: Test links + $ref: 'ui-patterns://links' + test_enum_items: + title: Test enum items + $ref: 'ui-patterns://enum_list' + items: + enum: + - foo + - bar + 'meta:enum': + foo: 'Foo' + bar: 'Bar' stories: preview: title: Preview @@ -50,6 +63,13 @@ stories: test_slot_string: Baz props: variant: test - test_prop_string: Qux + test_prop_string_error: Qux test_enum_string: h2 test_prop_bool: true + test_prop_links: + - title: Foo + url: / + - title: Bar + url: '#' + test_enum_items: + - bar diff --git a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/fail/fail.component.yml b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/fail/fail.component.yml index 98e89d3771bcb0593ff5d1a67de3820299280ed8..b92ce7e122a7bf02f07a3f65688b293ac24c91be 100644 --- a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/fail/fail.component.yml +++ b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/fail/fail.component.yml @@ -7,6 +7,6 @@ status: experimental props: type: object properties: - test_prop_string: + test_prop_string_fail: title: Test string type: string diff --git a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/foo/foo.component.yml b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/foo/foo.component.yml index e9cb24500f3304af1757e711e90a213bfd8e2350..daaf04c15a487f87e38d2aacd48fe3601cd073a2 100644 --- a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/foo/foo.component.yml +++ b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/foo/foo.component.yml @@ -16,7 +16,7 @@ slots: props: type: object properties: - test_prop_string: + test_prop_string_foo: title: Test string type: string stories: @@ -27,11 +27,11 @@ stories: test_slot_string: Baz props: variant: test - test_prop_string: Qux + test_prop_string_foo: Qux test: title: Test description: Story for the `foo` component. slots: test_slot_string: Foo props: - test_prop_string: Bar + test_prop_string_foo: Bar diff --git a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/foo/foo.twig b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/foo/foo.twig index ca2abee788f9213b829baa33b18ea27e79198549..682a7a746a9959f99db5d927fe56a2d00090d2e5 100644 --- a/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/foo/foo.twig +++ b/modules/ui_patterns_devel/tests/themes/ui_patterns_devel_theme_test/components/foo/foo.twig @@ -1,4 +1,4 @@ <b>variant:</b> {{ variant }} test_slot_string: {{ test_slot_string }} -test_prop_string: {{ test_prop_string }} +test_prop_string_foo: {{ test_prop_string_foo }}