I have an add-in that opens a dialog box using Office.context.ui.displayDialogAsync() with displayInIframe: true
When I click the 'X' close button in the top righthand corner nothing happens.
Similarly, when I call dialog.close() or Office.context.ui.closeContainer() nothing happens.
I have tested this on chrome and edge, both have this issue (I have not tested other browsers).
On the desktop app this works without any issues.
Simplified code for displaying the dialog window:
Office.context.ui.displayDialogAsync(url, { height: 60, width: 60, displayInIframe: true }, asyncResult => {
if (handleError(asyncResult)) return;
dialog = asyncResult.value;
const dialogClosed = async (_: any): Promise<void> => {
console.log("dialogClosed");
};
dialog.addEventHandler(Office.EventType.DialogEventReceived, dialogClosed);
dialog.addEventHandler(Office.EventType.DialogMessageReceived, processMessage);
const promises = [];
{working business logic}
Promise.all(promises).then(values => {
{working business logic}
event.completed();
});
});
If I add an event handler for DialogEventRecieved, as far as I can tell this is never triggered on web.
Any suggestion would be appreciated. Thanks.
The problem can be related to the following part of your code:
const promises = [];
{working business logic}
Promise.all(promises).then(values => {
{working business logic}
event.completed();
});
Try to remove any extra code for promises and just use something like that:
let dialog;
Office.context.ui.displayDialogAsync('https://myDomain/myDialog.html',
function (asyncResult) {
dialog = asyncResult.value;
dialog.addEventHandler(Office.EventType.DialogMessageReceived, processMessage);
}
);
function processMessage(arg) {
dialog.close();
// message processing code goes here;
}
Read more about that in the Use the Office dialog API in Office Add-ins article.
Related
I am developing a Configurable Tab to be used for meetings. I need to call getContext in order to retrieve the meetingId so that I know which meeting the tab is running in. All works well on desktop - getContext calls the callback function and returns the context. On mobile, though, the callback function is never called.
Here is the configurableTabs section of the manifest (configuration works fine without an issue):
"configurableTabs": [
{
"configurationUrl": "https://xxx.xxx.xxx/teams-app/meetingConfig.html",
"scopes": [
"team",
"groupchat"
],
"canUpdateConfiguration": true,
"context":[
"meetingChatTab",
"meetingDetailsTab",
"meetingSidePanel",
"meetingStage"
]
}
],
And here is the code calling getContext (this is from within the page loaded after configuration is saved and completed):
(async () => {
'use strict';
try {
microsoftTeams.initialize();
microsoftTeams.getContext( async function (context) {
document.getElementById("app").innerHTML += "<br/><br/>Return from context " + JSON.stringify(context); //this is never called
if (!!context && context.chatId) {
meetingId = context.chatId;
await initializeMeetingInfo();
store.dispatch('setMeetingInfo', meetingInfo);
initializeVue();
} else {
document.getElementById("app").innerHTML += "<br/><br/>Unable to get meeting Id. Context=" + JSON.stringify(context);
});
} catch (e) {
document.getElementById("app").innerHTML += "<br/><br/>" + 'teams getContext error: ' + JSON.stringify(e);
}
Any idea why getContext isn't calling the callback function on mobile (android Pixel 5)?
Thank you
Moving the answer from comments to answers section.
Could you please try the below sample code,
Script:
var app = angular.module('DemoApp', []) app.controller('myController', function ($scope) { $scope.load = function () { microsoftTeams.initialize(); microsoftTeams.getContext(context => { alert(JSON.stringify(context)); }); } }); HTML: This is a sample code.
I'm running echo server and redis. Private channels work perfectly, and messaging I have built for it works. Now I'm trying to get the whisper to work for the typing status as well but no luck. Does whisper require a pusher to work?
What I have tried on keyup (jquery)
Echo.private(chat- + userid)
.whisper('typing',{e: 'i am is typing...'});
console.log('key up'); // this one works so the keyup is triggered
then I'm of course listening the channel what I am whispering into:
Echo.private(chat- + userid).listenForWhisper('typing', (e) => {
console.log(e + ' this is typing');
});
But I get absolutely nothing anywhere. (debugging on at the echo server, nothing on console etc) Any help how to get this to work would be much appreciated.
Your input event:
$('input').on('keydown', function(){
let channel = Echo.private('chat')
setTimeout( () => {
channel.whisper('typing', {
user: userid,
typing: true
})
}, 300)
})
Your listening event:
Echo.private('chat')
.listenForWhisper('typing', (e) => {
e.typing ? $('.typing').show() : $('.typing').hide()
})
setTimeout( () => {
$('.typing').hide()
}, 1000)
Of course you have to have setup the authentication for this channel ahead of time to ensure that the trusted parties have access:
Broadcast::channel('chat', function ($user) {
return Auth::check();
});
Where $user will be the userid we passed to the user param in our object on the front end.
This is what my ReactJS componentDidMount looks like.
Your listening event.
componentDidMount() {
let timer; // timer variable to be cleared every time the user whispers
Echo.join('chatroom')
.here(...)
.joining(...)
.leaving(...)
.listen(...)
}).listenForWhisper('typing', (e) => {
this.setState({
typing: e.name
});
clearTimeout(timer); // <-- clear
// Take note of the 'clearTimeout' before setting another 'setTimeout' timer.
// This will clear previous timer that will make your typing status
// 'blink' if not cleared.
timer = setTimeout(() => {
this.setState({
typing: null
});
}, 500);
});
}
I have a button on my page, I want to write an e2e test using Protractor. What I want to implement is that when clicking the button, page change to http://localhost:8100/#/booking. How can I implement that?
describe('booking function', function() {
it('should go to booking page', function() {
broswer.get(localHostCruises);
element(by.css('.button.button-stable.button-block')).click();
//sudo code
expect(page change to "http://localhost:8100/#/book" );
})
})
you need to use the browser.getCurrentUrl() method, so something like this:
element(by.css('.button.button-stable.button-block')).click().then(function () {
expect(browser.getCurrentUrl()).toEqual("http://localhost:8100/#/book");
})
You can achieve this with below code.
let targetLocation = 'your/target/location';
browser.get('localHostCruises'); // Login page.
element(by.css('.button.button-stable.button-block')).click(); // click on button.
browser.setLocation(targetLocation);
// If Login takes some time, so wait until it's done.
browser.wait(() => {
return browser.getCurrentUrl().then((url) => {
let isMatch = url.match(targetLocation);
if (isMatch) {
page = new Page; // I assume you writing test cases in page object.
} else {
browser.setLocation(targetLocation);
}
return isMatch;
});
}, 2000, 'Should throw an error message.');
I'm having an issue similar to the issues reported both here and here, with a only a few changes in how my form data is loaded.
The solution provided in the second link seemingly resolves my issue, but removing the show/hide scaling effects should not be required in order for CKEditor to instantiate properly. There's bound to be a much better alternative to resolving this conflict.
My issue:
When I open my page, and click the edit button, a jQueryUI Dialog pops up, loads its data via ajax, and then I attempt to replace the textarea added to the dialog with a CKEditor instance. The first time I load the page, the dialog works without a hitch. I'm able to modify the data within the editor, save my form data, and get on with life. However, if I close the dialog, then open it again, the editor is no longer enabled. The buttons still have hover effects, and are clickable, but do nothing. The text area of the editor is disabled and set to "style: visibility: hidden; display: none;". Nearly all the information I can find regarding this issue is from many years ago, and the fixes involve using functions/techniques that no longer exist or are applicable.
Control Flow
I open the page containing a text link 'Edit Update', which calls my Javascript function openEditTicketUpdateDialog.
function openEditTicketUpdateDialog(tup_id, url)
{
simplePost(null, url, new Callback
(
function onSuccess(data)
{
$('#editticketupdatedialog').dialog('option', 'buttons',
[
{
text: 'Save Edits',
click: function()
{
// Save the Update info
var formData = {
tup_update: CKEDITOR.instances.tup_update_edit.getData(),
tup_internal: +$('#tup_internal_edit').is(":checked"),
tup_important: +$('#tup_important_edit').is(":checked")
};
simplePost(formData, data['submitRoute'], new Callback
(
function onSuccess(data)
{
$('#update-' + tup_id).html(data.input['tup_update']);
$('#updateflags-' + tup_id).html(data.flags);
$('#editticketupdatedialog').dialog('close');
},
function onFail(errors)
{
console.log(errors);
}
));
}
},
{
text: 'Cancel',
click: function()
{
$(this).dialog("close");
}
}
]);
$('#editticketupdatedialog').dialog('option', 'title', data['title']);
$('#editticketupdatedialog').html(data['view']);
$('#editticketupdatedialog').dialog('open');
destroyEditor('tup_update_edit');
console.log('CKEDITOR.status: ' + CKEDITOR.status);
createEditor('tup_update_edit');
},
function onFail(errors)
{
console.log(errors);
}
));
}
This function uses three helper functions, simplePost, destroyEditor and createEditor.
function simplePost(data, url, callback)
{
post(data, url, true, false, callback);
}
function createEditor(name)
{
console.log('Create editor: ' + name);
console.log('Current Instance: ');
console.log(CKEDITOR.instances.name);
if (CKEDITOR.status == 'loaded')
{
CKEDITOR.replace(name,
{
customConfig: '/js/ckeditor/custom/configurations/standard_config.js'
});
}
else
{
CKEDITOR.on('load', createEditor(name));
CKEDITOR.loadFullCore && CKEDITOR.loadFullCore();
}
console.log('After instance created: ');
var instance = CKEDITOR.instances.name;
console.log(instance);
}
function destroyEditor(name)
{
console.log('Destroy editor: ' + name);
console.log('Current Instance: ');
console.log(CKEDITOR.instances.name);
if (CKEDITOR.instances.name)
{
console.log('Instance exists - destroying...');
CKEDITOR.instances.name.destroy();
$('#' + name).off().remove();
}
console.log('After instance removed: ');
var instance = CKEDITOR.instances.name;
console.log(instance);
}
This method of creating a CKEditor instance was gathered from here. This method of destroying a CKEditor instance was gathered from here.
As you can see, openEditTicketUpdateDialog fires an AJAX call to my getEditUpdateForm function through Laravel routes.
public function getEditUpdateForm($tup_id, $update_number)
{
$update = Update::find($tup_id);
$data = [
'title' => 'Editing update #' . $update_number . ' of ticket #' . $update->tup_ticket,
'view' => View::make('tickets.ticketupdate-edit')
->with('update', $update)
->render(),
'submitRoute' => route('tickets/update/submit', $tup_id)
];
return Response::json(array('status' => 1, 'data' => $data));
}
From here, a status of 1 is returned, and the onSuccess function is called. I've attempted to add the create/delete calls before the $('#editticketupdatedialog').dialog('open'); call, but to no avail. I've also tried multiple other solutions that I've found surfacing, which involve hacked implementations of jQueryUI's Dialog functions and attributes: _allowInteraction and moveToTop. I was originally successful in resolving this issue the first time it arose by calling this function before doing a CKEDITOR.replace:
function enableCKEditorInDialog()
{
$.widget( "ui.dialog", $.ui.dialog, {
/**
* jQuery UI v1.11+ fix to accommodate CKEditor (and other iframed content) inside a dialog
* #see http://bugs.jqueryui.com/ticket/9087
* #see http://dev.ckeditor.com/ticket/10269
*/
_allowInteraction: function( event ) {
return this._super( event ) ||
// addresses general interaction issues with iframes inside a dialog
event.target.ownerDocument !== this.document[ 0 ] ||
// addresses interaction issues with CKEditor's dialog windows and iframe-based dropdowns in IE
!!$( event.target ).closest( ".cke_dialog, .cke_dialog_background_cover, .cke" ).length;
}
});
}
After updating to Laravel 5, and making a few other changes serverside, this fix no longer works. I have been successful in resolving my issue by removing the show/hide properties from my dialog. I would very much prefer not to have to remove these properties, as half the reasoning for having the dialog is the aesthetics of an animation. Here is my dialog initialization.
$('#editticketupdatedialog').dialog({
modal: true,
draggable: false,
minWidth: 722,
autoOpen: false,
show:
{
effect: "scale",
duration: 200
},
hide:
{
effect: "scale",
duration: 200
},
closeOnEscape: true
});
When I have these animations enabled, the first time I use the dialog, it works perfectly. The second time, I receive the error TypeError: this.getWindow(...).$ is undefined - ckeditor.js:83:18 in the JS console, which refers to this line:
function(a)
{
var d = this.getWindow().$.getComputedStyle(this.$,null);
return d ? d.getPropertyValue(a) : ""
}
Recap
My main goal here is to find a fix for this issue, without having to remove the jQueryUI Dialog animation. I am unsure whom to point fingers at, as I really can't determine if the issue lies in CKEditor, jQueryUI or my implementation.
I finally found a solution that works in my case. losnir updated the outdated solution to a post here, and adding the open function to my dialog initialization resolved my issue.
My initialization is as follows:
$('#editticketupdatedialog').dialog({
modal: true,
draggable: false,
minWidth: 722,
autoOpen: false,
show:
{
effect: "scale",
duration: 200
},
hide:
{
effect: "scale",
duration: 200
},
closeOnEscape: true,
open: function()
{
$(this).parent().promise().done(function ()
{
destroyEditor('tup_update_edit');
console.log('CKEDITOR.status: ' + CKEDITOR.status);
createEditor('tup_update_edit');
});
}
});
I need to create two toolbar on Firefox : one is horizontal on top and the other vertical on right side of the browser. But the sdk lib to firefox dont have resources to do it. Any sugestion ?
This might help, pretty simple guide laid out for easy reading:
http://www.borngeek.com/firefox/toolbar-tutorial/
Since version 1.15 the Addon SDK allows you to create toolbars and add buttons to it. I don't think it's possible to create a vertical toolbar, only horizontal ones.
There's a nice example on how to do it in the Addon SDK official repository:
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Toolbar } = require("sdk/ui/toolbar");
const { Frame } = require("sdk/ui/frame");
const { Button } = require("sdk/ui/button");
let button = new Button({
id: "button",
label: "send!",
icon: "./favicon.ico",
onClick: () => {
frame.postMessage({
hello: "content"
});
}
});
let frame = new Frame({
url: "./index.html",
onAttach: () => {
console.log("frame was attached");
},
onReady: () => {
console.log("frame document was loaded");
},
onLoad: () => {
console.log("frame load complete");
},
onMessage: (event) => {
console.log("got message from frame content", event);
if (event.data === "ping!")
event.source.postMessage("pong!", event.source.origin);
}
});
let toolbar = new Toolbar({
items: [frame],
title: "Addon Demo",
hidden: false,
onShow: () => {
console.log("toolbar was shown");
},
onHide: () => {
console.log("toolbar was hidden");
}
});
Also, there's an older SO thread explaining how to do it on older versions of the Addon SDK and for XUL-based addons.
The code above only works on Firefox Australis (upcomming version 29.0). You can use a Jetpack module like toolbarwidget-jplib by Rob--W.
So you can add widgets on the navigation bar:
require("toolbarwidget").ToolbarWidget({
toolbarID: "nav-bar", // <-- Place widget on Navigation bar
id: "mozilla-icon",
label: "My Mozilla Widget",
contentURL: "http://www.mozilla.org/favicon.ico"
});