Regression: Improve Sidenav open/close handling and fixed codeql configs and E2E tests (#24756)

pull/24753/head^2
Guilherme Gazzo 4 years ago committed by GitHub
parent 7676e95cef
commit 608e4f9d78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 82
      .github/workflows/codeql-analysis.yml
  2. 2
      app/ui-sidenav/client/sideNav.html
  3. 23
      app/ui-sidenav/client/sideNav.js
  4. 2
      app/ui-utils/client/lib/RoomManager.js
  5. 125
      app/ui-utils/client/lib/SideNav.js
  6. 114
      app/ui-utils/client/lib/SideNav.ts
  7. 136
      app/ui-utils/client/lib/menu.js
  8. 2
      client/lib/RoomManager.ts
  9. 3
      client/startup/unread.ts
  10. 29
      tests/cypress/integration/08-resolutions.spec.js

@ -1,4 +1,4 @@
name: "Code scanning - action"
name: 'Code scanning - action'
on:
push:
@ -6,52 +6,46 @@ on:
schedule:
- cron: '0 13 * * *'
paths-ignore:
- '**/*.spec.js'
- '**/*.test.js'
- '**/*.tests.js'
jobs:
CodeQL-Build:
# CodeQL runs on ubuntu-latest and windows-latest
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
# Override language selection by uncommenting this and choosing your languages
with:
languages: javascript
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# ℹ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
- name: Checkout repository
uses: actions/checkout@v3
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
# Override language selection by uncommenting this and choosing your languages
with:
languages: javascript
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# ℹ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

@ -1,5 +1,5 @@
<template name="sideNav">
<aside class="rcx-sidebar sidebar sidebar--main sidebar--{{sidebarViewMode}} {{#if sidebarHideAvatar}}sidebar--hide-avatar{{/if}}" role="navigation">
<aside class="rcx-sidebar sidebar sidebar--main sidebar--{{sidebarViewMode}} {{#if sidebarHideAvatar}}sidebar--hide-avatar{{/if}}" role="navigation" data-qa-opened="{{dataQa}}">
{{> sidebarHeader }}
<div class="wrapper-unread">
<div class="unread-rooms background-primary-action-color color-primary-action-contrast top-unread-rooms hidden">

@ -11,6 +11,10 @@ import { Users } from '../../models';
import { roomCoordinator } from '../../../client/lib/rooms/roomCoordinator';
Template.sideNav.helpers({
dataQa() {
return Template.instance().menuState.get() === 'opened';
},
flexTemplate() {
return SideNav.getFlex().template;
},
@ -49,14 +53,6 @@ Template.sideNav.events({
return SideNav.closeFlex();
},
'click .arrow'() {
return SideNav.toggleCurrent();
},
'scroll .rooms-list'() {
return menu.updateUnreadBars();
},
'dropped .sidebar'(e) {
return e.preventDefault();
},
@ -103,14 +99,21 @@ const redirectToDefaultChannelIfNeeded = () => {
Template.sideNav.onRendered(function () {
SideNav.init();
menu.init();
redirectToDefaultChannelIfNeeded();
return Meteor.defer(() => menu.updateUnreadBars());
this.stopMenuListener = menu.on('change', () => {
this.menuState.set(menu.isOpen() ? 'opened' : 'closed');
});
redirectToDefaultChannelIfNeeded();
});
Template.sideNav.onDestroyed(function () {
this.stopMenuListener();
});
Template.sideNav.onCreated(function () {
this.groupedByType = new ReactiveVar(false);
this.menuState = new ReactiveVar(menu.isOpen() ? 'opened' : 'closed');
this.autorun(() => {
const user = Users.findOne(Meteor.userId(), {
fields: {

@ -9,7 +9,6 @@ import _ from 'underscore';
import { fireGlobalEvent } from '../../../../client/lib/utils/fireGlobalEvent';
import { upsertMessage, RoomHistoryManager } from './RoomHistoryManager';
import { mainReady } from './mainReady';
import { menu } from './menu';
import { callbacks } from '../../../../lib/callbacks';
import { Notifications } from '../../../notifications';
import { CachedChatRoom, ChatMessage, ChatSubscription, CachedChatSubscription, ChatRoom } from '../../../models';
@ -117,7 +116,6 @@ export const RoomManager = new (function () {
name,
};
if (isNew) {
menu.updateUnreadBars();
callbacks.run('streamNewMessage', msg);
}
}

@ -1,125 +0,0 @@
import { FlowRouter } from 'meteor/kadira:flow-router';
import { Session } from 'meteor/session';
import { AccountBox } from './AccountBox';
import { Subscriptions } from '../../../models';
import { RoomManager } from '../../../../client/lib/RoomManager';
import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator';
export const SideNav = new (class {
constructor() {
this.initiated = false;
this.sideNav = {};
this.flexNav = {};
this.animating = false;
this.openQueue = [];
}
toggleFlex(status, callback) {
if (this.animating === true) {
return;
}
this.animating = true;
if (status === -1 || (status !== 1 && this.flexNav.opened)) {
this.flexNav.opened = false;
this.flexNav.addClass('animated-hidden');
} else {
this.flexNav.opened = true;
if (window.DISABLE_ANIMATION === true) {
this.flexNav.removeClass('animated-hidden');
} else {
setTimeout(() => this.flexNav.removeClass('animated-hidden'), 50);
}
}
if (window.DISABLE_ANIMATION === true) {
!this.flexNav.opened && this.setFlex();
this.animating = false;
return typeof callback === 'function' && callback();
}
return setTimeout(() => {
!this.flexNav.opened && this.setFlex();
this.animating = false;
return typeof callback === 'function' && callback();
}, 500);
}
closeFlex(callback = null) {
if (!roomCoordinator.isRouteNameKnown(FlowRouter.current().route.name)) {
const subscription = Subscriptions.findOne({ rid: RoomManager.lastRid });
if (subscription) {
roomCoordinator.openRouteLink(subscription.t, subscription, FlowRouter.current().queryParams);
} else {
FlowRouter.go('home');
}
}
if (this.animating === true) {
return;
}
this.toggleFlex(-1, callback);
}
flexStatus() {
return this.flexNav.opened;
}
setFlex(template, data = {}) {
Session.set('flex-nav-template', template);
return Session.set('flex-nav-data', data);
}
getFlex() {
return {
template: Session.get('flex-nav-template'),
data: Session.get('flex-nav-data'),
};
}
toggleCurrent() {
if (this.flexNav && this.flexNav.opened) {
return this.closeFlex();
}
return AccountBox.toggle();
}
validate() {
const invalid = [];
this.sideNav.find('input.required').each(function () {
if (!this.value.length) {
return invalid.push($(this).prev('label').html());
}
});
if (invalid.length) {
return invalid;
}
return false;
}
openFlex(callback = () => {}) {
if (!this.initiated) {
return this.openQueue.push({
config: this.getFlex(),
callback,
});
}
if (this.animating === true) {
return;
}
this.toggleFlex(1, callback);
}
init() {
this.sideNav = $('.sidebar');
this.flexNav = this.sideNav.find('.flex-nav');
this.setFlex('');
this.initiated = true;
if (this.openQueue.length > 0) {
this.openQueue.forEach((item) => {
this.setFlex(item.config.template, item.config.data);
return this.openFlex(item.callback);
});
this.openQueue = [];
}
}
})();

@ -0,0 +1,114 @@
import { FlowRouter } from 'meteor/kadira:flow-router';
import { Session } from 'meteor/session';
import { Emitter } from '@rocket.chat/emitter';
import { Subscriptions } from '../../../models/client';
import { RoomManager } from '../../../../client/lib/RoomManager';
import { roomCoordinator } from '../../../../client/lib/rooms/roomCoordinator';
export const SideNav = new (class extends Emitter<{
changed: undefined;
}> {
private opened = false;
private initiated = false;
private openQueue: {
config: {
template?: string;
data: Record<string, unknown>;
};
callback: () => void;
}[] = [];
private animating = false;
private sideNav: JQuery<HTMLElement>;
private flexNav: JQuery<HTMLElement>;
toggleFlex(status: 1 | -1, callback: () => void): void {
if (this.animating === true) {
return;
}
this.animating = true;
if (status === -1 || (status !== 1 && this.opened)) {
this.opened = false;
this.flexNav.addClass('animated-hidden');
} else {
this.opened = true;
this.flexNav.removeClass('animated-hidden');
}
this.emit('changed');
!this.opened && this.setFlex();
this.animating = false;
typeof callback === 'function' && callback();
}
closeFlex(callback: () => void = (): void => undefined): void {
const routeName = FlowRouter.current().route?.name;
if (!routeName || !roomCoordinator.isRouteNameKnown(routeName)) {
const subscription = Subscriptions.findOne({ rid: RoomManager.lastRid });
if (subscription) {
roomCoordinator.openRouteLink(subscription.t, subscription, FlowRouter.current().queryParams);
} else {
FlowRouter.go('home');
}
}
if (this.animating === true) {
return;
}
this.toggleFlex(-1, callback);
}
flexStatus(): boolean {
return this.opened;
}
setFlex(template?: string, data = {}): void {
Session.set('flex-nav-template', template);
return Session.set('flex-nav-data', data);
}
getFlex(): {
template?: string;
data: Record<string, unknown>;
} {
return {
template: Session.get('flex-nav-template'),
data: Session.get('flex-nav-data'),
};
}
openFlex(callback = (): void => undefined): void {
if (!this.initiated) {
this.openQueue.push({
config: this.getFlex(),
callback,
});
return;
}
if (this.animating === true) {
return;
}
this.toggleFlex(1, callback);
}
init(): void {
this.sideNav = $('.sidebar');
this.flexNav = this.sideNav.find('.flex-nav');
this.setFlex('');
this.initiated = true;
if (this.openQueue.length > 0) {
this.openQueue.forEach((item) => {
this.setFlex(item.config.template, item.config.data);
return this.openFlex(item.callback);
});
this.openQueue = [];
}
}
})();

@ -1,6 +1,5 @@
import { Session } from 'meteor/session';
import { Meteor } from 'meteor/meteor';
import _ from 'underscore';
import { Emitter } from '@rocket.chat/emitter';
import { isRtl } from '../../../utils';
@ -12,32 +11,7 @@ export const menu = new (class extends Emitter {
constructor() {
super();
this._open = false;
this.updateUnreadBars = _.throttle(() => {
if (this.list == null) {
return;
}
const listOffset = this.list.offset();
const listHeight = this.list.height();
let showTop = false;
let showBottom = false;
$('li.sidebar-item--unread').each(function () {
if ($(this).offset().top < listOffset.top - $(this).height()) {
showTop = true;
}
if ($(this).offset().top > listOffset.top + listHeight) {
showBottom = true;
}
});
if (showTop === true) {
$('.top-unread-rooms').removeClass('hidden');
} else {
$('.top-unread-rooms').addClass('hidden');
}
if (showBottom === true) {
return $('.bottom-unread-rooms').removeClass('hidden');
}
return $('.bottom-unread-rooms').addClass('hidden');
}, 200);
this.sideNavW = sideNavW;
}
@ -45,73 +19,6 @@ export const menu = new (class extends Emitter {
return isRtl(Meteor._localStorage.getItem('userLanguage'));
}
touchstart(e) {
this.movestarted = false;
this.blockmove = false;
this.touchstartX = undefined;
this.touchstartY = undefined;
this.diff = 0;
if (e.target === this.sidebarWrap[0] || $(e.target).closest('.main-content').length > 0) {
this.closePopover();
this.touchstartX = e.touches[0].clientX;
this.touchstartY = e.touches[0].clientY;
this.mainContent = $('.main-content');
}
}
touchmove(e) {
if (this.touchstartX == null) {
return;
}
const [touch] = e.touches;
const diffX = touch.clientX - this.touchstartX;
const diffY = touch.clientY - this.touchstartY;
const absX = Math.abs(diffX);
const absY = Math.abs(diffY);
if (!this.movestarted && absY > 5) {
this.blockmove = true;
}
if (this.blockmove) {
return;
}
this.sidebar.css('transition', 'none');
this.sidebarWrap.css('transition', 'none');
if (this.movestarted === true || absX > 5) {
this.movestarted = true;
if (this.isRtl) {
if (this.isOpen()) {
this.diff = -sideNavW + diffX;
} else {
this.diff = diffX;
}
if (this.diff < -sideNavW) {
this.diff = -sideNavW;
}
if (this.diff > 0) {
this.diff = 0;
}
} else {
if (this.isOpen()) {
this.diff = sideNavW + diffX;
} else {
this.diff = diffX;
}
if (this.diff > sideNavW) {
this.diff = sideNavW;
}
if (this.diff < 0) {
this.diff = 0;
}
}
// if (map((this.diff / sideNavW), 0, 1, -.1, .8) > 0) {
this.sidebar.css('box-shadow', '0 0 15px 1px rgba(0,0,0,.3)');
// this.sidebarWrap.css('z-index', '9998');
this.translate(this.diff);
// }
}
}
translate(diff, width = sideNavW) {
if (diff === undefined) {
diff = this.isRtl ? -1 * sideNavW : sideNavW;
@ -125,33 +32,6 @@ export const menu = new (class extends Emitter {
: this.sidebar.css('transform', `translate3d(${(diff - sideNavW).toFixed(3)}px, 0 , 0)`);
}
touchend() {
const [max, min] = [sideNavW * 0.76, sideNavW * 0.24];
if (this.movestarted !== true) {
return;
}
this.movestarted = false;
if (this.isRtl) {
if (this.isOpen()) {
return this.diff >= -max ? this.close() : this.open();
}
if (this.diff <= -min) {
return this.open();
}
return this.close();
}
if (this.isOpen()) {
if (this.diff >= max) {
return this.open();
}
return this.close();
}
if (this.diff >= min) {
return this.open();
}
return this.close();
}
init() {
this.menu = $('.sidebar');
this.sidebar = this.menu;
@ -159,18 +39,6 @@ export const menu = new (class extends Emitter {
this.wrapper = $('.messages-box > .wrapper');
const ignore = (fn) => (event) => document.body.clientWidth <= 780 && fn(event);
document.body.addEventListener(
'touchstart',
ignore((e) => this.touchstart(e)),
);
document.body.addEventListener(
'touchmove',
ignore((e) => this.touchmove(e)),
);
document.body.addEventListener(
'touchend',
ignore((e) => this.touchend(e)),
);
this.sidebarWrap.on(
'click',
ignore((e) => {
@ -213,12 +81,14 @@ export const menu = new (class extends Emitter {
open() {
this._open = true;
Session.set('isMenuOpen', this._open);
this.emit('change');
this.emit('open');
}
close() {
this._open = false;
Session.set('isMenuOpen', this._open);
this.emit('change');
this.emit('close');
}

@ -54,7 +54,7 @@ export const RoomManager = new (class RoomManager extends Emitter<{
}> {
private rid: IRoom['_id'] | undefined;
private lastRid: IRoom['_id'] | undefined;
public lastRid: IRoom['_id'] | undefined;
private rooms: Map<IRoom['_id'], RoomStore> = new Map();

@ -5,7 +5,6 @@ import { Tracker } from 'meteor/tracker';
import { Favico } from '../../app/favico/client';
import { ChatSubscription, ChatRoom } from '../../app/models/client';
import { settings } from '../../app/settings/client';
import { menu } from '../../app/ui-utils/client';
import { getUserPreference } from '../../app/utils/client';
import { ISubscription } from '../../definition/ISubscription';
import { fireGlobalEvent } from '../lib/utils/fireGlobalEvent';
@ -61,8 +60,6 @@ Meteor.startup(() => {
0,
);
menu.updateUnreadBars();
if (unreadCount > 0) {
if (unreadCount > 999) {
Session.set('unread', '999+');

@ -13,51 +13,48 @@ describe('[Resolution]', () => {
beforeEach(() => {
Global.setWindowSize(650, 800);
cy.wait(500);
cy.wait(50);
});
after(() => {
Global.setWindowSize(1600, 1600);
cy.wait(500);
// cy.wait(500);
sideNav.spotlightSearchIcon.should('be.visible');
});
it('it should close the sidenav', () => {
mainContent.mainContent.should('be.visible').getLocation().its('x').should('be.equal', 0);
sideNav.sideNavBar.should('be.visible').getLocation().its('x').should('not.be.equal', 0);
sideNav.sideNavBar.should('not.have.attr', 'data-qa-opened', 'false');
});
describe('moving elements:', () => {
beforeEach(() => {
cy.wait(150);
sideNav.sideNavBar
.getLocation()
.its('x')
.then((x) => {
if (x !== 0) {
cy.wait(150);
.parent()
.find('.sidebar')
.then((el) => {
if (!el[0].hasAttribute('data-qa-opened')) {
sideNav.burgerBtn.click({ force: true });
cy.wait(500);
}
});
});
it('it should open de sidenav', () => {
it('it should open the sidenav', () => {
mainContent.mainContent.should('be.visible').getLocation().its('x').should('be.equal', 0);
sideNav.sideNavBar.should('be.visible').getLocation().its('x').should('be.equal', 0);
sideNav.sideNavBar.should('have.attr', 'data-qa-opened', 'true');
});
it('it should not close sidebar on pressing the sidebar item menu', () => {
sideNav.firstSidebarItemMenu.click();
mainContent.mainContent.should('be.visible').getLocation().its('x').should('be.equal', 0);
sideNav.sideNavBar.should('be.visible').getLocation().its('x').should('be.equal', 0);
sideNav.sideNavBar.should('have.attr', 'data-qa-opened', 'true');
sideNav.firstSidebarItemMenu.click();
});
it('it should close the sidenav when open general channel', () => {
sideNav.openChannel('general');
cy.wait(500);
sideNav.sideNavBar.getLocation().its('x').should('not.be.equal', 0);
sideNav.sideNavBar.should('not.have.attr', 'data-qa-opened');
});
// Skipped because it's not closing sidebar after opening an item
@ -69,12 +66,12 @@ describe('[Resolution]', () => {
it('it should close the sidenav when press the preferences link', () => {
sideNav.preferences.click();
sideNav.sideNavBar.getLocation().its('x').should('not.be.equal', 0);
sideNav.sideNavBar.should('not.have.attr', 'data-qa-opened');
});
it('it should close the sidenav when press the profile link', () => {
sideNav.profile.click();
sideNav.sideNavBar.getLocation().its('x').should('not.be.equal', 0);
sideNav.sideNavBar.should('not.have.attr', 'data-qa-opened');
});
it('it should close the preferences nav', () => {

Loading…
Cancel
Save