Skip to content

Commit

Permalink
MM 27448 remove jquery from root (mattermost#6730)
Browse files Browse the repository at this point in the history
Co-authored-by: Joseph Baylon <[email protected]>
  • Loading branch information
CEOehis and Joseph Baylon committed Oct 22, 2020
1 parent d23f76b commit 90912d6
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 88 deletions.
84 changes: 26 additions & 58 deletions components/logged_in/logged_in.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import $ from 'jquery';
import PropTypes from 'prop-types';
import React from 'react';
import {Redirect} from 'react-router';
Expand Down Expand Up @@ -58,16 +57,7 @@ export default class LoggedIn extends React.PureComponent {
}

// Make sure the websockets close and reset version
$(window).on('beforeunload',
() => {
// Turn off to prevent getting stuck in a loop
$(window).off('beforeunload');
if (document.cookie.indexOf('MMUSERID=') > -1) {
viewChannel('', this.props.currentChannelId || '')(dispatch, getState);
}
WebSocketActions.close();
},
);
window.addEventListener('beforeunload', this.handleBeforeUnload);

// Listen for focused tab/window state
window.addEventListener('focus', this.onFocusListener);
Expand All @@ -84,56 +74,23 @@ export default class LoggedIn extends React.PureComponent {
window.location.origin,
);

// Because current CSS requires the root tag to have specific stuff

// Device tracking setup
if (UserAgent.isIos()) {
$('body').addClass('ios'); // eslint-disable-line jquery/no-class
document.body.classList.add('ios');
} else if (UserAgent.isAndroid()) {
$('body').addClass('android'); // eslint-disable-line jquery/no-class
document.body.classList.add('android');
}

if (!this.props.currentUser) {
$('#root').attr('class', ''); // eslint-disable-line jquery/no-attr
const rootEl = document.getElementById('root');
if (rootEl) {
rootEl.setAttribute('class', '');
}
GlobalActions.emitUserLoggedOutEvent('/login?redirect_to=' + encodeURIComponent(this.props.location.pathname), true, false);
}

$('body').on('mouseenter mouseleave', ':not(.post-list__dynamic) .post', function mouseOver(ev) {
if (ev.type === 'mouseenter') {
$(this).prev('.date-separator, .new-separator').addClass('hovered--after'); // eslint-disable-line jquery/no-find, jquery/no-class
$(this).next('.date-separator, .new-separator').addClass('hovered--before'); // eslint-disable-line jquery/no-find, jquery/no-class
} else {
$(this).prev('.date-separator, .new-separator').removeClass('hovered--after'); // eslint-disable-line jquery/no-find, jquery/no-class
$(this).next('.date-separator, .new-separator').removeClass('hovered--before'); // eslint-disable-line jquery/no-find, jquery/no-class
}
});

$('body').on('mouseenter mouseleave', '.search-item__container .post', function mouseOver(ev) {
if (ev.type === 'mouseenter') {
$(this).closest('.search-item__container').find('.date-separator').addClass('hovered--after'); // eslint-disable-line jquery/no-closest, jquery/no-find, jquery/no-class
$(this).closest('.search-item__container').next('div').find('.date-separator').addClass('hovered--before'); // eslint-disable-line jquery/no-closest, jquery/no-find, jquery/no-class
} else {
$(this).closest('.search-item__container').find('.date-separator').removeClass('hovered--after'); // eslint-disable-line jquery/no-closest, jquery/no-find, jquery/no-class
$(this).closest('.search-item__container').next('div').find('.date-separator').removeClass('hovered--before'); // eslint-disable-line jquery/no-closest, jquery/no-find, jquery/no-class
}
});

$('body').on('mouseenter mouseleave', ':not(.post-list__dynamic) .post.post--comment.same--root', function mouseOver(ev) {
if (ev.type === 'mouseenter') {
$(this).prev('.date-separator, .new-separator').addClass('hovered--comment'); // eslint-disable-line jquery/no-find, jquery/no-class
$(this).next('.date-separator, .new-separator').addClass('hovered--comment'); // eslint-disable-line jquery/no-find, jquery/no-class
} else {
$(this).prev('.date-separator, .new-separator').removeClass('hovered--comment'); // eslint-disable-line jquery/no-find, jquery/no-class
$(this).next('.date-separator, .new-separator').removeClass('hovered--comment'); // eslint-disable-line jquery/no-find, jquery/no-class
}
});

// Prevent backspace from navigating back a page
$(window).on('keydown.preventBackspace', (e) => {
if (e.which === BACKSPACE_CHAR && !$(e.target).is('input, textarea')) { // eslint-disable-line jquery/no-is
e.preventDefault();
}
});
window.addEventListener('keydown', this.handleBackSpace);

if (this.isValidState()) {
BrowserStore.signalLogin();
Expand All @@ -143,13 +100,7 @@ export default class LoggedIn extends React.PureComponent {
componentWillUnmount() {
WebSocketActions.close();

$('body').off('click.userpopover');
$('body').off('mouseenter mouseleave', '.post');
$('body').off('mouseenter mouseleave', '.post.post--comment.same--root');

$('.modal').off('show.bs.modal');

$(window).off('keydown.preventBackspace');
window.removeEventListener('keydown', this.handleBackSpace);

window.removeEventListener('focus', this.onFocusListener);
window.removeEventListener('blur', this.onBlurListener);
Expand Down Expand Up @@ -221,4 +172,21 @@ export default class LoggedIn extends React.PureComponent {
}
}
}

handleBackSpace = (e) => {
const excludedElements = ['input', 'textarea'];

if (e.which === BACKSPACE_CHAR && !(excludedElements.includes(e.target.tagName.toLowerCase()))) {
e.preventDefault();
}
}

handleBeforeUnload = () => {
// remove the event listener to prevent getting stuck in a loop
window.removeEventListener('beforeunload', this.handleBeforeUnload);
if (document.cookie.indexOf('MMUSERID=') > -1) {
viewChannel('', this.props.currentChannelId || '')(dispatch, getState);
}
WebSocketActions.close();
}
}
54 changes: 27 additions & 27 deletions components/root/root.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import $ from 'jquery';

import {rudderAnalytics, Client4} from 'mattermost-redux/client';
import PropTypes from 'prop-types';
import React from 'react';
Expand Down Expand Up @@ -108,30 +106,7 @@ export default class Root extends React.PureComponent {
setSystemEmojis(EmojiIndicesByAlias);

// Force logout of all tabs if one tab is logged out
$(window).bind('storage', (e) => { // eslint-disable-line jquery/no-bind
// when one tab on a browser logs out, it sets __logout__ in localStorage to trigger other tabs to log out
const isNewLocalStorageEvent = (event) => event.originalEvent.storageArea === localStorage && event.originalEvent.newValue;

if (e.originalEvent.key === StoragePrefixes.LOGOUT && isNewLocalStorageEvent(e)) {
console.log('detected logout from a different tab'); //eslint-disable-line no-console
GlobalActions.emitUserLoggedOutEvent('/', false, false);
}
if (e.originalEvent.key === StoragePrefixes.LOGIN && isNewLocalStorageEvent(e)) {
const isLoggedIn = getCurrentUser(store.getState());

// make sure this is not the same tab which sent login signal
// because another tabs will also send login signal after reloading
if (isLoggedIn) {
return;
}

// detected login from a different tab
function onVisibilityChange() {
location.reload();
}
document.addEventListener('visibilitychange', onVisibilityChange, false);
}
});
window.addEventListener('storage', this.handleLogoutLoginSignal);

// Prevent drag and drop files from navigating away from the app
document.addEventListener('drop', (e) => {
Expand Down Expand Up @@ -261,7 +236,32 @@ export default class Root extends React.PureComponent {

componentWillUnmount() {
this.mounted = false;
$(window).unbind('storage');
window.removeEventListener('storage', this.handleLogoutLoginSignal);
}

handleLogoutLoginSignal = (e) => {
// when one tab on a browser logs out, it sets __logout__ in localStorage to trigger other tabs to log out
const isNewLocalStorageEvent = (event) => event.storageArea === localStorage && event.newValue;

if (e.key === StoragePrefixes.LOGOUT && isNewLocalStorageEvent(e)) {
console.log('detected logout from a different tab'); //eslint-disable-line no-console
GlobalActions.emitUserLoggedOutEvent('/', false, false);
}
if (e.key === StoragePrefixes.LOGIN && isNewLocalStorageEvent(e)) {
const isLoggedIn = getCurrentUser(store.getState());

// make sure this is not the same tab which sent login signal
// because another tabs will also send login signal after reloading
if (isLoggedIn) {
return;
}

// detected login from a different tab
function onVisibilityChange() {
location.reload();
}
document.addEventListener('visibilitychange', onVisibilityChange, false);
}
}

render() {
Expand Down
5 changes: 2 additions & 3 deletions components/tutorial/tutorial_view.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import $ from 'jquery';
import PropTypes from 'prop-types';
import React from 'react';

Expand All @@ -10,13 +9,13 @@ import TutorialIntroScreens from './tutorial_intro_screens';
export default class TutorialView extends React.PureComponent {
componentDidMount() {
if (this.props.isRoot) {
$('body').addClass('app__body'); // eslint-disable-line jquery/no-class
document.body.classList.add('app__body');
}
}

componentWillUnmount() {
if (this.props.isRoot) {
$('body').removeClass('app__body'); // eslint-disable-line jquery/no-class
document.body.classList.remove('app__body');
}
}

Expand Down
55 changes: 55 additions & 0 deletions e2e/cypress/integration/keyboard_shortcuts/backspace_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************

// Group: @keyboard_shortcuts

describe('Keyboard Shortcuts', () => {
let testUser;
let testTeam;
let publicChannel;

before(() => {
cy.apiInitSetup({loginAfter: true}).then(({team, channel, user}) => {
// # Visit a test channel
testTeam = team;
publicChannel = channel;
testUser = user;

cy.visit(`/${team.name}/channels/${channel.name}`);
});
});

it('MM-T3421 - Pressing the backspace key without an input focused should not send the browser back in history', () => {
// # Navigate to a couple of pages
cy.visit(`/${testTeam.name}/channels/town-square`);
cy.visit(`/${testTeam.name}/channels/off-topic`);
cy.visit(`/${testTeam.name}/channels/${publicChannel.name}`);
cy.visit(`/${testTeam.name}/channels/off-topic`);

// # Visit a DM URL
cy.visit(`/${testTeam.name}/messages/@${testUser.username}`);
cy.url().should('include', `/${testTeam.name}/messages/@${testUser.username}`);

// # Type/edit some text and remove focus from the input field
cy.get('#post_textbox').clear().type('This is a normal sentence.').type('{backspace}{backspace}').blur();

// * Verify that the backspace key presses modified the input correctly
cy.get('#post_textbox').should('have.value', 'This is a normal sentenc');

// # Select the body to remove focus from the input field
cy.get('body').type('{backspace}');
cy.get('body').type('{backspace}');

// * Verify that the additional backspace key presses on blur doesn't affect the input
cy.get('#post_textbox').should('have.value', 'This is a normal sentenc');

// * Verify that the URL doesn't change from the last URL
cy.url().should('include', `/${testTeam.name}/messages/@${testUser.username}`);
});
});

0 comments on commit 90912d6

Please sign in to comment.