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

Enforce new ESLint rules for all .js and .js.erb files #575

Merged
merged 10 commits into from
Jan 4, 2024
56 changes: 50 additions & 6 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,76 @@ const stylistic = require("@stylistic/eslint-plugin");

const customizedStylistic = stylistic.configs.customize({
"indent": 2,
"quotes": "double",
"jsx": false,
"quote-props": "always",
"semi": "always",
"brace-style": "1tbs",
});

const cypressRules = {
"cypress/no-assigning-return-values": "error",
"cypress/no-unnecessary-waiting": "off", // TODO: fix this issue
"cypress/assertion-before-screenshot": "warn",
"cypress/no-force": "warn",
"cypress/no-async-tests": "error",
"cypress/no-pause": "error",
};

const ignoreFilesWithSprocketRequireSyntax = [
"app/assets/javascripts/application.js",
"app/assets/config/manifest.js",
"app/assets/javascripts/edit_clicker_assets.js",
"app/assets/javascripts/show_clicker_assets.js",
"app/assets/javascripts/geogebra_assets.js",
"vendor/assets/javascripts/thredded_timeago.js",
];

const customGlobals = {
TomSelect: "readable",
bootstrap: "readable",

// Rails globals
Routes: "readable",
App: "readable",
ActionCable: "readable",

// Common global methods
initBootstrapPopovers: "readable",
};

module.exports = {
root: true,
parserOptions: {
ecmaVersion: 2022,
sourceType: "module",
},
env: {
node: true,
browser: true,
jquery: true,
"node": true,
"browser": true,
"jquery": true,
"cypress/globals": true,
},
extends: [
"eslint:recommended",
// Allow linting of ERB files, see https://github.com/Splines/eslint-plugin-erb
"plugin:erb/recommended",
],
plugins: ["@stylistic", "erb"],
globals: customGlobals,
plugins: ["@stylistic", "erb", "cypress"],
rules: {
...customizedStylistic.rules,
"no-unused-vars": "warn",
"no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
...cypressRules,
// see https://github.com/eslint-stylistic/eslint-stylistic/issues/254
"@stylistic/quotes": ["error", "double", { avoidEscape: true }],
},
ignorePatterns: [
"node_modules/",
"pdfcomprezzor/",
"tmp/",
"public/packs/",
"public/packs-test/",
"public/uploads/",
...ignoreFilesWithSprocketRequireSyntax,
],
};
170 changes: 90 additions & 80 deletions app/assets/javascripts/_selectize_turbolinks_fix.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,130 +5,140 @@
// transfer knowledge about selected items from selectize to html options
var resetSelectized;

resetSelectized = function(index, select) {
resetSelectized = function (index, select) {
var i, len, selectedValue, val;
selectedValue = select.tomselect.getValue();
select.tomselect.destroy();
$(select).find('option').attr('selected', null);
if ($(select).prop('multiple')) {
$(select).find("option").attr("selected", null);
if ($(select).prop("multiple")) {
for (i = 0, len = selectedValue.length; i < len; i++) {
val = selectedValue[i];
if (val !== '') {
$(select).find("option[value='" + val + "']").attr('selected', true);
if (val !== "") {
$(select).find("option[value='" + val + "']").attr("selected", true);
}
}
} else {
if (selectedValue !== '') {
$(select).find("option[value='" + selectedValue + "']").attr('selected', true);
}
else {
if (selectedValue !== "") {
$(select).find("option[value='" + selectedValue + "']").attr("selected", true);
}
}
};

this.fillOptionsByAjax = function($selectizedSelection) {
$selectizedSelection.each(function() {
var courseId, existing_values, fill_path, loaded, locale, model_select, plugins, send_data, parent;
if (this.dataset.drag === 'true') {
plugins = ['remove_button', 'drag_drop'];
} else {
plugins = ['remove_button'];
function fillOptionsByAjax($selectizedSelection) {
// TODO: this function definitely needs some refactoring
$selectizedSelection.each(function () {
let plugins = [];
let send_data = false;
let fill_path = "";
let courseId = 0;
let loaded = false;
let locale = null;

if (this.dataset.drag === "true") {
plugins = ["remove_button", "drag_drop"];
}
else {
plugins = ["remove_button"];
}
if (this.dataset.ajax === 'true' && this.dataset.filled === 'false') {
model_select = this;
if (this.dataset.ajax === "true" && this.dataset.filled === "false") {
const model_select = this;
courseId = 0;
placeholder = this.dataset.placeholder;
no_result_msg = this.dataset.noResults;
existing_values = Array.apply(null, model_select.options).map(function(o) {
return o.value;
});
const placeholder = this.dataset.placeholder;
const no_result_msg = this.dataset.noResults;
send_data = false;
loaded = false;
parent = this.dataset.modal === undefined ? document.body : null;
if (this.dataset.model === 'tag') {
if (this.dataset.model === "tag") {
locale = this.dataset.locale;
fill_path = Routes.fill_tag_select_path({
locale: locale
locale: locale,
});
send_data = true;
} else if (this.dataset.model === 'user') {
}
else if (this.dataset.model === "user") {
fill_path = Routes.fill_user_select_path();
send_data = true;
} else if (this.dataset.model === 'user_generic') {
}
else if (this.dataset.model === "user_generic") {
fill_path = Routes.list_generic_users_path();
} else if (this.dataset.model === 'teachable') {
}
else if (this.dataset.model === "teachable") {
fill_path = Routes.fill_teachable_select_path();
} else if (this.dataset.model === 'medium') {
}
else if (this.dataset.model === "medium") {
fill_path = Routes.fill_media_select_path();
} else if (this.dataset.model === 'course_tag') {
}
else if (this.dataset.model === "course_tag") {
courseId = this.dataset.course;
fill_path = Routes.fill_course_tags_path();
}
(function() {
class MinimumLengthSelect extends TomSelect{

refreshOptions(triggerDropdown=true){
(function () {
class MinimumLengthSelect extends TomSelect {
refreshOptions(triggerDropdown = true) {
var query = this.inputValue();
if( query.length < 2){
if (query.length < 2) {
this.close(false);
return;
}

super.refreshOptions(triggerDropdown);
}

}
new MinimumLengthSelect("#" + model_select.id, {
plugins: plugins,
valueField: 'value',
labelField: 'name',
searchField: 'name',
maxOptions: null,
placeholder: placeholder,
closeAfterSelect: true,
load: function(query, callback) {
var url;
if (send_data || !loaded) {
url = fill_path + "?course_id=" + courseId + "&q=" + encodeURIComponent(query);
fetch(url).then(function(response) {
return response.json();
}).then(function(json) {
loaded = true;
return callback(json.map(function(item) {
return {
name: item.text,
value: item.value
};
}));
})["catch"](function() {
callback();
});
}
callback();
},
render: {
option: function(data, escape) {
return '<div>' + '<span class="title">' + escape(data.name) + '</span>' + '</div>';
plugins: plugins,
valueField: "value",
labelField: "name",
searchField: "name",
maxOptions: null,
placeholder: placeholder,
closeAfterSelect: true,
load: function (query, callback) {
var url;
if (send_data || !loaded) {
url = fill_path + "?course_id=" + courseId + "&q=" + encodeURIComponent(query);
fetch(url).then(function (response) {
return response.json();
}).then(function (json) {
loaded = true;
return callback(json.map(function (item) {
return {
name: item.text,
value: item.value,
};
}));
})["catch"](function () {
callback();
});
}
callback();
},
item: function(item, escape) {
return '<div title="' + escape(item.name) + '">' + escape(item.name) + '</div>';
render: {
option: function (data, escape) {
return "<div>" + '<span class="title">' + escape(data.name) + "</span>" + "</div>";
},
item: function (item, escape) {
return '<div title="' + escape(item.name) + '">' + escape(item.name) + "</div>";
},
no_results: function (data, escape) {
return '<div class="no-results">' + escape(no_result_msg) + "</div>";
},
},
no_results: function(data, escape) {
return '<div class="no-results">'+ escape(no_result_msg) + '</div>';
}
}
});
})();} else {
});
})();
}
else {
return new TomSelect("#" + this.id, {
plugins: plugins,
maxOptions: null
maxOptions: null,
});
}
});
};
}

$(document).on('turbolinks:before-cache', function() {
$('.tomselected').each(resetSelectized);
$(document).on("turbolinks:before-cache", function () {
$(".tomselected").each(resetSelectized);
});

$(document).on('turbolinks:load', function() {
fillOptionsByAjax($('.selectize'));
$(document).on("turbolinks:load", function () {
fillOptionsByAjax($(".selectize"));
});
26 changes: 13 additions & 13 deletions app/assets/javascripts/bootstrap_modal_turbolinks_fix.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
$(document).on('turbolinks:load', function () {
// show all active modals
$('.activeModal').modal('show');
// remove active status (this needs to be reestablished before caching)
$('.activeModal').removeClass('activeModal');
$(document).on("turbolinks:load", function () {
// show all active modals
$(".activeModal").modal("show");
// remove active status (this needs to be reestablished before caching)
$(".activeModal").removeClass("activeModal");
});

$(document).on('turbolinks:before-cache', function () {
// if some modal is open
if ($('body').hasClass('modal-open')) {
$('.modal.show').addClass('activeModal');
$('.modal.show').modal('hide');
// remove the greyed out background
$('.modal-backdrop').remove();
}
$(document).on("turbolinks:before-cache", function () {
// if some modal is open
if ($("body").hasClass("modal-open")) {
$(".modal.show").addClass("activeModal");
$(".modal.show").modal("hide");
// remove the greyed out background
$(".modal-backdrop").remove();
}
});
16 changes: 8 additions & 8 deletions app/assets/javascripts/bootstrap_popovers.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
$(document).on('turbolinks:load', function () {
initBootstrapPopovers();
$(document).on("turbolinks:load", function () {
initBootstrapPopovers();
});

/**
* Initializes all Bootstrap popovers on the page.
*
*
* This function might be used for the first initialization of popovers as well
* as for reinitialization on page changes.
*
* See: https://getbootstrap.com/docs/5.3/components/popovers/#enable-popovers
*/
function initBootstrapPopovers() {
const popoverHtmlElements = document.querySelectorAll('[data-bs-toggle="popover"]');
for (const element of popoverHtmlElements) {
new bootstrap.Popover(element);
}
}
const popoverHtmlElements = document.querySelectorAll('[data-bs-toggle="popover"]');
for (const element of popoverHtmlElements) {
new bootstrap.Popover(element);
}
}
6 changes: 4 additions & 2 deletions app/assets/javascripts/cable.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
// Action Cable provides the framework to deal with WebSockets in Rails.
// You can generate new channels where WebSocket features live using the `rails generate channel` command.
//
// disable eslint
/* eslint-disable */
//= require action_cable
//= require_self
//= require_tree ./channels
/* eslint-enable */

(function() {
(function () {
this.App || (this.App = {});

App.cable = ActionCable.createConsumer();

}).call(this);
Loading