Skip to content

Commit

Permalink
Modal loading indicator and close Modal event (SAP#1578)
Browse files Browse the repository at this point in the history
  • Loading branch information
azriel46d authored Aug 26, 2020
1 parent 1e26736 commit 3eb03a6
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 4 deletions.
6 changes: 6 additions & 0 deletions client/luigi-client.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ export declare interface UxManager {
*/
hideLoadingIndicator: () => void;

/**
* Closes the currently opened micro frontend modal.
* @memberof uxManager
*/
closeCurrentModal: () => void;

/**
* This method informs the main application that there are unsaved changes in the current view in the iframe. For example, that can be a view with form fields which were edited but not submitted.
* @param {boolean} isDirty indicates if there are any unsaved changes on the current page or in the component
Expand Down
9 changes: 9 additions & 0 deletions client/src/uxManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ class UxManager extends LuigiClientBase {
hideLoadingIndicator() {
helpers.sendPostMessageToLuigiCore({ msg: 'luigi.hide-loading-indicator' });
}

/**
* Closes the currently opened micro frontend modal.
* @memberof uxManager
*/
closeCurrentModal() {
helpers.sendPostMessageToLuigiCore({ msg: 'luigi.close-modal' });
}

/**
* Adds a backdrop to block the top and side navigation. It is based on the Fundamental UI Modal, which you can use in your micro frontend to achieve the same behavior.
* @memberof uxManager
Expand Down
86 changes: 83 additions & 3 deletions core/src/Modal.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,42 @@ <h3 class="fd-dialog__title">{modalSettings.title}</h3>
<div class="fd-dialog__body">
<div class="iframeModalCtn"></div>
</div>
{#if showLoadingIndicator}
<div
in:fade="{{delay: 250, duration: 250}}"
out:fade="{{duration: 250}}"
class="fd-page spinnerContainer"
aria-hidden="false"
aria-label="Loading"
>
<div
class="fd-busy-indicator--m"
aria-hidden="false"
aria-label="Loading"
data-testid="luigi-loading-spinner"
>
<div class="fd-busy-indicator--circle-0"></div>
<div class="fd-busy-indicator--circle-1"></div>
<div class="fd-busy-indicator--circle-2"></div>
</div>
</div>
{/if}
</div>
</div>

<script>
import { afterUpdate, createEventDispatcher } from 'svelte';

import {
afterUpdate,
createEventDispatcher,
onMount,
onDestroy
} from 'svelte';
import { fade } from 'svelte/transition';
const dispatch = createEventDispatcher();

import { Navigation } from './navigation/services/navigation';
import {
EventListenerHelpers,
GenericHelpers,
IframeHelpers,
RoutingHelpers
Expand All @@ -44,6 +70,8 @@ <h3 class="fd-dialog__title">{modalSettings.title}</h3>
let nodeObject;
let pathData;
let nodeParams;
let iframeCreated = false;
let showLoadingIndicator = true;

const prepareNodeData = async path => {
const pathUrlRaw =
Expand All @@ -61,6 +89,9 @@ <h3 class="fd-dialog__title">{modalSettings.title}</h3>
};

const getNode = async path => {
if (iframeCreated) {
return;
}
if (isDataPrepared) {
const iframe = createIframeModal(nodeObject.viewUrl, {
context: pathData.context,
Expand All @@ -71,6 +102,7 @@ <h3 class="fd-dialog__title">{modalSettings.title}</h3>
modalIframe: iframe,
modalIframeData: { ...pathData, nodeParams }
});
iframeCreated = true;
} else {
await prepareNodeData(path);
}
Expand Down Expand Up @@ -112,6 +144,38 @@ <h3 class="fd-dialog__title">{modalSettings.title}</h3>
getNode(nodepath);
});

const onMessage = async e => {
if ('luigi.show-loading-indicator' === e.data.msg) {
showLoadingIndicator = true;
}

if ('luigi.hide-loading-indicator' === e.data.msg) {
showLoadingIndicator = false;
}

if ('luigi.get-context' === e.data.msg) {
const loadingIndicatorAutoHideEnabled =
!nodeObject ||
!nodeObject.loadingIndicator ||
nodeObject.loadingIndicator.hideAutomatically !== false;
if (loadingIndicatorAutoHideEnabled) {
showLoadingIndicator = false;
}
}

if ('luigi.close-modal' === e.data.msg) {
dispatch('close');
}
};

onMount(() => {
EventListenerHelpers.addEventListener('message', onMessage);
});

onDestroy(() => {
EventListenerHelpers.removeEventListener('message', onMessage);
});

// [svelte-upgrade suggestion]
// review these functions and remove unnecessary 'export' keywords
export function handleKeydown(event) {
Expand All @@ -135,7 +199,23 @@ <h3 class="fd-dialog__title">{modalSettings.title}</h3>
border: 0;
position: absolute;
}

.lui-modal-mf {
position: relative;
}
.spinnerContainer {
background: rgba(243, 244, 245, 0.8);
display: flex;
align-items: center;
justify-content: center;
position: absolute;
bottom: 0;
right: 0;
min-width: auto;
min-height: auto;
display: flex;
width: 100%;
height: 100%;
}
.fd-dialog {
&__content {
width: 80%;
Expand Down
7 changes: 7 additions & 0 deletions core/src/utilities/helpers/event-listener-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ class EventListenerHelpersClass {
window.addEventListener(type, listenerFn);
}

removeEventListener(type, listenerFn) {
this.listeners = this.listeners.filter(
l => !(l.type === type && l.listenerFn === listenerFn)
);
window.removeEventListener(type, listenerFn);
}

removeAllEventListeners() {
this.listeners.forEach(l => {
window.removeEventListener(l.type, l.listenerFn);
Expand Down
4 changes: 4 additions & 0 deletions docs/luigi-client-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,10 @@ Adds a backdrop with a loading indicator for the micro frontend frame. This over

Removes the loading indicator. Use it after calling [showLoadingIndicator()](#showLoadingIndicator) or to hide the indicator when you use the [loadingIndicator.hideAutomatically: false](navigation-parameters-reference.md#node-parameters) node configuration.

#### closeCurrentModal

Closes the currently opened micro frontend modal.

#### addBackdrop

Adds a backdrop to block the top and side navigation. It is based on the Fundamental UI Modal, which you can use in your micro frontend to achieve the same behavior.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,27 @@ describe('Modal Microfrontend', () => {
.should('not.be.visible');
});

it(`can be closed by the Luigi client`, () => {
cy.wrap($iframeBody)
.contains('rendered in a modal')
.click();

cy.wrap($iframeBody)
.get('[data-testid=modal-mf]')
.should('be.visible');

cy.get('[data-testid=modal-mf] iframe').then(ifr => {
cy.wrap(ifr[0].contentDocument)
.its('body')
.contains('Close modal')
.click();

cy.wrap($iframeBody)
.get('[data-testid=modal-mf]')
.should('not.be.visible');
});
});

it(`sets proper URL inside iframe`, () => {
cy.wrap($iframeBody)
.contains('rendered in a modal')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ <h1 *ngIf="projectId" class="fd-section__title">
<h1 *ngIf="!projectId" class="fd-section__title">Global Settings</h1>
</div>

<div class="fd-layout-panel">
<div class="fd-layout-panel fd-has-margin-bottom-small">
<div class="fd-layout-panel__header">
<div class="fd-layout-panel__head">
<h3 class="fd-layout-panel__title">
Expand Down Expand Up @@ -92,6 +92,21 @@ <h3 class="fd-layout-panel__title">
</div>
</div>
</div>

<div *ngIf="isModal" class="fd-layout-panel fd-has-margin-bottom-small">
<div class="fd-layout-panel__header">
<div class="fd-layout-panel__head">
<h3 class="fd-layout-panel__title">
LuigiClient Modal
</h3>
</div>
</div>
<div class="fd-layout-panel__body">
<button class="fd-button" (click)="uxManager().closeCurrentModal()">
Close modal
</button>
</div>
</div>
</section>
<section *ngIf="testFeatureToggleActive" class="fd-section">
<div class="fd-layout-panel">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ import { Subscription } from 'rxjs';
})
export class SettingsComponent implements OnInit {
public linkManager = linkManager;
public uxManager = uxManager;
projectId: string;
groupId: string;
hasBack: boolean;
isModal: boolean;
nodeParams: NodeParams = null;
callbackValue = 'default value';
lcSubscription: Subscription;
Expand All @@ -44,6 +46,7 @@ export class SettingsComponent implements OnInit {

addInitListener(init => {
this.hasBack = linkManager().hasBack();
this.isModal = uxManager().isModal();
this.nodeParams =
Object.keys(getNodeParams()).length > 0 ? getNodeParams() : null;
let featureToggleList = getActiveFeatureToggles();
Expand Down

0 comments on commit 3eb03a6

Please sign in to comment.