Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add callback to openAsModal for core api #3049

Merged
merged 18 commits into from Dec 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion client/luigi-element.d.ts
Expand Up @@ -217,10 +217,11 @@ export declare interface LinkManager {
* @param {('fullscreen'|'l'|'m'|'s')} [modalSettings.size="l"] size of the modal
* @param {string} modalSettings.width lets you specify a precise width for the modal. Allowed units are 'px', '%', 'rem', 'em', 'vh' and 'vw'.
* @param {string} modalSettings.height lets you specify a precise height for the modal. Allowed units are 'px', '%', 'rem', 'em', 'vh' and 'vw'.
* @param {Function} onCloseCallback callback function called upon closing the openened modal
* @example
* LuigiClient.linkManager().openAsModal('projects/pr1/users', {title:'Users', size:'m'});
*/
openAsModal: (nodepath: string, modalSettings?: ModalSettings) => void;
openAsModal: (nodepath: string, modalSettings?: ModalSettings, onCloseCallback?: Function) => void;

/**
* Opens a view in a split view. You can specify the split view's title and size. If you don't specify the title, it is the node label. If there is no node label, the title remains empty. The default size of the split view is 40, which means 40% height of the split view.
Expand Down
44 changes: 25 additions & 19 deletions core/src/App.svelte
Expand Up @@ -143,7 +143,7 @@

const sendContextToClient = async (config, goBackContext = {}) => {
if (!config.iframe) {
console.info('iframe does not exist, not able to send context.');
console.debug('iframe does not exist, not able to send context.');
return;
}

Expand Down Expand Up @@ -976,8 +976,9 @@
* Closes the modal given the respective modal index. Index is used due to multiple modals functionality
* @param index the index of the modal to be closed corresponding to the 'mfModalList' array
* @param isClosedInternal flag if the modal is closed via close button or internal back navigation instead of changing browser URL manually or browser back button
* @param goBackContext the goBack context that is passed through when closing the modal
*/
const closeModal = (index, isClosedInternal) => {
const closeModal = (index, isClosedInternal, goBackContext) => {
const resetModalData = (index, isClosedInternal) => {
const showModalPathInUrl = LuigiConfig.getConfigBooleanValue(
'routing.showModalPathInUrl'
Expand All @@ -989,14 +990,19 @@
resetMicrofrontendModalData(index);
};
const targetModal = mfModalList[index];
const rp = GenericHelpers.getRemotePromise(
targetModal.mfModal.settings.onClosePromiseId
);
if (targetModal && targetModal.modalIframe) {
getUnsavedChangesModalPromise(targetModal.modalIframe.contentWindow).then(
() => {
resetModalData(index, isClosedInternal);
rp && rp.doResolve(goBackContext);
}
);
} else if (targetModal && targetModal.modalWC) {
resetModalData(index, isClosedInternal);
rp && rp.doResolve(goBackContext);
}
};

Expand Down Expand Up @@ -1529,34 +1535,35 @@

if ('luigi.navigation.back' === e.data.msg) {
const mfModalTopMostElement = mfModalList[mfModalList.length - 1];

const _goBackContext =
e.data.goBackContext && JSON.parse(e.data.goBackContext);
if (
IframeHelpers.isMessageSource(
e,
mfModalTopMostElement && mfModalTopMostElement.modalIframe
)
) {
closeModal(mfModalList.length - 1, true);
closeModal(mfModalList.length - 1, true, _goBackContext);

await sendContextToClient(config, {
goBackContext:
e.data.goBackContext && JSON.parse(e.data.goBackContext),
});
config.iframe &&
(await sendContextToClient(config, {
goBackContext: _goBackContext,
}));
} else if (IframeHelpers.isMessageSource(e, splitViewIframe)) {
closeSplitView();
await sendContextToClient(config, {
goBackContext:
e.data.goBackContext && JSON.parse(e.data.goBackContext),
});
config.iframe &&
(await sendContextToClient(config, {
goBackContext: _goBackContext,
}));
} else if (IframeHelpers.isMessageSource(e, drawerIframe)) {
if (activeDrawer) {
activeDrawer = !activeDrawer;
}
closeDrawer();
await sendContextToClient(config, {
goBackContext:
e.data.goBackContext && JSON.parse(e.data.goBackContext),
});
config.iframe &&
(await sendContextToClient(config, {
goBackContext: _goBackContext,
}));
} else {
// go back: context from the view
if (preservedViews && preservedViews.length > 0) {
Expand All @@ -1568,16 +1575,15 @@
config.iframe = Iframe.getActiveIframe(node);
isNavigateBack = true;
preservedViews = preservedViews;
goBackContext =
e.data.goBackContext && JSON.parse(e.data.goBackContext);
goBackContext = _goBackContext;
// TODO: check if getNavigationPath or history pop to update hash / path
handleNavigation(
{ params: { link: previousActiveIframeData.path } },
config
);
});
} else {
if (e.data.goBackContext) {
if (_goBackContext) {
console.warn(
`Warning: goBack() does not support goBackContext value. This is available only when using the Luigi preserveView feature.`
);
Expand Down
15 changes: 14 additions & 1 deletion core/src/core-api/_internalLinkManager.js
Expand Up @@ -53,7 +53,20 @@ export class linkManager extends LuigiCoreAPIBase {
return remotePromise;
}

openAsModal(path, modalSettings = {}) {
/**
* This function navigates to a modal after adding the onClosePromise that handles the callback for when the modal is closed.
* @param {string} path the navigation path to open in the modal
* @param {Object} modalSettings settings to configure the modal's title, size, width and height
* @param {Function} onCloseCallback callback function called upon closing the opened modal
*/
openAsModal(path, modalSettings = {}, onCloseCallback) {
if (GenericHelpers.isFunction(onCloseCallback)) {
const onClosePromise = GenericHelpers.createRemotePromise();
onClosePromise.then(value => {
onCloseCallback(value);
});
modalSettings.onClosePromiseId = onClosePromise.id;
}
this.navigate(path, true, modalSettings);
}

Expand Down
7 changes: 4 additions & 3 deletions core/src/core-api/navigation.js
Expand Up @@ -6,7 +6,7 @@ class LuigiNavigationManager {
* Use these functions for navigation-related features.
* @name LuigiNavigation
*/
constructor() {}
constructor() { }

/**
* Refreshes top navigation badge counters by rendering the navigation again.
Expand Down Expand Up @@ -55,11 +55,12 @@ class LuigiNavigationManager {
* @param {('fullscreen'|'l'|'m'|'s')} [modalSettings.size="l"] size of the modal
* @param {string} modalSettings.width lets you specify a precise width for the modal. Allowed units are 'px', '%', 'rem', 'em', 'vh' and 'vw'.
* @param {string} modalSettings.height lets you specify a precise height for the modal. Allowed units are 'px', '%', 'rem', 'em', 'vh' and 'vw'.
* @param {Function} onCloseCallback callback function called upon closing the opened modal
* @example
* Luigi.navigation().openAsModal('projects/pr1/users', {title:'Users', size:'m'});
*/
openAsModal(path, modalSettings) {
return new linkManager().openAsModal(path, modalSettings);
openAsModal(path, modalSettings, onCloseCallback) {
return new linkManager().openAsModal(path, modalSettings, onCloseCallback);
}

/**
Expand Down
10 changes: 5 additions & 5 deletions core/src/utilities/helpers/generic-helpers.js
Expand Up @@ -357,13 +357,13 @@ class GenericHelpersClass {

/**
* Creates a remote promise.
* @returns {Promise} which returns true when the promise will be resolved and returns false if the promise will be rejected.
* @returns {Promise} which returns true or a value when the promise will be resolved and returns false if the promise will be rejected.
*/
createRemotePromise() {
let res, rej;
const prom = new Promise(resolve => {
res = () => {
resolve(true);
res = value => {
resolve(value || true);
};
rej = () => {
resolve(false);
Expand All @@ -381,9 +381,9 @@ class GenericHelpersClass {
prom.id = luiRP.counter++;
luiRP.promises[prom.id] = prom;

prom.doResolve = () => {
prom.doResolve = value => {
delete luiRP.promises[prom.id];
res();
res(value);
};
prom.doReject = () => {
delete luiRP.promises[prom.id];
Expand Down
28 changes: 27 additions & 1 deletion core/test/core-api/internal-link-manager.spec.js
Expand Up @@ -6,7 +6,7 @@ import { linkManager } from '../../src/core-api/_internalLinkManager';

let lm;

describe('linkManager', function() {
describe('linkManager', function () {
beforeEach(() => {
lm = new linkManager();
});
Expand Down Expand Up @@ -122,6 +122,32 @@ describe('linkManager', function() {

sinon.assert.calledOnce(lm.navigate);
});

it('calls openAsModal with callback', () => {
const prom = new Promise((resolve) => {
resolve()
})
prom.id = 1;

sinon.stub(GenericHelpers, 'isFunction').returns(true);
sinon.stub(GenericHelpers, 'createRemotePromise').returns(prom);

lm.openAsModal('path', { size: 1 }, () => { });

sinon.assert.calledOnce(GenericHelpers.isFunction);
sinon.assert.calledOnce(GenericHelpers.createRemotePromise);
sinon.assert.calledOnceWithExactly(lm.navigate, 'path', true, { size: 1, onClosePromiseId: 1 });
});

it('calls openAsModal with wrong callback', () => {
sinon.stub(GenericHelpers, 'isFunction').returns(false);

lm.openAsModal('path', { size: 1 }, 'not a function');

sinon.assert.calledOnce(GenericHelpers.isFunction);
sinon.assert.calledOnceWithExactly(lm.navigate, 'path', true, { size: 1 });
});

});

describe('openAsDrawer', () => {
Expand Down
1 change: 1 addition & 0 deletions docs/luigi-core-api.md
Expand Up @@ -563,6 +563,7 @@ Opens a view in a modal. You can specify the modal's title and size. If you do n
- `modalSettings.size` **(`"fullscreen"` \| `"l"` \| `"m"` \| `"s"`)** size of the modal (optional, default `"l"`)
- `modalSettings.width` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** lets you specify a precise width for the modal. Allowed units are 'px', '%', 'rem', 'em', 'vh' and 'vw'.
- `modalSettings.height` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** lets you specify a precise height for the modal. Allowed units are 'px', '%', 'rem', 'em', 'vh' and 'vw'.
- `onCloseCallback` **[Function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)** callback function called upon closing the opened modal

##### Examples

Expand Down
Expand Up @@ -133,6 +133,35 @@ describe('Navigation', () => {
cy.get('[data-testid=modal-mf]').should('not.be.visible');
});

it('Open modal with callback from core api', () => {
// projects page
cy.get('.fd-shellbar')
.contains('Projects')
.click();

//projects page
cy.get('.fd-app__sidebar')
.contains('Project Two')
.click();

//project two page
cy.expectPathToBe('/projects/pr2');

cy.get('.fd-app__sidebar')
.contains('Modal with Callback')
.click();

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

cy.get('[data-testid=modal-mf] [aria-label=close]').click();

cy.on('window:alert', (str) => {
expect(str).to.equal(`Callback called`)
})

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

it('Nav sync - click sidenav', () => {
// projects page
cy.get('.fd-shellbar')
Expand Down