Skip to content

Conversation

@fcasibu
Copy link

@fcasibu fcasibu commented Dec 25, 2025

Description

Refactor frontend/ts/modals/* to replace jQuery (some still TODO). I've also modified dom.ts to add setValue for ElementsWithUtils<ElementWithValue> instance. Still trying to grep the whole flow/codebase, so usage of qsr or qs is uncertain (Probably use qsr always? Can find non-existing elements that way or developer error). There are a lot of atomic commits in this PR, feel free to squash.

While doing the refactor, also stumbled upon things that I have questions for (didn't made any changes to them), which I thought can be improved:

  • custom-generator.ts:146: The user is able to write minLength > maxLength. Is this behavior correct?
  • custom-test-duration.ts:99: It seems like parseInput, always expects a non nullable string, and we seem to always expect that #customTestDurationModal input to always exist (so I used qsr), which makes the conditions val !== null, !isNaN(val) seem to be unnecessary (val is never null or NaN, tbh also isFinite, and val >= 0 is the only valid condition)
  • custom-text.ts:169: These elements does not seem exist .randomWordsCheckbox, .replaceNewlineWithSpace, .typographyCheck, and .delimiterCheck (last two is just in a challenge file). Are they good to remove?
  • edit-preset.ts:146: Noticed that we're not updaitng the DOM, since for the most part, in the logic, we mostly use state.checkboxes, so no problem happens, but I think it is also a good idea to update the DOM to match current state? Either calling updateUI() here or just changing the checked value inline.
  • quote-rate.ts:89: getQuoteStats expects a quote but we're not really passing anything to it, so this essentially does nothing, since it returns immediately on !quote

Checks

  • Adding/modifying Typescript code?
    • I have used qs, qsa or qsr instead of JQuery selectors.
  • Check if any open issues are related to this PR; if so, be sure to tag them below.
  • Make sure the PR title follows the Conventional Commits standard. (https://www.conventionalcommits.org for more info)
  • Make sure to include your GitHub username prefixed with @ inside parentheses at the end of the PR title.

Related #7186

#7186

@monkeytypegeorge monkeytypegeorge added the frontend User interface or web stuff label Dec 25, 2025
@fcasibu fcasibu force-pushed the refactor/ts-modals-remove-jquery branch from e92798c to d0997c2 Compare December 25, 2025 12:18
const action = qsr("#editPresetModal .modal").getAttribute("data-action");
const propPresetName = qsa<HTMLInputElement>(
"#editPresetModal .modal input",
)[0]?.getValue() as string;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can likely just query for a specific selector, but uncertain. Same for line 237

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, query the specific one instead.

@fcasibu fcasibu force-pushed the refactor/ts-modals-remove-jquery branch 2 times, most recently from c45226a to 83a24cb Compare December 26, 2025 12:50
@fcasibu fcasibu marked this pull request as ready for review December 26, 2025 12:52
@github-actions github-actions bot added the waiting for review Pull requests that require a review before continuing label Dec 26, 2025
@fcasibu fcasibu force-pushed the refactor/ts-modals-remove-jquery branch from 83a24cb to e2f2cc7 Compare December 26, 2025 13:19
@fcasibu fcasibu force-pushed the refactor/ts-modals-remove-jquery branch from e2f2cc7 to 937446c Compare December 30, 2025 07:36
@Miodec Miodec requested a review from Copilot January 6, 2026 14:33
@github-actions github-actions bot removed the waiting for review Pull requests that require a review before continuing label Jan 6, 2026
Copy link
Member

@Miodec Miodec left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In files that use AnimatedModal, (for example in edit-preset) instead of constantly querying for "#editPresetModal .modal, i think i would prefer using modal.getModal().

So:
qsr("#editPresetModal .modal").setAttribute("data-action", "edit");
becomes:
modal.getModal().setAttribute("data-action", "edit");

@github-actions github-actions bot added the waiting for update Pull requests or issues that require changes/comments before continuing label Jan 6, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors modal files in frontend/ts/modals/* to remove jQuery dependencies and replace them with custom DOM utility methods. The changes add a setValue method for ElementsWithUtils<ElementWithValue> instances and modify toggleClass to accept an optional force parameter.

Key changes:

  • Replaced jQuery selectors ($()) with custom DOM utilities (qs, qsr, qsa)
  • Replaced jQuery methods like .val(), .prop(), .attr() with custom wrappers (.getValue(), .setValue(), .isChecked(), .setAttribute(), etc.)
  • Added event delegation support using .onChild() for dynamically generated content

Reviewed changes

Copilot reviewed 25 out of 25 changed files in this pull request and generated 21 comments.

Show a summary per file
File Description
frontend/src/ts/utils/dom.ts Added setValue method to ElementsWithUtils class and optional force parameter to toggleClass
frontend/src/ts/modals/word-filter.ts Removed jQuery selectors and methods, replaced with custom DOM utilities
frontend/src/ts/modals/version-history.ts Replaced jQuery with qsr() for modal content manipulation
frontend/src/ts/modals/user-report.ts Converted jQuery selectors to qsr() for form input retrieval
frontend/src/ts/modals/simple-modals.ts Refactored event handlers from jQuery delegation to .onChild() pattern
frontend/src/ts/modals/share-test-settings.ts Replaced jQuery with custom DOM utilities for checkbox and input handling
frontend/src/ts/modals/share-custom-theme.ts Converted jQuery color input retrieval to qsr().getValue()
frontend/src/ts/modals/saved-texts.ts Replaced jQuery with qs, qsr for list manipulation and event handling
frontend/src/ts/modals/save-custom-text.ts Converted checkbox manipulation from jQuery to custom utilities
frontend/src/ts/modals/quote-submit.ts Replaced jQuery form input retrieval with qsr().getValue()
frontend/src/ts/modals/quote-search.ts Refactored dropdown handling and result list manipulation
frontend/src/ts/modals/quote-report.ts Converted form input retrieval to custom DOM utilities
frontend/src/ts/modals/quote-rate.ts Replaced jQuery text/star manipulation with qsr().setText()
frontend/src/ts/modals/quote-approve.ts Major refactor using createElementWithUtils and custom event handling
frontend/src/ts/modals/practise-words.ts Converted button state management to custom utilities
frontend/src/ts/modals/mobile-test-config.ts Replaced jQuery mode/config button handling
frontend/src/ts/modals/last-signed-out-result.ts Converted result display to custom DOM utilities
frontend/src/ts/modals/google-sign-up.ts Replaced jQuery input retrieval and button state management
frontend/src/ts/modals/edit-result-tags.ts Converted tag button iteration to qsa()
frontend/src/ts/modals/edit-profile.ts Refactored badge selection event handling
frontend/src/ts/modals/edit-preset.ts Complex refactor of preset checkbox and input management
frontend/src/ts/modals/dev-options.ts Replaced jQuery .prepend() with .prependHtml()
frontend/src/ts/modals/custom-text.ts Major refactor of custom text UI state management
frontend/src/ts/modals/custom-test-duration.ts Converted input parsing to custom utilities
frontend/src/ts/modals/custom-generator.ts Replaced jQuery input retrieval with qs().getValue()
Comments suppressed due to low confidence (1)

frontend/src/ts/modals/saved-texts.ts:109

  • Event delegation issue. Same problem - event handlers for dynamically generated content need event delegation.
  qs("#savedTextsModal .list .savedText .button.name")?.on("click", (e) => {
    const name = (e.target as HTMLElement).textContent;
    CustomTextState.setCustomTextName(name, false);
    const text = getSavedText(name, false);
    hide({ modalChainData: { text, long: false } });
  });

  qs("#savedTextsModal .listLong .savedLongText .button.name")?.on(
    "click",
    (e) => {
      const name = (e.target as HTMLElement)?.textContent;
      CustomTextState.setCustomTextName(name, true);
      const text = getSavedText(name, true);
      hide({ modalChainData: { text, long: true } });
    },

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

$("wordFilterModal .presetInput").empty();
qsr("#wordFilterModal .languageInput").empty();
qsr("#wordFilterModal .layoutInput").empty();
qsr("wordFilterModal .presetInput").empty();
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing '#' selector prefix. The selector should be "#wordFilterModal .presetInput" instead of "wordFilterModal .presetInput".

Suggested change
qsr("wordFilterModal .presetInput").empty();
qsr("#wordFilterModal .presetInput").empty();

Copilot uses AI. Check for mistakes.
$("#wordFilterModal button.addButton").on("click", () => {
$("#wordFilterModal .loadingIndicator").removeClass("hidden");
qsr("#wordFilterModal button.addButton").on("click", () => {
qsr("#wordFilterModal .loadingIndicator").show();
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent use of removeClass("hidden") versus show(). Line 315 uses show() while line 139 uses removeClass("hidden"). These should be consistent - either always use show()/hide() or removeClass("hidden")/addClass("hidden").

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +19
qsr("#versionHistoryModal .modal").setHtml(
`<div class="releases"></div`,
);
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The setHtml call is missing a closing tag. The HTML string "<div class="releases"></div should be "<div class="releases"></div>" with a closing angle bracket.

Copilot uses AI. Check for mistakes.
Comment on lines +53 to 55
qsr("#versionHistoryModal .modal").setHtml(
`<div class="releases">${msg}</div`,
);
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The setHtml call is missing a closing tag. The HTML string "<div class="releases">${msg}</div should be "<div class="releases">${msg}</div>" with a closing angle bracket.

Copilot uses AI. Check for mistakes.
Comment on lines +247 to +248
console.log({ updateConfig, propPresetName });

Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debugging console.log statement should be removed before merging to production.

Suggested change
console.log({ updateConfig, propPresetName });

Copilot uses AI. Check for mistakes.
$("#savedTextsModal .list .savedText .button.name").on("click", (e) => {
const name = $(e.target).text();
qs("#savedTextsModal .list .savedText .button.name")?.on("click", (e) => {
const name = (e.target as HTMLElement).textContent;
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential issue with textContent returning null. The textContent property can return null if the element has no text. This should be handled with a null check or use a fallback value to prevent passing null to setCustomTextName.

Copilot uses AI. Check for mistakes.
"click",
(e) => {
const name = $(e.target).text();
const name = (e.target as HTMLElement)?.textContent;
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential issue with textContent returning null. The textContent property can return null if the element has no text. This should be handled with a null check or use a fallback value to prevent passing null to setCustomTextName.

Copilot uses AI. Check for mistakes.
Comment on lines +183 to +191
if (state.checkboxes.get(settingGroup)) {
qsr(
`#editPresetModal .modal .checkboxList .checkboxTitlePair[data-id="${settingGroup}"] input`,
).setAttribute("checked", "true");
} else {
qsr(
`#editPresetModal .modal .checkboxList .checkboxTitlePair[data-id="${settingGroup}"] input`,
).removeAttribute("checked");
}
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect implementation for setting checkbox checked state. Using setAttribute/removeAttribute for the "checked" attribute only affects the initial state in the HTML, not the actual checked property. This should use a method that sets the checked property (e.g., .setChecked()) or directly manipulate .native.checked to properly control checkbox state.

Copilot uses AI. Check for mistakes.
$("#saveCustomTextModal .isLongText").prop("checked", false);
$("#saveCustomTextModal button.save").prop("disabled", true);
qsr<HTMLInputElement>("#saveCustomTextModal .textName").setValue("");
qsr("#saveCustomTextModal .isLongText").removeAttribute("checked");
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using removeAttribute("checked") to uncheck a checkbox is incorrect. This only removes the HTML attribute but doesn't change the actual checked state of the checkbox. Use a method like .setChecked(false) or directly set .native.checked = false.

Suggested change
qsr("#saveCustomTextModal .isLongText").removeAttribute("checked");
qsr<HTMLInputElement>("#saveCustomTextModal .isLongText").setChecked(false);

Copilot uses AI. Check for mistakes.
Comment on lines +83 to +87
qsa(`${popup} .inputs .group[data-id="limit"] input.sections`)?.show();
qsa(`${popup} .inputs .group[data-id="limit"] input.words`)?.hide();
} else {
$(`${popup} .inputs .group[data-id="limit"] input.words`).removeClass(
"hidden",
);
$(`${popup} .inputs .group[data-id="limit"] input.sections`).addClass(
"hidden",
);
qsa(`${popup} .inputs .group[data-id="limit"] input.words`)?.show();
qsa(`${popup} .inputs .group[data-id="limit"] input.sections`)?.hide();
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect use of qsa on elements. Lines 83-84 and 86-87 use qsa() which returns multiple elements, but then call show()/hide() directly on the result. Since these selectors likely match multiple elements, this is correct usage. However, the inconsistency with lines 63-64 which use qs() for .group elements should be reviewed - if multiple .section elements exist, qsa should be used consistently.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

frontend User interface or web stuff waiting for update Pull requests or issues that require changes/comments before continuing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants