Skip to content

Commit 1dd0ca7

Browse files
MiodecfehmergianpenaLeonabcd123
authored
refactor: begin transition away from jquery (@Miodec, @fehmer) (#7185)
Co-authored-by: Christian Fehmer <[email protected]> Co-authored-by: Gian Peña <[email protected]> Co-authored-by: Leonabcd123 <[email protected]>
1 parent 204fffe commit 1dd0ca7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1309
-599
lines changed

.github/pull_request_template.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
### Checks
66

7+
- [ ] Adding/modifying Typescript code?
8+
- [ ] I have used `qs`,`qsa` or `qsr` instead of JQuery selectors.
79
- [ ] Adding quotes?
810
- [ ] Make sure to include translations for the quotes in the description (or another comment) so we can verify their content.
911
- [ ] Adding a language?

docs/CONTRIBUTING_ADVANCED.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ If you are on a UNIX system and you get a spawn error, run npm with `sudo`.
155155

156156
Code formatting is enforced by [Prettier](https://prettier.io/docs/en/install.html), which automatically runs every time you make a commit.
157157

158+
We are currently in the process of converting from JQuery to vanilla JS. When submitting new code, please use the `qs`, `qsa` and `qsr` helper functions. These return a class with a lot of JQuery-like methods. You can read how they work and import them from `frontend/src/ts/utils/dom.ts`.
159+
158160
For guidelines on commit messages, adding themes, languages, or quotes, please refer to [CONTRIBUTING.md](./CONTRIBUTING.md). Following these guidelines will increase the chances of getting your change accepted.
159161

160162
## Questions

frontend/__tests__/setup-tests.ts

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,44 @@ vi.mock("../src/ts/firebase", () => ({
1818
isAuthenticated: () => false,
1919
}));
2020

21-
const input = document.createElement("input");
22-
input.id = "wordsInput";
23-
document.body.appendChild(input);
21+
vi.mock("../src/ts/utils/dom", () => {
22+
const createMockElement = (): any => {
23+
const mock = {
24+
qsr: vi.fn(),
25+
qs: vi.fn().mockReturnValue(null),
26+
find: vi.fn(),
27+
addClass: vi.fn(),
28+
removeClass: vi.fn(),
29+
hide: vi.fn(),
30+
show: vi.fn(),
31+
setText: vi.fn(),
32+
prependHtml: vi.fn(),
33+
empty: vi.fn(),
34+
appendHtml: vi.fn(),
35+
native: document.createElement("div"),
36+
};
37+
38+
// Make chainable methods return the mock itself
39+
mock.qsr.mockImplementation(() => createMockElement());
40+
mock.addClass.mockReturnValue(mock);
41+
mock.removeClass.mockReturnValue(mock);
42+
mock.hide.mockReturnValue(mock);
43+
mock.show.mockReturnValue(mock);
44+
mock.setText.mockReturnValue(mock);
45+
mock.prependHtml.mockReturnValue(mock);
46+
mock.empty.mockReturnValue(mock);
47+
48+
return mock;
49+
};
50+
51+
return {
52+
qsr: vi.fn().mockImplementation(() => createMockElement()),
53+
qs: vi.fn().mockImplementation(() => createMockElement()),
54+
qsa: vi.fn().mockReturnValue([]),
55+
};
56+
});
57+
58+
// Mock document.querySelector to return a div
59+
global.document.querySelector = vi
60+
.fn()
61+
.mockReturnValue(document.createElement("div"));

frontend/src/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<div id="backgroundLoader" class="hidden"></div>
1111
<div id="bannerCenter" class="focus"></div>
1212
<div id="notificationCenter">
13-
<div class="clearAll button invisible" style="display: none">
13+
<div class="clearAll button hidden">
1414
<i class="fas fa-times"></i>
1515
Clear all
1616
</div>

frontend/src/styles/notifications.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
.clearAll.button {
1414
font-size: 0.75em;
1515
margin-bottom: 1rem;
16+
overflow: hidden;
1617
}
1718
&.focus .clearAll {
1819
visibility: hidden;

frontend/src/ts/auth.ts

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { getActiveFunboxesWithFunction } from "./test/funbox/list";
3030
import * as Sentry from "./sentry";
3131
import { tryCatch } from "@monkeytype/util/trycatch";
3232
import * as AuthEvent from "./observables/auth-event";
33+
import { qs, qsa } from "./utils/dom";
3334

3435
export const gmailProvider = new GoogleAuthProvider();
3536
export const githubProvider = new GithubAuthProvider();
@@ -43,9 +44,9 @@ async function sendVerificationEmail(): Promise<void> {
4344
}
4445

4546
Loader.show();
46-
$(".sendVerificationEmail").prop("disabled", true);
47+
qs(".sendVerificationEmail")?.disable();
4748
const result = await Ape.users.verificationEmail();
48-
$(".sendVerificationEmail").prop("disabled", false);
49+
qs(".sendVerificationEmail")?.enable();
4950
if (result.status !== 200) {
5051
Loader.hide();
5152
Notifications.add(
@@ -100,7 +101,7 @@ async function getDataAndInit(): Promise<boolean> {
100101
} catch (error) {
101102
console.error(error);
102103
LoginPage.enableInputs();
103-
$("header nav .view-account").css("opacity", 1);
104+
qs("header nav .view-account")?.setStyle({ opacity: "1" });
104105
if (error instanceof DB.SnapshotInitError) {
105106
if (error.responseCode === 429) {
106107
Notifications.add(
@@ -215,9 +216,9 @@ export async function signIn(email: string, password: string): Promise<void> {
215216
return;
216217
}
217218

218-
const rememberMe = $(".pageLogin .login #rememberMe input").prop(
219-
"checked",
220-
) as boolean;
219+
const rememberMe =
220+
qs<HTMLInputElement>(".pageLogin .login #rememberMe input")?.isChecked() ??
221+
false;
221222

222223
const { error } = await tryCatch(
223224
signInWithEmailAndPassword(email, password, rememberMe),
@@ -249,9 +250,9 @@ async function signInWithProvider(provider: AuthProvider): Promise<void> {
249250
LoginPage.showPreloader();
250251
LoginPage.disableInputs();
251252
LoginPage.disableSignUpButton();
252-
const rememberMe = $(".pageLogin .login #rememberMe input").prop(
253-
"checked",
254-
) as boolean;
253+
const rememberMe =
254+
qs<HTMLInputElement>(".pageLogin .login #rememberMe input")?.isChecked() ??
255+
false;
255256

256257
const { error } = await tryCatch(signInWithPopup(provider, rememberMe));
257258

@@ -406,24 +407,24 @@ async function signUp(): Promise<void> {
406407
}
407408
}
408409

409-
$(".pageLogin .login form").on("submit", (e) => {
410+
qs(".pageLogin .login form")?.on("submit", (e) => {
410411
e.preventDefault();
411412
const email =
412-
($(".pageLogin .login input")[0] as HTMLInputElement).value ?? "";
413+
qsa<HTMLInputElement>(".pageLogin .login input")?.[0]?.getValue() ?? "";
413414
const password =
414-
($(".pageLogin .login input")[1] as HTMLInputElement).value ?? "";
415+
qsa<HTMLInputElement>(".pageLogin .login input")?.[1]?.getValue() ?? "";
415416
void signIn(email, password);
416417
});
417418

418-
$(".pageLogin .login button.signInWithGoogle").on("click", () => {
419+
qs(".pageLogin .login button.signInWithGoogle")?.on("click", () => {
419420
void signInWithGoogle();
420421
});
421422

422-
$(".pageLogin .login button.signInWithGitHub").on("click", () => {
423+
qs(".pageLogin .login button.signInWithGitHub")?.on("click", () => {
423424
void signInWithGitHub();
424425
});
425426

426-
$("nav .accountButtonAndMenu .menu button.signOut").on("click", () => {
427+
qs("nav .accountButtonAndMenu .menu button.signOut")?.on("click", () => {
427428
if (!isAuthAvailable()) {
428429
Notifications.add("Authentication uninitialized", -1, {
429430
duration: 3,
@@ -433,19 +434,20 @@ $("nav .accountButtonAndMenu .menu button.signOut").on("click", () => {
433434
signOut();
434435
});
435436

436-
$(".pageLogin .register form").on("submit", (e) => {
437+
qs(".pageLogin .register form")?.on("submit", (e) => {
437438
e.preventDefault();
438439
void signUp();
439440
});
440441

441-
$(".pageAccountSettings").on("click", "#addGoogleAuth", () => {
442+
qs(".pageAccountSettings")?.onChild("click", "#addGoogleAuth", () => {
442443
void addGoogleAuth();
443444
});
444-
$(".pageAccountSettings").on("click", "#addGithubAuth", () => {
445+
446+
qs(".pageAccountSettings")?.onChild("click", "#addGithubAuth", () => {
445447
void addGithubAuth();
446448
});
447449

448-
$(".pageAccount").on("click", ".sendVerificationEmail", () => {
450+
qs(".pageAccount")?.onChild("click", ".sendVerificationEmail", () => {
449451
if (!ConnectionState.get()) {
450452
Notifications.add("You are offline", 0, {
451453
duration: 2,

frontend/src/ts/commandline/commandline.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
ValidationResult,
2020
} from "../elements/input-validation";
2121
import { isInputElementFocused } from "../input/input-element";
22+
import { qs } from "../utils/dom";
2223

2324
type CommandlineMode = "search" | "input";
2425
type InputModeParams = {
@@ -55,14 +56,14 @@ let lastState:
5556
| undefined;
5657

5758
function removeCommandlineBackground(): void {
58-
$("#commandLine").addClass("noBackground");
59+
qs("#commandLine")?.addClass("noBackground");
5960
if (Config.showOutOfFocusWarning) {
6061
OutOfFocus.hide();
6162
}
6263
}
6364

6465
function addCommandlineBackground(): void {
65-
$("#commandLine").removeClass("noBackground");
66+
qs("#commandLine")?.removeClass("noBackground");
6667
if (Config.showOutOfFocusWarning && !isInputElementFocused()) {
6768
OutOfFocus.show();
6869
}

frontend/src/ts/controllers/ad-controller.ts

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import Config from "../config";
77
import * as TestState from "../test/test-state";
88
import * as EG from "./eg-ad-controller";
99
import * as PW from "./pw-ad-controller";
10+
import { onDocumentReady, qs } from "../utils/dom";
1011

1112
const breakpoint = 900;
1213
let widerThanBreakpoint = true;
@@ -61,32 +62,32 @@ function removeAll(): void {
6162
}
6263

6364
function removeSellout(): void {
64-
$("#ad-footer-wrapper").remove();
65-
$("#ad-footer-small-wrapper").remove();
66-
$("#ad-about-1-wrapper").remove();
67-
$("#ad-about-1-small-wrapper").remove();
68-
$("#ad-about-2-wrapper").remove();
69-
$("#ad-about-2-small-wrapper").remove();
70-
$("#ad-settings-1-wrapper").remove();
71-
$("#ad-settings-1-small-wrapper").remove();
72-
$("#ad-settings-2-wrapper").remove();
73-
$("#ad-settings-2-small-wrapper").remove();
74-
$("#ad-settings-3-wrapper").remove();
75-
$("#ad-settings-3-small-wrapper").remove();
76-
$("#ad-account-1-wrapper").remove();
77-
$("#ad-account-1-small-wrapper").remove();
78-
$("#ad-account-2-wrapper").remove();
79-
$("#ad-account-2-small-wrapper").remove();
65+
qs("#ad-footer-wrapper")?.remove();
66+
qs("#ad-footer-small-wrapper")?.remove();
67+
qs("#ad-about-1-wrapper")?.remove();
68+
qs("#ad-about-1-small-wrapper")?.remove();
69+
qs("#ad-about-2-wrapper")?.remove();
70+
qs("#ad-about-2-small-wrapper")?.remove();
71+
qs("#ad-settings-1-wrapper")?.remove();
72+
qs("#ad-settings-1-small-wrapper")?.remove();
73+
qs("#ad-settings-2-wrapper")?.remove();
74+
qs("#ad-settings-2-small-wrapper")?.remove();
75+
qs("#ad-settings-3-wrapper")?.remove();
76+
qs("#ad-settings-3-small-wrapper")?.remove();
77+
qs("#ad-account-1-wrapper")?.remove();
78+
qs("#ad-account-1-small-wrapper")?.remove();
79+
qs("#ad-account-2-wrapper")?.remove();
80+
qs("#ad-account-2-small-wrapper")?.remove();
8081
}
8182

8283
function removeOn(): void {
83-
$("#ad-vertical-right-wrapper").remove();
84-
$("#ad-vertical-left-wrapper").remove();
84+
qs("#ad-vertical-right-wrapper")?.remove();
85+
qs("#ad-vertical-left-wrapper")?.remove();
8586
}
8687

8788
function removeResult(): void {
88-
$("#ad-result-wrapper").remove();
89-
$("#ad-result-small-wrapper").remove();
89+
qs("#ad-result-wrapper")?.remove();
90+
qs("#ad-result-small-wrapper")?.remove();
9091
}
9192

9293
function updateVerticalMargin(): void {
@@ -219,7 +220,7 @@ export async function renderResult(): Promise<void> {
219220
await checkCookieblocker();
220221

221222
if (adBlock) {
222-
$("#ad-result-wrapper .iconAndText .text").html(`
223+
qs("#ad-result-wrapper .iconAndText .text")?.setHtml(`
223224
Using an ad blocker? No worries
224225
<div class="smalltext">
225226
We understand ads can be annoying
@@ -233,7 +234,7 @@ export async function renderResult(): Promise<void> {
233234
}
234235

235236
if (cookieBlocker) {
236-
$("#ad-result-wrapper .iconAndText .text").html(`
237+
qs("#ad-result-wrapper .iconAndText .text")?.setHtml(`
237238
Ads not working? Ooops
238239
<div class="smalltext">
239240
You may have a cookie popup blocker enabled - ads will not show without your consent
@@ -255,15 +256,15 @@ export async function renderResult(): Promise<void> {
255256

256257
export function updateFooterAndVerticalAds(visible: boolean): void {
257258
if (visible) {
258-
$("#ad-vertical-left-wrapper").removeClass("testPage");
259-
$("#ad-vertical-right-wrapper").removeClass("testPage");
260-
$("#ad-footer-wrapper").removeClass("testPage");
261-
$("#ad-footer-small-wrapper").removeClass("testPage");
259+
qs("#ad-vertical-left-wrapper")?.removeClass("testPage");
260+
qs("#ad-vertical-right-wrapper")?.removeClass("testPage");
261+
qs("#ad-footer-wrapper")?.removeClass("testPage");
262+
qs("#ad-footer-small-wrapper")?.removeClass("testPage");
262263
} else {
263-
$("#ad-vertical-left-wrapper").addClass("testPage");
264-
$("#ad-vertical-right-wrapper").addClass("testPage");
265-
$("#ad-footer-wrapper").addClass("testPage");
266-
$("#ad-footer-small-wrapper").addClass("testPage");
264+
qs("#ad-vertical-left-wrapper")?.addClass("testPage");
265+
qs("#ad-vertical-right-wrapper")?.addClass("testPage");
266+
qs("#ad-footer-wrapper")?.addClass("testPage");
267+
qs("#ad-footer-small-wrapper")?.addClass("testPage");
267268
}
268269
}
269270

@@ -293,7 +294,7 @@ const debouncedMarginUpdate = debounce(500, updateVerticalMargin);
293294
const debouncedBreakpointUpdate = debounce(500, updateBreakpoint);
294295
const debouncedBreakpoint2Update = debounce(500, updateBreakpoint2);
295296

296-
$(window).on("resize", () => {
297+
window.addEventListener("resize", () => {
297298
debouncedMarginUpdate();
298299
debouncedBreakpointUpdate();
299300
debouncedBreakpoint2Update();
@@ -316,7 +317,7 @@ BannerEvent.subscribe(() => {
316317
updateVerticalMargin();
317318
});
318319

319-
$(() => {
320+
onDocumentReady(() => {
320321
updateBreakpoint(true);
321322
updateBreakpoint2();
322323
});
@@ -325,7 +326,7 @@ window.onerror = function (error): void {
325326
//@ts-expect-error ---
326327
if (choice === "eg") {
327328
if (typeof error === "string" && error.startsWith("EG APS")) {
328-
$("#ad-result-wrapper .iconAndText").addClass("withLeft");
329+
qs("#ad-result-wrapper .iconAndText")?.addClass("withLeft");
329330
}
330331
}
331332
};

frontend/src/ts/controllers/analytics-controller.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
} from "firebase/analytics";
66
import { getAnalytics } from "../firebase";
77
import { createErrorMessage } from "../utils/misc";
8+
import { qs } from "../utils/dom";
89

910
let analytics: AnalyticsType;
1011

@@ -28,7 +29,7 @@ export function activateAnalytics(): void {
2829
try {
2930
analytics = getAnalytics();
3031
setAnalyticsCollectionEnabled(analytics, true);
31-
$("body").append(`
32+
qs("body")?.appendHtml(`
3233
<script
3334
async
3435
src="https://www.googletagmanager.com/gtag/js?id=UA-165993088-1"

0 commit comments

Comments
 (0)