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 @@ +