From a284e26d336aef5bb17732d268adf4f91aef7480 Mon Sep 17 00:00:00 2001 From: iszmais <45942348+iszmais@users.noreply.github.com> Date: Tue, 15 Mar 2022 10:28:53 +0100 Subject: [PATCH] implements Toast container and set dependency in standard toast (#4059) --- src/UI/Component/Toast/Toast.php | 3 +- .../Component/Toast/Container.php | 48 +++++++++ .../Component/Toast/Factory.php | 3 +- .../Component/Toast/Renderer.php | 31 +++++- .../Implementation/Component/Toast/Toast.php | 3 + src/UI/examples/Toast/Container/base.php | 18 +++- .../Toast/Container/with_multiple_toasts.php | 48 +++++---- .../examples/Toast/Container/with_toast.php | 21 +++- src/UI/examples/Toast/Standard/base.php | 21 +++- .../examples/Toast/Standard/with_action.php | 22 ++++- .../Toast/Standard/with_additional_links.php | 26 ++++- .../Toast/Standard/with_description.php | 22 ++++- .../Toast/Standard/with_link_title.php | 22 ++++- .../Toast/Standard/with_long_description.php | 22 ++++- src/UI/templates/default/Toast/toast.less | 15 ++- .../default/Toast/tpl.container.html | 1 + src/UI/templates/default/Toast/tpl.toast.html | 3 +- src/UI/templates/js/Toast/toast.js | 25 +---- templates/default/delos.css | 13 ++- templates/default/less/variables.less | 1 + .../Toast/Standard/StandardToastTest.html | 15 --- tests/UI/Client/Toast/ToastTest.html | 22 +++++ .../standard.test.js => toast.test.js} | 13 +-- .../Component/Toast/ToastClientHtmlTest.php | 75 ++++++++++++++ tests/UI/Component/Toast/ToastTest.php | 97 +++++++++++++++++++ 25 files changed, 482 insertions(+), 108 deletions(-) create mode 100644 src/UI/Implementation/Component/Toast/Container.php create mode 100644 src/UI/templates/default/Toast/tpl.container.html delete mode 100644 tests/UI/Client/Toast/Standard/StandardToastTest.html create mode 100644 tests/UI/Client/Toast/ToastTest.html rename tests/UI/Client/Toast/{Standard/standard.test.js => toast.test.js} (86%) create mode 100644 tests/UI/Component/Toast/ToastClientHtmlTest.php create mode 100644 tests/UI/Component/Toast/ToastTest.php diff --git a/src/UI/Component/Toast/Toast.php b/src/UI/Component/Toast/Toast.php index aa4f34e8dc91..79747e9f6499 100644 --- a/src/UI/Component/Toast/Toast.php +++ b/src/UI/Component/Toast/Toast.php @@ -4,6 +4,7 @@ use ILIAS\UI\Component\Button\Shy; use ILIAS\UI\Component\Component; +use ILIAS\UI\Component\JavaScriptBindable; use ILIAS\UI\Component\Link\Link; use ILIAS\UI\Component\Signal; use ILIAS\UI\Component\Symbol\Icon\Icon; @@ -13,7 +14,7 @@ * Interface Toast * @package ILIAS\UI\Component\Toast */ -interface Toast extends Component +interface Toast extends Component, JavaScriptBindable { /** * @return string|Shy|Link diff --git a/src/UI/Implementation/Component/Toast/Container.php b/src/UI/Implementation/Component/Toast/Container.php new file mode 100644 index 000000000000..f05832700236 --- /dev/null +++ b/src/UI/Implementation/Component/Toast/Container.php @@ -0,0 +1,48 @@ +toasts; + } + + public function withAdditionalToast(ComponentInterface\Toast $toast) : Container + { + $clone = clone $this; + $clone->toasts[] = $toast; + return $clone; + } + + public function withoutToasts() : Container + { + $clone = clone $this; + $clone->toasts = []; + return $clone; + } +} diff --git a/src/UI/Implementation/Component/Toast/Factory.php b/src/UI/Implementation/Component/Toast/Factory.php index afb59c7bf8ba..837ad4a21d05 100644 --- a/src/UI/Implementation/Component/Toast/Factory.php +++ b/src/UI/Implementation/Component/Toast/Factory.php @@ -3,7 +3,6 @@ namespace ILIAS\UI\Implementation\Component\Toast; use ILIAS\UI\Component\Symbol\Icon\Icon; -use ILIAS\UI\Component\Toast\Container; use ILIAS\UI\Implementation\Component\SignalGeneratorInterface; class Factory implements \ILIAS\UI\Component\Toast\Factory @@ -25,6 +24,6 @@ public function standard($title, Icon $icon) : Toast public function container() : Container { - throw new \ILIAS\UI\NotImplementedException(); + return new Container(); } } diff --git a/src/UI/Implementation/Component/Toast/Renderer.php b/src/UI/Implementation/Component/Toast/Renderer.php index 5fd6dab1f7e2..755e2aa2555d 100644 --- a/src/UI/Implementation/Component/Toast/Renderer.php +++ b/src/UI/Implementation/Component/Toast/Renderer.php @@ -2,7 +2,6 @@ namespace ILIAS\UI\Implementation\Component\Toast; -use ilException; use ILIAS\UI\Component\Button\Shy; use ILIAS\UI\Component\Link\Link; use ILIAS\UI\Implementation\Render\AbstractComponentRenderer; @@ -23,6 +22,9 @@ public function render(Component\Component $component, RendererInterface $defaul if ($component instanceof Component\Toast\Toast) { return $this->renderToast($component, $default_renderer); } + if ($component instanceof Component\Toast\Container) { + return $this->renderContainer($component, $default_renderer); + } throw new LogicException("Cannot render: " . get_class($component)); } @@ -62,16 +64,41 @@ protected function renderToast(Component\Toast\Toast $component, RendererInterfa $tpl->setVariable("ICON", $default_renderer->render($component->getIcon())); $tpl->setVariable("CLOSE", $default_renderer->render($this->getUIFactory()->button()->close())); + $component = $component->withAdditionalOnLoadCode(function ($id) { + return " + il.UI.toast.setToastSettings($id); + il.UI.toast.showToast($id); + "; + }); + + $tpl->setCurrentBlock("id"); + $tpl->setVariable('ID', $this->bindJavaScript($component)); + $tpl->parseCurrentBlock(); + + return $tpl->get(); + } + + protected function renderContainer(Component\Toast\Container $component, RendererInterface $default_renderer) : string + { + $tpl = $this->getTemplate("tpl.container.html", true, true); + $tpl->setVariable("TOASTS", $default_renderer->render($component->getToasts())); return $tpl->get(); } + public function registerResources(ResourceRegistry $registry) : void + { + parent::registerResources($registry); + $registry->register('./src/UI/templates/js/Toast/toast.js'); + } + /** * @inheritdoc */ protected function getComponentInterfaceName() : array { return [ - Component\Toast\Toast::class + Component\Toast\Toast::class, + Component\Toast\Container::class ]; } } diff --git a/src/UI/Implementation/Component/Toast/Toast.php b/src/UI/Implementation/Component/Toast/Toast.php index 8610bba86d4c..8e116154d737 100644 --- a/src/UI/Implementation/Component/Toast/Toast.php +++ b/src/UI/Implementation/Component/Toast/Toast.php @@ -8,11 +8,14 @@ use ILIAS\UI\Component\Symbol\Icon\Icon; use ILIAS\UI\Implementation\Component\ComponentHelper; use ILIAS\UI\Component\Toast as ComponentInterface; +use ILIAS\UI\Implementation\Component\JavaScriptBindable; use ILIAS\UI\Implementation\Component\SignalGeneratorInterface; class Toast implements ComponentInterface\Toast { use ComponentHelper; + use JavaScriptBindable; + public const DEFAULT_VANISH_TIME_IN_MS = 5000; public const DEFAULT_DELAY_TIME_IN_MS = 500; diff --git a/src/UI/examples/Toast/Container/base.php b/src/UI/examples/Toast/Container/base.php index 01afe8d0be36..4801f89d7cb2 100644 --- a/src/UI/examples/Toast/Container/base.php +++ b/src/UI/examples/Toast/Container/base.php @@ -6,5 +6,21 @@ function base() : string { global $DIC; $tc = $DIC->ui()->factory()->toast()->container(); - return $DIC->ui()->renderer()->render($tc); + + $toasts = []; + + $toasts = base64_encode($DIC->ui()->renderer()->renderAsync($toasts)); + $button = $DIC->ui()->factory()->button()->standard($DIC->language()->txt('show'), ''); + $button = $button->withAdditionalOnLoadCode(function ($id) use ($toasts) { + return "$id.addEventListener('click', () => { + $id.parentNode.querySelector('.il-toast-container').innerHTML = atob('$toasts'); + $id.parentNode.querySelector('.il-toast-container').querySelectorAll('script').forEach(element => { + let newScript = document.createElement('script'); + newScript.innerHTML = element.innerHTML; + element.parentNode.appendChild(newScript); + }) + });"; + }); + + return $DIC->ui()->renderer()->render([$button,$tc]); } diff --git a/src/UI/examples/Toast/Container/with_multiple_toasts.php b/src/UI/examples/Toast/Container/with_multiple_toasts.php index c3e9cf5a1444..7fa9c3461656 100644 --- a/src/UI/examples/Toast/Container/with_multiple_toasts.php +++ b/src/UI/examples/Toast/Container/with_multiple_toasts.php @@ -5,23 +5,35 @@ function with_multiple_toasts() : string { global $DIC; - $tc = $DIC->ui()->factory()->toast()->container() - ->withAdditionalToast( - $DIC->ui()->factory()->toast()->standard( - 'Example 1', - $DIC->ui()->factory()->symbol()->icon()->standard('info', 'Test') - ) - )->withAdditionalToast( - $DIC->ui()->factory()->toast()->standard( - 'Example 2', - $DIC->ui()->factory()->symbol()->icon()->standard('info', 'Test') - ) - )->withAdditionalToast( - $DIC->ui()->factory()->toast()->standard( - 'Example 3', - $DIC->ui()->factory()->symbol()->icon()->standard('info', 'Test') - ) - ); + $tc = $DIC->ui()->factory()->toast()->container(); - return $DIC->ui()->renderer()->render($tc); + $toasts = [ + $DIC->ui()->factory()->toast()->standard( + 'Example 1', + $DIC->ui()->factory()->symbol()->icon()->standard('mail', 'Test')->withIsOutlined(true) + ), + $DIC->ui()->factory()->toast()->standard( + 'Example 2', + $DIC->ui()->factory()->symbol()->icon()->standard('mail', 'Test')->withIsOutlined(true) + ), + $DIC->ui()->factory()->toast()->standard( + 'Example 3', + $DIC->ui()->factory()->symbol()->icon()->standard('mail', 'Test')->withIsOutlined(true) + ) + ]; + + $toasts = base64_encode($DIC->ui()->renderer()->renderAsync($toasts)); + $button = $DIC->ui()->factory()->button()->standard($DIC->language()->txt('show'), ''); + $button = $button->withAdditionalOnLoadCode(function ($id) use ($toasts) { + return "$id.addEventListener('click', () => { + $id.parentNode.querySelector('.il-toast-container').innerHTML = atob('$toasts'); + $id.parentNode.querySelector('.il-toast-container').querySelectorAll('script').forEach(element => { + let newScript = document.createElement('script'); + newScript.innerHTML = element.innerHTML; + element.parentNode.appendChild(newScript); + }) + });"; + }); + + return $DIC->ui()->renderer()->render([$button,$tc]); } diff --git a/src/UI/examples/Toast/Container/with_toast.php b/src/UI/examples/Toast/Container/with_toast.php index 66ddbbdc235d..d6086ba4f530 100644 --- a/src/UI/examples/Toast/Container/with_toast.php +++ b/src/UI/examples/Toast/Container/with_toast.php @@ -5,12 +5,27 @@ function with_toast() : string { global $DIC; - $tc = $DIC->ui()->factory()->toast()->container()->withAdditionalToast( + $tc = $DIC->ui()->factory()->toast()->container(); + + $toasts = [ $DIC->ui()->factory()->toast()->standard( 'Example', $DIC->ui()->factory()->symbol()->icon()->standard('info', 'Test') ) - ); + ]; + + $toasts = base64_encode($DIC->ui()->renderer()->renderAsync($toasts)); + $button = $DIC->ui()->factory()->button()->standard($DIC->language()->txt('show'), ''); + $button = $button->withAdditionalOnLoadCode(function ($id) use ($toasts) { + return "$id.addEventListener('click', () => { + $id.parentNode.querySelector('.il-toast-container').innerHTML = atob('$toasts'); + $id.parentNode.querySelector('.il-toast-container').querySelectorAll('script').forEach(element => { + let newScript = document.createElement('script'); + newScript.innerHTML = element.innerHTML; + element.parentNode.appendChild(newScript); + }) + });"; + }); - return $DIC->ui()->renderer()->render($tc); + return $DIC->ui()->renderer()->render([$button,$tc]); } diff --git a/src/UI/examples/Toast/Standard/base.php b/src/UI/examples/Toast/Standard/base.php index 1e62d1ca0fda..b5ff46a573e4 100644 --- a/src/UI/examples/Toast/Standard/base.php +++ b/src/UI/examples/Toast/Standard/base.php @@ -5,12 +5,27 @@ function base() : string { global $DIC; - $tc = $DIC->ui()->factory()->toast()->container()->withAdditionalToast( + $tc = $DIC->ui()->factory()->toast()->container(); + + $toasts = [ $DIC->ui()->factory()->toast()->standard( 'Example', $DIC->ui()->factory()->symbol()->icon()->standard('info', 'Test') ) - ); + ]; + + $toasts = base64_encode($DIC->ui()->renderer()->renderAsync($toasts)); + $button = $DIC->ui()->factory()->button()->standard($DIC->language()->txt('show'), ''); + $button = $button->withAdditionalOnLoadCode(function ($id) use ($toasts) { + return "$id.addEventListener('click', () => { + $id.parentNode.querySelector('.il-toast-container').innerHTML = atob('$toasts'); + $id.parentNode.querySelector('.il-toast-container').querySelectorAll('script').forEach(element => { + let newScript = document.createElement('script'); + newScript.innerHTML = element.innerHTML; + element.parentNode.appendChild(newScript); + }) + });"; + }); - return $DIC->ui()->renderer()->render($tc); + return $DIC->ui()->renderer()->render([$button,$tc]); } diff --git a/src/UI/examples/Toast/Standard/with_action.php b/src/UI/examples/Toast/Standard/with_action.php index 95ecf0640056..1aaaedd64b7e 100644 --- a/src/UI/examples/Toast/Standard/with_action.php +++ b/src/UI/examples/Toast/Standard/with_action.php @@ -5,11 +5,27 @@ function with_action() : string { global $DIC; - $tc = $DIC->ui()->factory()->toast()->container()->withAdditionalToast( + $tc = $DIC->ui()->factory()->toast()->container(); + + $toasts = [ $DIC->ui()->factory()->toast()->standard( 'Example', $DIC->ui()->factory()->symbol()->icon()->standard('info', 'Test') )->withAction('https://www.ilias.de') - ); - return $DIC->ui()->renderer()->render($tc); + ]; + + $toasts = base64_encode($DIC->ui()->renderer()->renderAsync($toasts)); + $button = $DIC->ui()->factory()->button()->standard($DIC->language()->txt('show'), ''); + $button = $button->withAdditionalOnLoadCode(function ($id) use ($toasts) { + return "$id.addEventListener('click', () => { + $id.parentNode.querySelector('.il-toast-container').innerHTML = atob('$toasts'); + $id.parentNode.querySelector('.il-toast-container').querySelectorAll('script').forEach(element => { + let newScript = document.createElement('script'); + newScript.innerHTML = element.innerHTML; + element.parentNode.appendChild(newScript); + }) + });"; + }); + + return $DIC->ui()->renderer()->render([$button,$tc]); } diff --git a/src/UI/examples/Toast/Standard/with_additional_links.php b/src/UI/examples/Toast/Standard/with_additional_links.php index 343f06439fa5..e520befe8d7d 100644 --- a/src/UI/examples/Toast/Standard/with_additional_links.php +++ b/src/UI/examples/Toast/Standard/with_additional_links.php @@ -5,13 +5,29 @@ function with_additional_links() : string { global $DIC; - $tc = $DIC->ui()->factory()->toast()->container()->withAdditionalToast( + $tc = $DIC->ui()->factory()->toast()->container(); + + $toasts = [ $DIC->ui()->factory()->toast()->standard( 'Example', $DIC->ui()->factory()->symbol()->icon()->standard('info', 'Example') ) - ->withAdditionalLink($DIC->ui()->factory()->link()->standard('ILIAS', 'https://www.ilias.de')) - ->withAdditionalLink($DIC->ui()->factory()->link()->standard('GitHub', 'https://www.github.com')) - ); - return $DIC->ui()->renderer()->render($tc); + ->withAdditionalLink($DIC->ui()->factory()->link()->standard('ILIAS', 'https://www.ilias.de')) + ->withAdditionalLink($DIC->ui()->factory()->link()->standard('GitHub', 'https://www.github.com')) + ]; + + $toasts = base64_encode($DIC->ui()->renderer()->renderAsync($toasts)); + $button = $DIC->ui()->factory()->button()->standard($DIC->language()->txt('show'), ''); + $button = $button->withAdditionalOnLoadCode(function ($id) use ($toasts) { + return "$id.addEventListener('click', () => { + $id.parentNode.querySelector('.il-toast-container').innerHTML = atob('$toasts'); + $id.parentNode.querySelector('.il-toast-container').querySelectorAll('script').forEach(element => { + let newScript = document.createElement('script'); + newScript.innerHTML = element.innerHTML; + element.parentNode.appendChild(newScript); + }) + });"; + }); + + return $DIC->ui()->renderer()->render([$button,$tc]); } diff --git a/src/UI/examples/Toast/Standard/with_description.php b/src/UI/examples/Toast/Standard/with_description.php index e4c07e2ad75a..c0bf0272739a 100644 --- a/src/UI/examples/Toast/Standard/with_description.php +++ b/src/UI/examples/Toast/Standard/with_description.php @@ -5,11 +5,27 @@ function with_description() : string { global $DIC; - $tc = $DIC->ui()->factory()->toast()->container()->withAdditionalToast( + $tc = $DIC->ui()->factory()->toast()->container(); + + $toasts = [ $DIC->ui()->factory()->toast()->standard( 'Example', $DIC->ui()->factory()->symbol()->icon()->standard('info', 'Test') )->withDescription('This is an example description.') - ); - return $DIC->ui()->renderer()->render($tc); + ]; + + $toasts = base64_encode($DIC->ui()->renderer()->renderAsync($toasts)); + $button = $DIC->ui()->factory()->button()->standard($DIC->language()->txt('show'), ''); + $button = $button->withAdditionalOnLoadCode(function ($id) use ($toasts) { + return "$id.addEventListener('click', () => { + $id.parentNode.querySelector('.il-toast-container').innerHTML = atob('$toasts'); + $id.parentNode.querySelector('.il-toast-container').querySelectorAll('script').forEach(element => { + let newScript = document.createElement('script'); + newScript.innerHTML = element.innerHTML; + element.parentNode.appendChild(newScript); + }) + });"; + }); + + return $DIC->ui()->renderer()->render([$button,$tc]); } diff --git a/src/UI/examples/Toast/Standard/with_link_title.php b/src/UI/examples/Toast/Standard/with_link_title.php index 5e6c9cbe55ae..407d336ba496 100644 --- a/src/UI/examples/Toast/Standard/with_link_title.php +++ b/src/UI/examples/Toast/Standard/with_link_title.php @@ -5,11 +5,27 @@ function with_link_title() : string { global $DIC; - $tc = $DIC->ui()->factory()->toast()->container()->withAdditionalToast( + $tc = $DIC->ui()->factory()->toast()->container(); + + $toasts = [ $DIC->ui()->factory()->toast()->standard( $DIC->ui()->factory()->link()->standard('Example', 'https://www.ilias.de'), $DIC->ui()->factory()->symbol()->icon()->standard('info', 'Test') ) - ); - return $DIC->ui()->renderer()->render($tc); + ]; + + $toasts = base64_encode($DIC->ui()->renderer()->renderAsync($toasts)); + $button = $DIC->ui()->factory()->button()->standard($DIC->language()->txt('show'), ''); + $button = $button->withAdditionalOnLoadCode(function ($id) use ($toasts) { + return "$id.addEventListener('click', () => { + $id.parentNode.querySelector('.il-toast-container').innerHTML = atob('$toasts'); + $id.parentNode.querySelector('.il-toast-container').querySelectorAll('script').forEach(element => { + let newScript = document.createElement('script'); + newScript.innerHTML = element.innerHTML; + element.parentNode.appendChild(newScript); + }) + });"; + }); + + return $DIC->ui()->renderer()->render([$button,$tc]); } diff --git a/src/UI/examples/Toast/Standard/with_long_description.php b/src/UI/examples/Toast/Standard/with_long_description.php index d122c420fd6d..0b5cc28b9bfd 100644 --- a/src/UI/examples/Toast/Standard/with_long_description.php +++ b/src/UI/examples/Toast/Standard/with_long_description.php @@ -5,7 +5,9 @@ function with_long_description() : string { global $DIC; - $tc = $DIC->ui()->factory()->toast()->container()->withAdditionalToast( + $tc = $DIC->ui()->factory()->toast()->container(); + + $toasts = [ $DIC->ui()->factory()->toast()->standard( 'Example', $DIC->ui()->factory()->symbol()->icon()->standard('info', 'Test') @@ -18,6 +20,20 @@ function with_long_description() : string 'scalability of the object and could therefore be called to proof its responsivity which confirms its benefit ' . 'as an example in spite of its unnatural form and missing usecase for productive systems' ) - ); - return $DIC->ui()->renderer()->render($tc); + ]; + + $toasts = base64_encode($DIC->ui()->renderer()->renderAsync($toasts)); + $button = $DIC->ui()->factory()->button()->standard($DIC->language()->txt('show'), ''); + $button = $button->withAdditionalOnLoadCode(function ($id) use ($toasts) { + return "$id.addEventListener('click', () => { + $id.parentNode.querySelector('.il-toast-container').innerHTML = atob('$toasts'); + $id.parentNode.querySelector('.il-toast-container').querySelectorAll('script').forEach(element => { + let newScript = document.createElement('script'); + newScript.innerHTML = element.innerHTML; + element.parentNode.appendChild(newScript); + }) + });"; + }); + + return $DIC->ui()->renderer()->render([$button,$tc]); } diff --git a/src/UI/templates/default/Toast/toast.less b/src/UI/templates/default/Toast/toast.less index dda34391cdea..d6dff4523563 100644 --- a/src/UI/templates/default/Toast/toast.less +++ b/src/UI/templates/default/Toast/toast.less @@ -1,13 +1,20 @@ -.il-toast-wrapper { - position: absolute; +.il-toast-container { + position: fixed; z-index: 1; - top: 0; + top: @il-toast-container-top; right: 0; + width: @il-toast-width; +} + +.il-toast-container:empty { + display: none; +} + +.il-toast-wrapper { overflow: hidden; } .il-toast { - width: @il-toast-width; margin: @il-toast-margin; padding: @il-padding-base; background-color: white; diff --git a/src/UI/templates/default/Toast/tpl.container.html b/src/UI/templates/default/Toast/tpl.container.html new file mode 100644 index 000000000000..2335965a5a0f --- /dev/null +++ b/src/UI/templates/default/Toast/tpl.container.html @@ -0,0 +1 @@ +
{TOASTS}
\ No newline at end of file diff --git a/src/UI/templates/default/Toast/tpl.toast.html b/src/UI/templates/default/Toast/tpl.toast.html index 80203a8f2dee..4ec287672710 100644 --- a/src/UI/templates/default/Toast/tpl.toast.html +++ b/src/UI/templates/default/Toast/tpl.toast.html @@ -1,4 +1,4 @@ -
+
id="{ID}" data-vanishurl="{VANISH_ASYNC}" data-vanish="{TOAST_VANISH}" data-delay="{TOAST_DELAY}">
{ICON}
{TITLE}
@@ -12,5 +12,4 @@
-
\ No newline at end of file diff --git a/src/UI/templates/js/Toast/toast.js b/src/UI/templates/js/Toast/toast.js index 6f1ce6520609..f3a7848ef64f 100644 --- a/src/UI/templates/js/Toast/toast.js +++ b/src/UI/templates/js/Toast/toast.js @@ -29,27 +29,7 @@ il.UI.toast = ((UI) => { element.querySelector('.il-toast').classList.remove('active'); }; - let getRelativeAnchestor = (x) => { - let pos = window.getComputedStyle(x).position; - if (pos ==='relative' || pos === 'fixed' || pos === 'sticky' || !x.parentNode) { - return x; - } else { - return getRelativeAnchestor(x.parentNode); - } - }; - let appearToast = (element) => { - let item = getRelativeAnchestor(element); - let height = 0; - let item_top = item.getBoundingClientRect().top; - item.querySelectorAll('.il-toast').forEach((e) => { - let temp_height = e.getBoundingClientRect().bottom - item_top; - if(e !== element.querySelector('.il-toast') && e.classList.contains('active') && height < temp_height) { - height = temp_height; - } - }) - - element.style.top = height + 'px'; element.querySelector('.il-toast').classList.add('active'); element.querySelector('.il-toast .close').addEventListener('click', () => {closeToast(element, true);}); setTimeout(() => {closeToast(element);}, vanishTime); @@ -60,8 +40,5 @@ il.UI.toast = ((UI) => { closeToast: closeToast, appearToast: appearToast, setToastSettings: setToastSettings, - getRelativeAnchestor: getRelativeAnchestor } -})(il.UI) -il.UI.toast.setToastSettings(document.currentScript.parentElement); -il.UI.toast.showToast(document.currentScript.parentElement); +})(il.UI) \ No newline at end of file diff --git a/templates/default/delos.css b/templates/default/delos.css index 7eebfd3ff119..a3430ba59131 100644 --- a/templates/default/delos.css +++ b/templates/default/delos.css @@ -10872,15 +10872,20 @@ footer { .il-link.link-bulky .bulky-label { color: #161616; } -.il-toast-wrapper { - position: absolute; +.il-toast-container { + position: fixed; z-index: 1; - top: 0; + top: 100px; right: 0; + width: 300px; +} +.il-toast-container:empty { + display: none; +} +.il-toast-wrapper { overflow: hidden; } .il-toast { - width: 300px; margin: 20px; padding: 8px; background-color: white; diff --git a/templates/default/less/variables.less b/templates/default/less/variables.less index 65e87aa4dc4b..cbb349010fb2 100644 --- a/templates/default/less/variables.less +++ b/templates/default/less/variables.less @@ -795,6 +795,7 @@ with the il- variable set here. //== Toast // //## +@il-toast-container-top: 100px; @il-toast-margin: 20px; @il-toast-horizontal-division: 20px 1fr 20px; @il-toast-transition: all .25s ease-in-out; diff --git a/tests/UI/Client/Toast/Standard/StandardToastTest.html b/tests/UI/Client/Toast/Standard/StandardToastTest.html deleted file mode 100644 index 089e7e498444..000000000000 --- a/tests/UI/Client/Toast/Standard/StandardToastTest.html +++ /dev/null @@ -1,15 +0,0 @@ -
-
-
-
NoIcon
-
Title
- -
Description
-
-
- - -
-
diff --git a/tests/UI/Client/Toast/ToastTest.html b/tests/UI/Client/Toast/ToastTest.html new file mode 100644 index 000000000000..34ead0d6488c --- /dev/null +++ b/tests/UI/Client/Toast/ToastTest.html @@ -0,0 +1,22 @@ + + Toast Test HTML + + + + +
+
+
+
+ Test +
+
Title
+ +
Description
+
+
+
+
+ diff --git a/tests/UI/Client/Toast/Standard/standard.test.js b/tests/UI/Client/Toast/toast.test.js similarity index 86% rename from tests/UI/Client/Toast/Standard/standard.test.js rename to tests/UI/Client/Toast/toast.test.js index 435fcf16ce46..237307e1a568 100644 --- a/tests/UI/Client/Toast/Standard/standard.test.js +++ b/tests/UI/Client/Toast/toast.test.js @@ -7,7 +7,7 @@ global.setTimeout = (callback, time) => { } beforeEach( (done) => { - JSDOM.fromFile('./tests/UI/Client/Toast/Standard/StandardToastTest.html', { runScripts: "dangerously", resources: "usable"}) + JSDOM.fromFile('./tests/UI/Client/Toast/ToastTest.html', { runScripts: "dangerously", resources: "usable"}) .then(dom => { global.window = dom.window; window.XMLHttpRequest = class { @@ -62,7 +62,6 @@ describe('appearToast', () => { it ('show and arrange', () => { il.UI.toast.appearToast(element); expect(toast.classList.contains('active')).to.be.true; - expect(element.style.top).to.be.string('0px'); }) it ('trigger close action', () => { il.UI.toast.appearToast(element); @@ -76,16 +75,6 @@ describe('appearToast', () => { }) }) -describe('getRelativeAnchestor', () => { - it ('with near target', () => { - expect(il.UI.toast.getRelativeAnchestor(element).id).to.be.string('anchestor'); - }) - it ('with far target', () => { - expect(il.UI.toast.getRelativeAnchestor(toast).id).to.be.string('anchestor'); - }) -}) - - describe('closeToast', () => { it ('initiate transition', () => { toast.classList.add('active') diff --git a/tests/UI/Component/Toast/ToastClientHtmlTest.php b/tests/UI/Component/Toast/ToastClientHtmlTest.php new file mode 100644 index 000000000000..3249319dc57d --- /dev/null +++ b/tests/UI/Component/Toast/ToastClientHtmlTest.php @@ -0,0 +1,75 @@ +createMock(ILIAS\UI\Implementation\Component\SignalGenerator::class) + ); + } + + public function getIconFactory() + { + return new ILIAS\UI\Implementation\Component\Symbol\Icon\Factory(); + } + + public function testRenderClientHtml() + { + $expected_html = file_get_contents(__DIR__ . "/../../Client/Toast/ToastTest.html"); + + $rendered_html = ' + Toast Test HTML + + + + + {CONTAINER} + '; + + $container = $this->getToastFactory()->container()->withAdditionalToast( + $this->getToastFactory()->standard( + 'Title', + $this->getIconFactory()->standard('mail', 'Test')->withIsOutlined(true) + ) + ->withVanishTime(5000) + ->withDelayTime(500) + ->withDescription('Description') + ->withAction('https://www.ilias.de') + ); + + $rendered_html = str_replace('{CONTAINER}', $this->getDefaultRenderer()->render($container), $rendered_html); + $rendered_html = preg_replace('/id=".*?"/', '', $rendered_html); + + $this->assertEquals($this->brutallyTrimHTML($expected_html), $this->brutallyTrimHTML($rendered_html)); + } +} \ No newline at end of file diff --git a/tests/UI/Component/Toast/ToastTest.php b/tests/UI/Component/Toast/ToastTest.php new file mode 100644 index 000000000000..d47bfbe8474c --- /dev/null +++ b/tests/UI/Component/Toast/ToastTest.php @@ -0,0 +1,97 @@ +getToastFactory(); + + $this->assertInstanceOf("ILIAS\\UI\\Component\\Toast\\Factory", $f); + + $this->assertInstanceOf("ILIAS\\UI\\Component\\Toast\\Toast", $f->standard('', $this->getIconFactory()->standard('', ''))); + $this->assertInstanceOf("ILIAS\\UI\\Component\\Toast\\Container", $f->container()); + } + + /** + * @dataProvider toast_provider + */ + public function test_toast(string $title, string $description, int $vanish_time, int $delay_time, string $action) : void + { + $toast = $this->getToastFactory()->standard($title, $this->getIconFactory()->standard('','')) + ->withDescription($description) + ->withVanishTime($vanish_time) + ->withDelayTime($delay_time) + ->withAction($action) + ->withAdditionalLink($this->getLinkFactory()->standard('','')); + + $this->assertNotNull($toast); + $this->assertEquals($title, $toast->getTitle()); + $this->assertEquals($description, $toast->getDescription()); + $this->assertEquals($vanish_time, $toast->getVanishTime()); + $this->assertEquals($delay_time, $toast->getDelayTime()); + $this->assertEquals($action, $toast->getAction()); + $this->assertCount(1, $toast->getLinks()); + $this->assertInstanceOf(Link::class, $toast->getLinks()[0]); + $this->assertCount(0, $toast->withoutLinks()->getLinks()); + $this->assertInstanceOf(Icon::class, $toast->getIcon()); + } + + /** + * @dataProvider toast_provider + */ + public function test_toast_container(string $title, string $description, int $vanish_time) : void + { + $container = $this->getToastFactory()->container()->withAdditionalToast( + $this->getToastFactory()->standard('', $this->getIconFactory()->standard('', '')) + ); + + $this->assertNotNull($container); + $this->assertCount(1, $container->getToasts()); + $this->assertInstanceOf(Toast::class, $container->getToasts()[0]); + $this->assertCount(0, $container->withoutToasts()->getToasts()); + } + + public function toast_provider() : array + { + return [ + ['title', 'description', 5000, 500, 'test.php'], + ['', '', -5000, -500, ''], + ['"/>', '"/>', PHP_INT_MAX, PHP_INT_MIN, 'test.php'] + ]; + } +}