How can I use gmail-js to add my extension button in the toolbar? - reactjs

I need to add my chrome extension button to the toolbar, I found the Gmail-js
gmail.tools.add_toolbar_button('<div id="icon_placeholder"></div>', function () { }, 'temp_css')
and it adds the button but if I go to another folder, it vanishes (if I inspect it, the element is still there, just invisible).
Another problem is if I start from inside a message or from another folder (hard refreshing the page) and then going to inbox, it won't show up.
How can I use the gmail-js to render and persist the button there? it'd be ok if it only shows up on inbox, as long as it's there (on inbox) no matter what folder start browsing from
I tried just rendering the button and checking to see if:
The element is not present in the page, if so, render it (doesn't seem to be working)
Check if the page was refreshed, if so, render (doesn't seem to be working)
Here's a piece of the code I have so far:
const gmail = new GmailFactory.Gmail() as Gmail;
var btn = gmail.tools.add_toolbar_button('<div id="icon_placeholder"></div>', function () { }, 'temp_css').get(0)['className'];
const getElement = document.querySelectorAll('.' + btn.toString().replace(' ', '.'))[5]
console.log('getElement ' + getElement.getAttribute('class'))
var app: HTMLElement = document.createElement('div') as HTMLElement;
var pos: HTMLElement = getElement as HTMLElement;
if (pos !== null) {
pos.appendChild(app);
ReactDOM.render(<IconExtChrome />, app);
}
function refreshButton(): boolean {
if (!getElement) {
pos.appendChild(app);
ReactDOM.render(<IconExtChrome />, app)
return false
}
}
if (sessionStorage.getItem('reloaded') != null) {
console.log('page was reloaded');
pos.appendChild(app);
ReactDOM.render(<IconExtChrome />, app)
} else {
console.log('page was not reloaded');
}
sessionStorage.setItem('reloaded', 'yes');
refreshButton()```

Related

Detect react event from Tampermonkey

I'm enhancing a React front end with Tampermonkey , by adding highlights to show cursor location in a grid, and allowing users to directly enter data , rather than then enter data.
After 2 or 3 cursor moves or data entry the grid refreshes or updates - no page change - and looses the highlighting I set up.
I'd like to catch the refresh/update and reset the highlighting.
I'm a noob..
The network tab shows post events so I tried https://jsbin.com/dixelocazo/edit?js,console
var open = window.XMLHttpRequest.prototype.open,
send = window.XMLHttpRequest.prototype.send;
to try and use POST events to detect the refresh. No joy !
I also looked at ajax events.
No luck :(
Can someone point me in the right direction here ?
Once I catch the event, I can then reset the highlighting to fix the problem
Since normally the userscripts run in a sandbox, JavaScript functions or objects cannot be used directly by default, here's what you can do:
Disable the sandbox:
// #grant none
You won't be able to use any GM functions, though.
Run in the page context via unsafeWindow:
const __send = unsafeWindow.XMLHttpRequest.prototype.send;
unsafeWindow.XMLHttpRequest.prototype.send = function () {
this.addEventListener('loadend', e => {
console.log('intercepted', e);
}, {once: true});
__send.apply(this, arguments);
};
Use MutationObserver to detect changes in page DOM:
const observer = new MutationObserver(mutations => {
const matched = [];
for (const {addedNodes} of mutations) {
for (const n of addedNodes) {
if (!n.tagName)
continue;
if (n.matches('.prey:not(.my-highlight)')) {
matched.push(n);
} else if (n.firstElementChild) {
matched.push(...n.querySelectorAll('.prey:not(.my-highlight)'));
}
}
}
// process the matched elements
for (const el of matched) {
el.classList.add('my-highlight');
}
});
observer.observe(document.querySelector('.surviving-ancestor') || document.body, {
subtree: true,
childList: true,
});
.surviving-ancestor means the element that isn't replaced/recreated by the page script. In devtools element inspector it's the one that isn't highlighted temporarily during DOM updates.
See also Performance of MutationObserver.

Drag and Drop with Protractor in AngularJS

I appreciate there's quite a bit of stuff already been said about automating drag and drop as part of E2E testing. However after many, many hours of fiddling around, I cannot get any of the methods described to work...that is using Functions, coordinates etc etc. Oddly enough, console.log maintains the tests have passed, but the screenshots clearly show nothing has happened.
Screenshots shows a portion of the application
The user selects a paper and drags onto the image. As the drag 'starts' the grey overlay on the image clears and the paper is rendered on the room.
The code snippet shows one of the more simple ideas I've tried and I would be very pleased to receive any help going!
const JS_HTML5_DND = 'function e(e,t,n,i){var r=a.createEvent("DragEvent");r.initMouseEvent(t,!0,!0,o,0,0,0,c,g,!1,!1,!1,!1,0,null),Object.defineProperty(r,"dataTransfer",{get:function(){return d}}),e.dispatchEvent(r),o.setTimeout(i,n)}var t=arguments[0],n=arguments[1],i=arguments[2]||0,r=arguments[3]||0;if(!t.draggable)throw new Error("Source element is not draggable.");var a=t.ownerDocument,o=a.defaultView,l=t.getBoundingClientRect(),u=n?n.getBoundingClientRect():l,c=l.left+(l.width>>1),g=l.top+(l.height>>1),s=u.left+(u.width>>1)+i,f=u.top+(u.height>>1)+r,d=Object.create(Object.prototype,{_items:{value:{}},effectAllowed:{value:"all",writable:!0},dropEffect:{value:"move",writable:!0},files:{get:function(){return this._items.Files}},types:{get:function(){return Object.keys(this._items)}},setData:{value:function(e,t){this._items[e]=t}},getData:{value:function(e){return this._items[e]}},clearData:{value:function(e){delete this._items[e]}},setDragImage:{value:function(e){}}});if(n=a.elementFromPoint(s,f),!n)throw new Error("The target element is not interactable and need to be scrolled into the view.");u=n.getBoundingClientRect(),e(t,"dragstart",101,function(){var i=n.getBoundingClientRect();c=i.left+s-u.left,g=i.top+f-u.top,e(n,"dragenter",1,function(){e(n,"dragover",101,function(){n=a.elementFromPoint(c,g),e(n,"drop",1,function(){e(t,"dragend",1,callback)})})})})';
describe('Drag and Drop Test', function() {
it('should drag', function () {
var e1 = element(by.xpath('html/body/webapp-app/div/div/webapp-johnlewis-visualiser/div/div[2]/div/digitalbridge-shortlist/div/div/ul/li[1]/a/img'));
var e2 = element(by.css('.db-project-designer'));
element(by.xpath('html/body/webapp-app/div/div/webapp-johnlewis-visualiser/div/div[2]/div/digitalbridge-shortlist/div/div/ul/li[1]/a/img')).click();
//element(by.xpath('html/body/webapp-app/div/div/webapp-johnlewis-visualiser/div/div[1]/div/div/digitalbridge-project/div/digitalbridge-project-designer/canvas')).click();
browser.driver.actions().dragAndDrop(e1.getWebElement(),e2.getWebElement()).perform();
browser.sleep(2000);
});
});
The constant is showing an error 'const' is available in ES6 (use 'esversion: 6') or Mozilla JS extensions (use moz). (W104) - I do have ES6 installed in Node_Modules.
I inserted the click line to see if pre-selecting the item made any difference...it didn't!
Thank you
David
Try this library https://github.com/SunGard-Labs/sg-protractor-tools
The library also includes functions that simplify common tasks like
Scrolling to an element
Drag and drop
Waiting for DOM elements to become visible or hidden
module.exports = function simulateDragAndDrop(sourceNode, destinationNode) {
const EVENT_TYPES = {
DRAG_END: 'dragend',
DRAG_START: 'dragstart',
DROP: 'drop'
};
function createCustomEvent(type) {
const event = new CustomEvent('CustomEvent');
event.initCustomEvent(type, true, true, null);
event.dataTransfer = {
data: {
},
setData: function(type, val) {
this.data[type] = val;
},
getData: function(type) {
return this.data[type];
}
};
return event;
}
function dispatchEvent(node, type, event) {
if (node.dispatchEvent) {
return node.dispatchEvent(event);
}
if (node.fireEvent) {
return node.fireEvent('on' + type, event);
}
}
const event = createCustomEvent(EVENT_TYPES.DRAG_START);
dispatchEvent(sourceNode, EVENT_TYPES.DRAG_START, event);
const dropEvent = createCustomEvent(EVENT_TYPES.DROP);
dropEvent.dataTransfer = event.dataTransfer;
dispatchEvent(destinationNode, EVENT_TYPES.DROP, dropEvent);
const dragEndEvent = createCustomEvent(EVENT_TYPES.DRAG_END);
dragEndEvent.dataTransfer = event.dataTransfer;
dispatchEvent(sourceNode, EVENT_TYPES.DRAG_END, dragEndEvent);
}
You can call it from you code like this
browser.executeScript(dragAndDrop, element, targetArea);

How to trigger animations in a React app based on the scroll position

Let's say I need to add an element to the navbar when the user have scrolled past the header of the site. How can I do something like this in React without using jQuery?
You can do some thing like this: (this function was copied from my own react-sticky-dynamic-header that I created before: https://github.com/thinhvo0108/react-sticky-dynamic-header )
componentDidMount() {
var h1 = parseInt(this.refs.header.offsetHeight);
window.addEventListener('scroll', this._calcScroll.bind(this, h1));
}
componentWillUnmount() {
window.removeEventListener('scroll', this._calcScroll)
}
_calcScroll(h1) {
var _window = window;
var heightDiff = parseInt(h1);
var scrollPos = _window.scrollY;
if (scrollPos > heightDiff) {
// here this means user has scrolled past your header,
// you may rerender by setting State or do whatever
this.setState({
//stateKey: stateValue,
});
} else {
// here the user has scrolled back to header's territory,
// it's optional here for you to remove the element on navbar as stated in the question or not
this.setState({
//stateKey: stateValue,
});
}
}
render() {
return (
<div ref="header">YOUR HEADER HERE</div>
);
}
For a smooth animation when your element added or removed from the navbar, you can just add this into the element's CSS style:
#your-element{
transition: opacity 0.3s ease-in;
}
You can try to install my library to see if it can extend your needs:
https://www.npmjs.com/package/react-sticky-dynamic-header
Feel free to post here some errors if any, thanks

Navigation Experimental - Replace NavigationHeader Title Within a Scenes View

I'm trying to update the NavigationHeader title (along with left and right components) from a specific scene's view.
Example:
User navigates to profile view
Get data from server
Call redux action to update the navigation header with the username
My current approach is not working too well. I'm trying to use the NavigationStateUtils to replace the scene with an updated version of itself. This works if my view is the first one in the stack. However, if I try adding the call to update nav header on a route that isn't the first, it will freeze because the navigation hasn't finished it's animation.
I could try adding a timeout on the call, but it seems hacky to come up with a delay that makes sense globally.
case UPDATE_NAV_HEADER: {
const tabs = state.get('tabs')
const tabKey = tabs.getIn(['routes', tabs.get('index')]).get('key')
const scenes = state.get(tabKey).toJS()
const route = scenes.routes.slice(-1)[0]
var leftComponent = null
if (action.payload.leftComponent) {
leftComponent = action.payload.leftComponent
} else if (route.navLeftComponent) {
leftComponent = route.navLeftComponent
}
var rightComponent = null
if (action.payload.rightComponent) {
rightComponent = action.payload.rightComponent
} else if (route.navRightComponent) {
rightComponent = route.navRightComponent
}
const newScene = NavigationStateUtils.replaceAt(
scenes,
route.key,
{
key: route.key,
title: action.payload.newTitle ? action.payload.newTitle : route.title,
navLeftComponent: leftComponent,
navRightComponent: rightComponent,
shouldRenderHeader: "shouldRenderHeader" in route ? route.shouldRenderHeader : true,
shouldRenderTabBar: "shouldRenderTabBar" in route ? route.shouldRenderTabBar : true
}
)
return state.set(tabKey, fromJS(newScene))
}

ExtJS loading panels repetition

In my website, in order to load diferent pages (to be multipage website) I have a main panel that has the id 'content-panel'.
When I want to load a diferent page I have a javascript function that is called 'loadPage' that loads the page (panel) that I want to the 'content-panel'.
But the page that I want to load has to have this code:
Ext.require(['*']);
Ext.onReady(function() {
...
var panel = Ext.Cmp('content-panel');
panel.add(loginPanel);
panel.layout.setActiveItem(loginPanel);
panel.doLayout();
panel.setLoading(false);
});
In this case it is loading the page/panel that is loginPanel, that is defined inside Ext.onReady
For me this is fine, I don't know of any other way of my website being multi-page.
But everytime that I want to go to a page it loads that page to the 'content-panel', even if it already been loaded before. I want a way to only add the page to 'content-panel' if it is not inside 'content-panel' items.
UPDATE:
Here is the loadPage
function swap(parent, replacement, url) {
var alpha = document.querySelector(parent);
var target = alpha.childNodes[0];
var omega = document.createElement(replacement);
omega.src = url;
omega.type = 'text/javascript';
alpha.replaceChild(omega, target);
}
function loadPage(panel, toPanel) {
toPanel.setLoading(true);
swap('head', 'script', panel);
}
it is used like this: loadPage('Ctl_base/view_admin/mainPage', Ext.getCmp('panel'));
I'm using CodeIgniter with ExtJS.
What I have already tried:
I want to do panel.add(loginPanel) only if the loginPanel doesn't exist.
I have tried:
if(panel.getComponent(loginScreen) == undefined) { panel.add(loginPanel); }
and it adds the component even when panel already has that component.
I have also tried:
function hasComponent(parent, child) {
parent.items.items.forEach(function(item) {
if(item == child){
return true;
}
});
return false;
}
if(!hasComponent(panel, loginPanel)) { panel.add(loginPanel); }
and it also doesn't work.
I have manage to tackle this question by putting and itemId on the panel that I want to load, and on panel.layout.setActiveItem(loginPanel); I have put panel.layout.setActiveItem('itemIdOfPanel');

Resources