From dc48b8bbfc55e009af021b17f772ea05ffe8531a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Fri, 13 Mar 2026 10:38:42 +0100 Subject: [PATCH 01/20] Implement Callout Implement a callout box element that can be used to convey important information to the user. This element is designed to be used above certain form elements, or over the whole form or page. --- asset/css/callout.less | 60 +++++++++++++++++++++ src/Common/CalloutType.php | 31 +++++++++++ src/Widget/Callout.php | 103 +++++++++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+) create mode 100644 asset/css/callout.less create mode 100644 src/Common/CalloutType.php create mode 100644 src/Widget/Callout.php diff --git a/asset/css/callout.less b/asset/css/callout.less new file mode 100644 index 000000000..889d5ace9 --- /dev/null +++ b/asset/css/callout.less @@ -0,0 +1,60 @@ +// Layout +.callout { + display: flex; + justify-content: center; + column-gap: 1em; + + width: fit-content; + margin: 0 auto 1em auto; + + &.fullwidth { + width: 100%; + } + + i.icon::before { + margin-right: 0; + } + + p { + margin: 0; + } + + .callout-text { + display: flex; + flex-direction: column; + } + + &.form-callout { + margin-left: 14em; + width: auto; + } +} + +// Style +.callout { + padding: .5em 1em; + border: 1px solid var(--callout-color); + background-color: color-mix(in srgb, var(--callout-color) 10%, transparent); + border-radius: .25em; + + i.icon { + color: var(--callout-color); + font-size: 1.5em; + } + + &.info { + --callout-color: @color-pending; + } + + &.success { + --callout-color: @color-ok; + } + + &.warning { + --callout-color: @color-warning; + } + + &.error { + --callout-color: @color-critical; + } +} diff --git a/src/Common/CalloutType.php b/src/Common/CalloutType.php new file mode 100644 index 000000000..aedc05ee0 --- /dev/null +++ b/src/Common/CalloutType.php @@ -0,0 +1,31 @@ + new Icon('circle-info'), + self::Success => new Icon('circle-check'), + self::Warning => new Icon('warning'), + self::Error => new Icon('circle-xmark'), + }; + } +} diff --git a/src/Widget/Callout.php b/src/Widget/Callout.php new file mode 100644 index 000000000..293f86c6a --- /dev/null +++ b/src/Widget/Callout.php @@ -0,0 +1,103 @@ + 'callout']; + + /** @var string|null An optional title */ + protected ?string $title; + + /** @var string The content to display */ + protected string $content; + + /** @var CalloutType The type of callout, determines the color and icon */ + protected CalloutType $type; + + public function __construct(CalloutType $type, string $content, ?string $title = null) + { + $this->type = $type; + $this->content = $content; + $this->title = $title; + + $this->addAttributes(Attributes::create(['class' => $type->value])); + } + + public function assemble(): void + { + $this->addHtml($this->type->getIcon()); + + if ($this->title) { + $this->addHtml(HtmlElement::create( + 'div', + ['class' => 'callout-text'], + [ + HtmlElement::create('strong', null, new Text($this->title)), + HtmlElement::create('p', null, new Text($this->content)), + ], + )); + } else { + $this->addHtml(HtmlElement::create( + 'div', + ['class' => 'callout-text'], + HtmlElement::create('strong', null, new Text($this->content)), + )); + } + } + + /** + * Callouts are only as wide as their content. + * Setting it to fullwidth will force the callout to be as wide as its container. + * + * @param bool $fullwidth should the callout be fullwidth + * + * @return $this + */ + public function setFullwidth(bool $fullwidth = true): static + { + if ($fullwidth) { + $this->addAttributes(Attributes::create(['class' => 'fullwidth'])); + } else { + $this->removeAttribute('class', 'fullwidth'); + } + + return $this; + } + + /** + * Setting this to true will allow the callout to be used for a single form element. + * This is used to visually align the callout to the content of the form element. + * + * @param bool $isFormElement should the callout be used for a form element + * + * @return $this + */ + public function setFormElement(bool $isFormElement = true): static + { + if ($isFormElement) { + $this->addAttributes(Attributes::create(['class' => 'form-callout'])); + } else { + $this->removeAttribute('class', 'from-callout'); + } + + return $this; + } +} From e8b43369e72287561891ffb01ee91c6cfa543099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Fri, 13 Mar 2026 10:55:55 +0100 Subject: [PATCH 02/20] Stylelint suggestions --- asset/css/callout.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asset/css/callout.less b/asset/css/callout.less index 889d5ace9..1b90f64e7 100644 --- a/asset/css/callout.less +++ b/asset/css/callout.less @@ -5,7 +5,7 @@ column-gap: 1em; width: fit-content; - margin: 0 auto 1em auto; + margin: 0 auto 1em; &.fullwidth { width: 100%; From eb54bbf8d4902987fee3e05854b01613ec384c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Fri, 13 Mar 2026 10:56:28 +0100 Subject: [PATCH 03/20] Left align fillwidth and form callouts --- asset/css/callout.less | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/asset/css/callout.less b/asset/css/callout.less index 1b90f64e7..920465e25 100644 --- a/asset/css/callout.less +++ b/asset/css/callout.less @@ -9,6 +9,13 @@ &.fullwidth { width: 100%; + justify-content: start; + } + + &.form-callout { + margin-left: 14em; + width: auto; + justify-content: start; } i.icon::before { @@ -23,11 +30,6 @@ display: flex; flex-direction: column; } - - &.form-callout { - margin-left: 14em; - width: auto; - } } // Style From bed4d4c1eed28f8b0e4ee38e26340eb1f656784c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Mon, 16 Mar 2026 09:34:08 +0100 Subject: [PATCH 04/20] Use constructor property promotion --- src/Widget/Callout.php | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/Widget/Callout.php b/src/Widget/Callout.php index 293f86c6a..a2555cc6f 100644 --- a/src/Widget/Callout.php +++ b/src/Widget/Callout.php @@ -23,21 +23,18 @@ class Callout extends BaseHtmlElement protected $defaultAttributes = ['class' => 'callout']; - /** @var string|null An optional title */ - protected ?string $title; - - /** @var string The content to display */ - protected string $content; - - /** @var CalloutType The type of callout, determines the color and icon */ - protected CalloutType $type; - - public function __construct(CalloutType $type, string $content, ?string $title = null) - { - $this->type = $type; - $this->content = $content; - $this->title = $title; - + /** + * Create a new callout + * + * @param CalloutType $type the type of the callout. The type determines the color and icon that is used. + * @param string $content the text content of the callout + * @param string|null $title an optional title, displayed above the content + */ + public function __construct( + protected CalloutType $type, + protected string $content, + protected ?string $title = null + ) { $this->addAttributes(Attributes::create(['class' => $type->value])); } From f27828d83d30f211982e86bf4a450f1e6c887076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Tue, 17 Mar 2026 10:39:10 +0100 Subject: [PATCH 05/20] Allow ValidHtml as the callout body --- asset/css/callout.less | 4 ++++ src/Widget/Callout.php | 40 ++++++++++++++++++++++------------------ 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/asset/css/callout.less b/asset/css/callout.less index 920465e25..336d3e886 100644 --- a/asset/css/callout.less +++ b/asset/css/callout.less @@ -26,6 +26,10 @@ margin: 0; } + .callout-title { + margin-bottom: .5em; + } + .callout-text { display: flex; flex-direction: column; diff --git a/src/Widget/Callout.php b/src/Widget/Callout.php index a2555cc6f..011ce1eb2 100644 --- a/src/Widget/Callout.php +++ b/src/Widget/Callout.php @@ -6,6 +6,7 @@ use ipl\Html\BaseHtmlElement; use ipl\Html\HtmlElement; use ipl\Html\Text; +use ipl\Html\ValidHtml; use ipl\I18n\Translation; use ipl\Web\Common\CalloutType; @@ -23,18 +24,27 @@ class Callout extends BaseHtmlElement protected $defaultAttributes = ['class' => 'callout']; + /** + * @var ValidHtml The content of the callout + */ + protected ValidHtml $content; + /** * Create a new callout * * @param CalloutType $type the type of the callout. The type determines the color and icon that is used. - * @param string $content the text content of the callout + * @param ValidHtml|string $content the content of the callout * @param string|null $title an optional title, displayed above the content */ public function __construct( protected CalloutType $type, - protected string $content, + ValidHtml|string $content, protected ?string $title = null ) { + if (is_string($content)) { + $content = new Text($content); + } + $this->content = $content; $this->addAttributes(Attributes::create(['class' => $type->value])); } @@ -42,22 +52,16 @@ public function assemble(): void { $this->addHtml($this->type->getIcon()); - if ($this->title) { - $this->addHtml(HtmlElement::create( - 'div', - ['class' => 'callout-text'], - [ - HtmlElement::create('strong', null, new Text($this->title)), - HtmlElement::create('p', null, new Text($this->content)), - ], - )); - } else { - $this->addHtml(HtmlElement::create( - 'div', - ['class' => 'callout-text'], - HtmlElement::create('strong', null, new Text($this->content)), - )); - } + $this->addHtml(HtmlElement::create( + 'div', + ['class' => 'callout-text'], + [ + $this->title + ? HtmlElement::create('strong', ['class' => 'callout-title'], new Text($this->title)) + : null, + $this->content, + ], + )); } /** From 40fac2457da4a255c5cb3bb8919fc09127583b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Tue, 24 Mar 2026 11:49:27 +0100 Subject: [PATCH 06/20] Prefix css classes --- asset/css/callout.less | 10 +++++----- src/Common/CalloutType.php | 20 ++++++++++---------- src/Widget/Callout.php | 4 ++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/asset/css/callout.less b/asset/css/callout.less index 336d3e886..fcb4fabf5 100644 --- a/asset/css/callout.less +++ b/asset/css/callout.less @@ -7,7 +7,7 @@ width: fit-content; margin: 0 auto 1em; - &.fullwidth { + &.callout-fullwidth { width: 100%; justify-content: start; } @@ -48,19 +48,19 @@ font-size: 1.5em; } - &.info { + &.callout-type-info { --callout-color: @color-pending; } - &.success { + &.callout-type-success { --callout-color: @color-ok; } - &.warning { + &.callout-type-warning { --callout-color: @color-warning; } - &.error { + &.callout-type-error { --callout-color: @color-critical; } } diff --git a/src/Common/CalloutType.php b/src/Common/CalloutType.php index aedc05ee0..b8d58cc0b 100644 --- a/src/Common/CalloutType.php +++ b/src/Common/CalloutType.php @@ -9,10 +9,10 @@ */ enum CalloutType: string { - case Info = "info"; - case Success = "success"; - case Warning = "warning"; - case Error = "error"; + case Info = 'callout-type-info'; + case Success = 'callout-type-success'; + case Warning = 'callout-type-warning'; + case Error = 'callout-type-error'; /** * Get the icon element for use in the callout @@ -21,11 +21,11 @@ enum CalloutType: string */ public function getIcon(): Icon { - return match ($this) { - self::Info => new Icon('circle-info'), - self::Success => new Icon('circle-check'), - self::Warning => new Icon('warning'), - self::Error => new Icon('circle-xmark'), - }; + return new Icon(match ($this) { + self::Info => 'circle-info', + self::Success => 'circle-check', + self::Warning => 'warning', + self::Error => 'circle-xmark', + }); } } diff --git a/src/Widget/Callout.php b/src/Widget/Callout.php index 011ce1eb2..78ecc7bdf 100644 --- a/src/Widget/Callout.php +++ b/src/Widget/Callout.php @@ -75,9 +75,9 @@ public function assemble(): void public function setFullwidth(bool $fullwidth = true): static { if ($fullwidth) { - $this->addAttributes(Attributes::create(['class' => 'fullwidth'])); + $this->addAttributes(Attributes::create(['class' => 'callout-fullwidth'])); } else { - $this->removeAttribute('class', 'fullwidth'); + $this->removeAttribute('class', 'callout-fullwidth'); } return $this; From 13f281174e71822e5a4195717e9953815ae712a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Tue, 24 Mar 2026 13:05:57 +0100 Subject: [PATCH 07/20] Move convesion from string to Text into Callout::assemble --- src/Widget/Callout.php | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/Widget/Callout.php b/src/Widget/Callout.php index 78ecc7bdf..b3bf9f3fc 100644 --- a/src/Widget/Callout.php +++ b/src/Widget/Callout.php @@ -24,11 +24,6 @@ class Callout extends BaseHtmlElement protected $defaultAttributes = ['class' => 'callout']; - /** - * @var ValidHtml The content of the callout - */ - protected ValidHtml $content; - /** * Create a new callout * @@ -38,13 +33,9 @@ class Callout extends BaseHtmlElement */ public function __construct( protected CalloutType $type, - ValidHtml|string $content, + protected ValidHtml|string $content, protected ?string $title = null ) { - if (is_string($content)) { - $content = new Text($content); - } - $this->content = $content; $this->addAttributes(Attributes::create(['class' => $type->value])); } @@ -59,7 +50,7 @@ public function assemble(): void $this->title ? HtmlElement::create('strong', ['class' => 'callout-title'], new Text($this->title)) : null, - $this->content, + is_string($this->content) ? new Text($this->content) : $this->content, ], )); } From b7a9484d5862abbb190875e891445685754352a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Mon, 11 May 2026 08:19:24 +0200 Subject: [PATCH 08/20] Apply suggestions from code review Co-authored-by: jrauh01 --- src/Widget/Callout.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Widget/Callout.php b/src/Widget/Callout.php index b3bf9f3fc..a106f58e9 100644 --- a/src/Widget/Callout.php +++ b/src/Widget/Callout.php @@ -39,7 +39,7 @@ public function __construct( $this->addAttributes(Attributes::create(['class' => $type->value])); } - public function assemble(): void + protected function assemble(): void { $this->addHtml($this->type->getIcon()); @@ -59,7 +59,7 @@ public function assemble(): void * Callouts are only as wide as their content. * Setting it to fullwidth will force the callout to be as wide as its container. * - * @param bool $fullwidth should the callout be fullwidth + * @param bool $fullwidth Whether the callout should be full width * * @return $this */ @@ -78,7 +78,7 @@ public function setFullwidth(bool $fullwidth = true): static * Setting this to true will allow the callout to be used for a single form element. * This is used to visually align the callout to the content of the form element. * - * @param bool $isFormElement should the callout be used for a form element + * @param bool $isFormElement Whether the callout should be used for a form element * * @return $this */ @@ -87,7 +87,7 @@ public function setFormElement(bool $isFormElement = true): static if ($isFormElement) { $this->addAttributes(Attributes::create(['class' => 'form-callout'])); } else { - $this->removeAttribute('class', 'from-callout'); + $this->removeAttribute('class', 'form-callout'); } return $this; From bca37158c101d0aa546db4a9588e4aa9a0e179fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Mon, 11 May 2026 08:57:12 +0200 Subject: [PATCH 09/20] Rename form-callout to callout-form-element & adjust docstrings --- asset/css/callout.less | 4 ++-- src/Widget/Callout.php | 40 ++++++++++++++++++++++++++-------------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/asset/css/callout.less b/asset/css/callout.less index fcb4fabf5..a83f3a710 100644 --- a/asset/css/callout.less +++ b/asset/css/callout.less @@ -7,12 +7,12 @@ width: fit-content; margin: 0 auto 1em; - &.callout-fullwidth { + &.callout-full-width { width: 100%; justify-content: start; } - &.form-callout { + &.callout-form-element { margin-left: 14em; width: auto; justify-content: start; diff --git a/src/Widget/Callout.php b/src/Widget/Callout.php index a106f58e9..7bdb9f75c 100644 --- a/src/Widget/Callout.php +++ b/src/Widget/Callout.php @@ -12,14 +12,20 @@ /** * An information box that can be used to display information to the user. - * It consists of a set of standardized colors and icons that can be used to visually distinguish the type of - * information. - * A content string and an optional title can be passed to the constructor. + * It consists of a set of standardized colors and icons that can be used to + * visually distinguish the type of information. A content string and an + * optional title can be passed to the constructor. */ class Callout extends BaseHtmlElement { use Translation; + /** @var string Classname for form-element callouts */ + protected const CLASS_FORM_ELEMENT = 'callout-form-element'; + + /** @var string Classname for full-width callouts */ + protected const CLASS_FULL_WIDTH = 'callout-full-width'; + protected $tag = 'div'; protected $defaultAttributes = ['class' => 'callout']; @@ -27,9 +33,11 @@ class Callout extends BaseHtmlElement /** * Create a new callout * - * @param CalloutType $type the type of the callout. The type determines the color and icon that is used. - * @param ValidHtml|string $content the content of the callout - * @param string|null $title an optional title, displayed above the content + * The $type parameter determines the color and icon of the callout. + * + * @param CalloutType $type The type of the callout. + * @param ValidHtml|string $content The content of the callout + * @param string|null $title An optional title, displayed above the content */ public function __construct( protected CalloutType $type, @@ -56,25 +64,29 @@ protected function assemble(): void } /** - * Callouts are only as wide as their content. + * Set the callout width to be 100% of its parent container + * + * Callouts are normally only as wide as their content. * Setting it to fullwidth will force the callout to be as wide as its container. * - * @param bool $fullwidth Whether the callout should be full width + * @param bool $isFullWidth Whether the callout should be full width * * @return $this */ - public function setFullwidth(bool $fullwidth = true): static + public function setFullwidth(bool $isFullWidth = true): static { - if ($fullwidth) { - $this->addAttributes(Attributes::create(['class' => 'callout-fullwidth'])); + if ($isFullWidth) { + $this->addAttributes(Attributes::create(['class' => static::CLASS_FULL_WIDTH])); } else { - $this->removeAttribute('class', 'callout-fullwidth'); + $this->removeAttribute('class', static::CLASS_FULL_WIDTH); } return $this; } /** + * Set up the callout to be used inside a form + * * Setting this to true will allow the callout to be used for a single form element. * This is used to visually align the callout to the content of the form element. * @@ -85,9 +97,9 @@ public function setFullwidth(bool $fullwidth = true): static public function setFormElement(bool $isFormElement = true): static { if ($isFormElement) { - $this->addAttributes(Attributes::create(['class' => 'form-callout'])); + $this->addAttributes(Attributes::create(['class' => static::CLASS_FORM_ELEMENT])); } else { - $this->removeAttribute('class', 'form-callout'); + $this->removeAttribute('class', static::CLASS_FORM_ELEMENT); } return $this; From 9b55d42b4d429645103bf6d1c07769274523e958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Mon, 11 May 2026 09:53:40 +0200 Subject: [PATCH 10/20] Apply suggestions from code review Co-authored-by: jrauh01 --- src/Widget/Callout.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Widget/Callout.php b/src/Widget/Callout.php index 7bdb9f75c..a35b547af 100644 --- a/src/Widget/Callout.php +++ b/src/Widget/Callout.php @@ -20,10 +20,10 @@ class Callout extends BaseHtmlElement { use Translation; - /** @var string Classname for form-element callouts */ + /** @var string Class name for form element callouts */ protected const CLASS_FORM_ELEMENT = 'callout-form-element'; - /** @var string Classname for full-width callouts */ + /** @var string Class name for full width callouts */ protected const CLASS_FULL_WIDTH = 'callout-full-width'; protected $tag = 'div'; @@ -35,9 +35,9 @@ class Callout extends BaseHtmlElement * * The $type parameter determines the color and icon of the callout. * - * @param CalloutType $type The type of the callout. + * @param CalloutType $type The type of the callout * @param ValidHtml|string $content The content of the callout - * @param string|null $title An optional title, displayed above the content + * @param ?string $title An optional title, displayed above the content */ public function __construct( protected CalloutType $type, @@ -56,9 +56,9 @@ protected function assemble(): void ['class' => 'callout-text'], [ $this->title - ? HtmlElement::create('strong', ['class' => 'callout-title'], new Text($this->title)) + ? HtmlElement::create('strong', ['class' => 'callout-title'], Text::create($this->title)) : null, - is_string($this->content) ? new Text($this->content) : $this->content, + is_string($this->content) ? Text::create($this->content) : $this->content, ], )); } From b97d20ea6388d95362cc496be9d2a2fded84ccfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Mon, 11 May 2026 11:12:36 +0200 Subject: [PATCH 11/20] Update src/Widget/Callout.php Change setFullwidth to setFullWidth Co-authored-by: jrauh01 --- src/Widget/Callout.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Widget/Callout.php b/src/Widget/Callout.php index a35b547af..3eddf54b2 100644 --- a/src/Widget/Callout.php +++ b/src/Widget/Callout.php @@ -73,7 +73,7 @@ protected function assemble(): void * * @return $this */ - public function setFullwidth(bool $isFullWidth = true): static + public function setFullWidth(bool $isFullWidth = true): static { if ($isFullWidth) { $this->addAttributes(Attributes::create(['class' => static::CLASS_FULL_WIDTH])); From 01c6fb95111741b9c32000fe87f43620fdd8e007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Mon, 11 May 2026 15:02:08 +0200 Subject: [PATCH 12/20] Add comment to hardcoded margin --- asset/css/callout.less | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/asset/css/callout.less b/asset/css/callout.less index a83f3a710..12daf8b9c 100644 --- a/asset/css/callout.less +++ b/asset/css/callout.less @@ -13,6 +13,11 @@ } &.callout-form-element { + /* + * 14em is the width of the `control-label-group` element. This property value assumes that there are no + * special cases for which the label width differs. There is no way to get the width of the form label + * programmatically. + */ margin-left: 14em; width: auto; justify-content: start; From 3a9cce0957fecc7db954291d856589bba722c25b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Tue, 12 May 2026 09:18:59 +0200 Subject: [PATCH 13/20] Apply suggestions from code review Co-authored-by: jrauh01 --- src/Common/CalloutType.php | 2 +- src/Widget/Callout.php | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Common/CalloutType.php b/src/Common/CalloutType.php index b8d58cc0b..0ca328905 100644 --- a/src/Common/CalloutType.php +++ b/src/Common/CalloutType.php @@ -5,7 +5,7 @@ use ipl\Web\Widget\Icon; /** - * An enum containing all possible callout types for the {@see Callout} widget. + * An enum containing all possible callout types for the {@see Callout} widget */ enum CalloutType: string { diff --git a/src/Widget/Callout.php b/src/Widget/Callout.php index 3eddf54b2..7b9c5423b 100644 --- a/src/Widget/Callout.php +++ b/src/Widget/Callout.php @@ -11,10 +11,10 @@ use ipl\Web\Common\CalloutType; /** - * An information box that can be used to display information to the user. - * It consists of a set of standardized colors and icons that can be used to - * visually distinguish the type of information. A content string and an - * optional title can be passed to the constructor. + * Information box with a type specific color and icon + * + * The type controls both the color scheme and the icon. An optional title + * is displayed above the content. */ class Callout extends BaseHtmlElement { @@ -64,10 +64,10 @@ protected function assemble(): void } /** - * Set the callout width to be 100% of its parent container + * Set the callout width to 100% of its parent container * * Callouts are normally only as wide as their content. - * Setting it to fullwidth will force the callout to be as wide as its container. + * Setting it to full width will force the callout to be as wide as its container. * * @param bool $isFullWidth Whether the callout should be full width * From 5628c305edb89f088583631dfda184f14b8fcd5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Tue, 12 May 2026 09:21:04 +0200 Subject: [PATCH 14/20] Remove unused translation --- src/Widget/Callout.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Widget/Callout.php b/src/Widget/Callout.php index 7b9c5423b..3e80ce280 100644 --- a/src/Widget/Callout.php +++ b/src/Widget/Callout.php @@ -7,7 +7,6 @@ use ipl\Html\HtmlElement; use ipl\Html\Text; use ipl\Html\ValidHtml; -use ipl\I18n\Translation; use ipl\Web\Common\CalloutType; /** @@ -18,8 +17,6 @@ */ class Callout extends BaseHtmlElement { - use Translation; - /** @var string Class name for form element callouts */ protected const CLASS_FORM_ELEMENT = 'callout-form-element'; From 78cc2e683cb68614daf5558440c16f3377aec847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Wed, 13 May 2026 09:22:43 +0200 Subject: [PATCH 15/20] Explicitly check against null This allows for a title with a string that evaluates to a falsy value --- src/Widget/Callout.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Widget/Callout.php b/src/Widget/Callout.php index 3e80ce280..8d0369a98 100644 --- a/src/Widget/Callout.php +++ b/src/Widget/Callout.php @@ -2,6 +2,7 @@ namespace ipl\Web\Widget; +use InvalidArgumentException; use ipl\Html\Attributes; use ipl\Html\BaseHtmlElement; use ipl\Html\HtmlElement; @@ -52,7 +53,7 @@ protected function assemble(): void 'div', ['class' => 'callout-text'], [ - $this->title + $this->title !== null ? HtmlElement::create('strong', ['class' => 'callout-title'], Text::create($this->title)) : null, is_string($this->content) ? Text::create($this->content) : $this->content, From 87b88b6a6c0d6a8284165ceae3885c73a3853e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Wed, 13 May 2026 09:22:49 +0200 Subject: [PATCH 16/20] Add unit tests --- tests/Widget/CalloutTest.php | 81 ++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 tests/Widget/CalloutTest.php diff --git a/tests/Widget/CalloutTest.php b/tests/Widget/CalloutTest.php new file mode 100644 index 000000000..4d72c42a2 --- /dev/null +++ b/tests/Widget/CalloutTest.php @@ -0,0 +1,81 @@ + + +
+ Title + Content +
+ +HTML; + + $this->assertHtml($html, $callout); + } + + public function testCalloutFalsyTitle(): void + { + $callout = new Callout(CalloutType::Warning, 'Content', '0'); + + $html = <<<'HTML' +
+ +
+ 0 + Content +
+
+HTML; + $this->assertHtml($html, $callout); + } + + public function testCalloutEmptyTitle(): void + { + $callout = new Callout(CalloutType::Error, 'Content', ''); + + $html = <<<'HTML' +
+ +
+ + Content +
+
+HTML; + $this->assertHtml($html, $callout); + } + + public function testCalloutValidHtmlContent(): void + { + $callout = new Callout( + CalloutType::Success, + Html::tag('p', ['class' => 'test-class'], 'This is a Test'), + 'Test Title', + ); + + $html = <<<'HTML' +
+ +
+ Test Title +

This is a Test

+
+
+HTML; + + $this->assertHtml($html, $callout); + } +} From d0361f085766387d284901b97ecce17ebc1f3dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Wed, 13 May 2026 09:37:11 +0200 Subject: [PATCH 17/20] fixup! --- tests/Widget/CalloutTest.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/Widget/CalloutTest.php b/tests/Widget/CalloutTest.php index 4d72c42a2..475b8cc9c 100644 --- a/tests/Widget/CalloutTest.php +++ b/tests/Widget/CalloutTest.php @@ -9,6 +9,22 @@ class CalloutTest extends TestCase { + public function testCalloutWithoutTitle(): void + { + $callout = new Callout(CalloutType::Info, 'Content'); + + $html = <<<'HTML' +
+ +
+ Content +
+
+HTML; + + $this->assertHtml($html, $callout); + } + public function testCalloutWithTitle(): void { $callout = new Callout(CalloutType::Info, 'Content', 'Title'); From cfc6c1d06f95e17d11db6366a238311e96056296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Wed, 13 May 2026 10:17:31 +0200 Subject: [PATCH 18/20] Remove unused using Co-authored-by: jrauh01 --- src/Widget/Callout.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Widget/Callout.php b/src/Widget/Callout.php index 8d0369a98..1296c5487 100644 --- a/src/Widget/Callout.php +++ b/src/Widget/Callout.php @@ -2,7 +2,6 @@ namespace ipl\Web\Widget; -use InvalidArgumentException; use ipl\Html\Attributes; use ipl\Html\BaseHtmlElement; use ipl\Html\HtmlElement; From a4ca1e461b42ef750b98cca1489cde609d761c08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Wed, 13 May 2026 10:30:28 +0200 Subject: [PATCH 19/20] Add tests for setFormElement and setFullWidth --- tests/Widget/CalloutTest.php | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/Widget/CalloutTest.php b/tests/Widget/CalloutTest.php index 475b8cc9c..111890697 100644 --- a/tests/Widget/CalloutTest.php +++ b/tests/Widget/CalloutTest.php @@ -94,4 +94,60 @@ public function testCalloutValidHtmlContent(): void $this->assertHtml($html, $callout); } + + public function testCalloutFullWidth(): void + { + $callout = (new Callout(CalloutType::Error, 'Content')) + ->setFullWidth(true); + + $html = <<<'HTML' +
+ +
+ Content +
+
+HTML; + $this->assertHtml($html, $callout); + + $callout->setFullWidth(false); + + $html2 = <<<'HTML' +
+ +
+ Content +
+
+HTML; + $this->assertHtml($html2, $callout); + } + + public function testCalloutFormElement(): void + { + $callout = (new Callout(CalloutType::Error, 'Content')) + ->setFormElement(true); + + $html = <<<'HTML' +
+ +
+ Content +
+
+HTML; + $this->assertHtml($html, $callout); + + $callout->setFormElement(false); + + $html2 = <<<'HTML' +
+ +
+ Content +
+
+HTML; + $this->assertHtml($html2, $callout); + } } From 0447547e7b05f5ee40fc42aede1787ed98acd5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rie=C3=9F?= Date: Wed, 13 May 2026 10:35:30 +0200 Subject: [PATCH 20/20] Fully qualify Callout class --- src/Common/CalloutType.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/CalloutType.php b/src/Common/CalloutType.php index 0ca328905..ea86d5679 100644 --- a/src/Common/CalloutType.php +++ b/src/Common/CalloutType.php @@ -5,7 +5,7 @@ use ipl\Web\Widget\Icon; /** - * An enum containing all possible callout types for the {@see Callout} widget + * An enum containing all possible callout types for the {@see \ipl\Web\Widget\Callout} widget */ enum CalloutType: string {