From 38357dd0a97443007d5761b07bc909e859245685 Mon Sep 17 00:00:00 2001 From: Dave Roberts Date: Fri, 11 Sep 2026 11:42:38 +0100 Subject: [PATCH 1/4] Basic lib definitions - initial set --- src/frontend/js/lib/component.d.ts | 60 +++++++++++++++++++++++ src/frontend/js/lib/component.js | 13 +++-- src/frontend/js/lib/get-field-values.d.ts | 8 +++ src/frontend/js/lib/logging.d.ts | 11 +++++ src/frontend/js/lib/set-field-values.d.ts | 6 +++ src/frontend/js/lib/validation.d.ts | 43 ++++++++++++++++ 6 files changed, 134 insertions(+), 7 deletions(-) create mode 100644 src/frontend/js/lib/component.d.ts create mode 100644 src/frontend/js/lib/get-field-values.d.ts create mode 100644 src/frontend/js/lib/logging.d.ts create mode 100644 src/frontend/js/lib/set-field-values.d.ts create mode 100644 src/frontend/js/lib/validation.d.ts diff --git a/src/frontend/js/lib/component.d.ts b/src/frontend/js/lib/component.d.ts new file mode 100644 index 000000000..bbe690dd7 --- /dev/null +++ b/src/frontend/js/lib/component.d.ts @@ -0,0 +1,60 @@ +declare module "component" { + /** + * Default component class. + * Components should inherit this class. + */ + abstract class Component { + /** + * Whether a component can be reinitialized on an element. For legacy + * reasons, the default is not to be and initialization will only be run + * once. For components that set this to true, they must cleanly handle + * such a reinitialization (returning the object but not resetting up HTML + * elements etc) + * @returns True if the component can be reinitialized, false otherwise + */ + static get allowReinitialization(): boolean; + + /** + * The HTML element the component is associated with. + */ + readonly element: HTMLElement; + + /** + * Create a new component instance. + * @param element The element to initialize the component on + * @throws If the element is not an HTMLElement + */ + constructor(element: HTMLElement); + } + + /** + * Register a component that can be initialized + * @param componentInitializer Function that will be called when component initializes + */ + const registerComponent: (componentInitializer: Function) => void; + + /** + * Initialize all registered components in the defined scope + * @param scope The scope to initialize the components in (either JQuery elements or DOM). + */ + const initializeRegisteredComponents: (scope: HTMLElement | JQuery) => void; + + /** + * Get an Array of elements matching `selector` within `scope` + * @param scope The scope to select elements + * @param selector The selector to select elements + * @returns An array of elements + */ + const getComponentElements: (scope: HTMLElement, selector: string) => HTMLElement[]; + + /** + * Initialize component `Component` on all elements matching `selector` within `scope` + * Will only initialize elements that have not been initialized. + * @param scope The scope to initialize the objects on + * @param selector The selector to select elements + * @param componentClass The Component class to initialize + * @returns An array of initialized components + */ + const initializeComponent: (scope: HTMLElement, selector: string | Function, componentClass: T) => T[]; + +} diff --git a/src/frontend/js/lib/component.js b/src/frontend/js/lib/component.js index 1ef02fd87..b0ab0f5eb 100644 --- a/src/frontend/js/lib/component.js +++ b/src/frontend/js/lib/component.js @@ -102,11 +102,11 @@ const getComponentElements = (scope, selector) => { * Will only initialize elements that have not been initialized. * @param {HTMLElement} scope The scope to initialize the objects on * @param {string|Function} selector The selector to select elements - * @param {Component} ComponentClass The Component class to initialize + * @param {Component} componentClass The Component class to initialize * @returns {Array[Component]} An array of initialized components */ -const initializeComponent = (scope, selector, ComponentClass) => { - if (!(ComponentClass.prototype instanceof Component)) { +const initializeComponent = (scope, selector, componentClass) => { + if (!(componentClass.prototype instanceof Component)) { throw new Error( "Components can only be initialized when they inherit the basecomponent" ); @@ -125,11 +125,10 @@ const initializeComponent = (scope, selector, ComponentClass) => { return elements .filter((el) => { return ( - ComponentClass.allowReinitialization + componentClass.allowReinitialization // See comments for allowReinitialization() - || !componentIsInitialized(el, ComponentClass.name) - ); - }).map((el) => new ComponentClass(el)); + || !componentIsInitialized(el, componentClass.name)); + }).map((el) => new componentClass(el)); }; export { diff --git a/src/frontend/js/lib/get-field-values.d.ts b/src/frontend/js/lib/get-field-values.d.ts new file mode 100644 index 000000000..f42ee903b --- /dev/null +++ b/src/frontend/js/lib/get-field-values.d.ts @@ -0,0 +1,8 @@ +/** + * get the value from a field, depending on its type + * @param $depends - The jQuery object representing the field + * @param filtered - Whether the field is filtered + * @param for_code - Whether to return the value for code generation + * @param for_autosave - Whether to return the value for autosave + * @returns The value(s) of the field, formatted according to the field type + */declare const getFieldValues: ($depends: JQuery, filtered: boolean, for_code: boolean, for_autosave: boolean) => string | Array | object diff --git a/src/frontend/js/lib/logging.d.ts b/src/frontend/js/lib/logging.d.ts new file mode 100644 index 000000000..04412307f --- /dev/null +++ b/src/frontend/js/lib/logging.d.ts @@ -0,0 +1,11 @@ +declare module "logging" { + export interface Logging { + log(...message: unknown[]): void; + info(...message: unknown[]): void; + warn(...message: unknown[]): void; + error(...message: unknown[]): void; + formatMessage(type: string, ...message: unknown[]): string; + } + + export const logging: Logging; +} diff --git a/src/frontend/js/lib/set-field-values.d.ts b/src/frontend/js/lib/set-field-values.d.ts new file mode 100644 index 000000000..a980b6082 --- /dev/null +++ b/src/frontend/js/lib/set-field-values.d.ts @@ -0,0 +1,6 @@ +/** + * Set the value of a field based on its type. + * @param $field The field to set values for + * @param values The values to set for the field + */ +declare const setFieldValues: ($field: JQuery, value: (string | object)[]) => void; diff --git a/src/frontend/js/lib/validation.d.ts b/src/frontend/js/lib/validation.d.ts new file mode 100644 index 000000000..56f16c46f --- /dev/null +++ b/src/frontend/js/lib/validation.d.ts @@ -0,0 +1,43 @@ +declare module "validation" { + /** + * Bind events to a field to trigger validation + * @param field - The field to bind validation events to + */ + export const initValidationOnField: (field: JQuery) => void; + + /** + * Validate tree + * @param field The field to validate + * @returns Returns true if the tree is valid, false otherwise + */ + export const validateTree: (field: JQuery) => boolean; + + /** + * Validate radio-group + * @param field The field to validate + * @returns Returns true if the radio group is valid, false otherwise + */ + export const validateRadioGroup: (field: JQuery) => boolean; + + /** + * Validate checkbox group + * @param field The field to validate + * @returns Returns true if at least one checkbox is checked, false otherwise + */ + export const validateCheckboxGroup: (field: JQuery) => boolean; + + + /** + * Validate the required fields of a form + * @param form The form to validate + * @returns Returns true if the form is valid, false otherwise + */ + export const validateRequiredFields: (form: JQuery) => boolean; + + /** + * Validate query builder inputs + * @param field The querybuilder to validate + * @returns False if validation fails, otherwise true on success + */ + export const validateQueryBuilder: (field: JQuery) => boolean; +} From 3703d1e9dad2b1f138f0629ab3d38b3b0f8eaf23 Mon Sep 17 00:00:00 2001 From: Dave Roberts Date: Tue, 15 Sep 2026 11:19:41 +0000 Subject: [PATCH 2/4] Update source files --- .../components/button/lib/component.ts | 2 +- .../components/button/lib/delete-button.ts | 5 ++-- .../components/button/lib/rename-button.ts | 7 +++--- .../components/button/lib/save-view-button.ts | 4 ++-- .../button/lib/submit-field-button.ts | 3 ++- .../components/dashboard/lib/component.tsx | 4 ++-- .../components/dashboard/lib/react/App.tsx | 24 +++++++++---------- .../lib/react/Dashboard/DashboardView.tsx | 2 +- .../lib/react/EditModal/EditModal.test.tsx | 14 +++++------ .../lib/react/EditModal/EditModal.tsx | 2 +- .../dashboard/lib/react/Footer.test.tsx | 1 + .../dashboard/lib/react/api/index.ts | 5 ++-- .../form-group/input/lib/documentComponent.ts | 23 +++++++++--------- .../searchable-select/FilterSelectHelper.ts | 21 ++++++---------- .../JQuerySearchableSelect.ts | 2 +- .../searchable-select/lib/SearchableSelect.ts | 4 ++-- .../components/help-view/lib/component.ts | 2 +- .../lib/util/errorHandler/lib/errorHandler.ts | 2 +- src/frontend/js/lib/util/filedrag/index.ts | 14 ++++++----- .../js/lib/util/filedrag/lib/filedrag.ts | 20 +++++++++------- .../lib/util/gadsStorage/lib/GadsStorage.ts | 3 ++- src/frontend/js/lib/util/mapper/mapper.ts | 2 +- .../js/lib/util/scriptErrorHandler/index.ts | 12 ++++++---- 23 files changed, 93 insertions(+), 85 deletions(-) diff --git a/src/frontend/components/button/lib/component.ts b/src/frontend/components/button/lib/component.ts index 1a47f6ce8..93d852185 100644 --- a/src/frontend/components/button/lib/component.ts +++ b/src/frontend/components/button/lib/component.ts @@ -125,7 +125,7 @@ class ButtonComponent extends Component { if (!this.buttonsMap) throw "Buttons map is not initialized"; if (!this.buttonsMap.has(className)) return; this.linkedClasses.push(className); - this.buttonsMap.get(className)(el); + this.buttonsMap.get(className)?.(el); }); } } diff --git a/src/frontend/components/button/lib/delete-button.ts b/src/frontend/components/button/lib/delete-button.ts index bad6d0f5c..dd3a4202c 100644 --- a/src/frontend/components/button/lib/delete-button.ts +++ b/src/frontend/components/button/lib/delete-button.ts @@ -25,10 +25,11 @@ export default function createDeleteButton(element: JQuery) { element.on("click", function (e: JQuery.ClickEvent) { e.stopPropagation(); }); - if (window.test) throw e; + // TS6 is a bit odd - it recognises window.test as a function and wants to call it so we resort to typechecks + if (typeof window.test !== "undefined") throw e; } $deleteModal.find(".modal-title").text(modalTitle); - $deleteModal.find("button[type=submit]").val(id); + $deleteModal.find("button[type=submit]").val(id!); }); } diff --git a/src/frontend/components/button/lib/rename-button.ts b/src/frontend/components/button/lib/rename-button.ts index 2b4fdd988..c365b27da 100644 --- a/src/frontend/components/button/lib/rename-button.ts +++ b/src/frontend/components/button/lib/rename-button.ts @@ -39,7 +39,7 @@ declare global { */ class RenameButton { private readonly dataClass = "rename-button"; - private value: string; + private value?: string; /** * Attach event to button @@ -162,7 +162,7 @@ class RenameButton { private triggerRename(id: number, button: JQuery) { const previousValue = $(`#current-${id}`).text(); const extension = "." + previousValue.split(".").pop(); - const newName = this.value.endsWith(extension) ? this.value : this.value + extension; + const newName = this.value?.endsWith(extension) ? this.value : this.value + extension; if (newName === "" || newName === previousValue) return; $(`#current-${id}`).text(newName); const event = $.Event("rename", { oldName: previousValue, newName, target: button }); @@ -198,7 +198,8 @@ class RenameButton { if (typeof jQuery !== "undefined") { (function ($) { $.fn.renameButton = function () { - return this.each(function (_: unknown, el: HTMLButtonElement) { + return this.each(function (_: unknown, el: HTMLElement) { + if (!(el instanceof HTMLButtonElement)) return; new RenameButton(el); }); }; diff --git a/src/frontend/components/button/lib/save-view-button.ts b/src/frontend/components/button/lib/save-view-button.ts index 38f2c00b0..a687b76b4 100644 --- a/src/frontend/components/button/lib/save-view-button.ts +++ b/src/frontend/components/button/lib/save-view-button.ts @@ -13,10 +13,10 @@ export default function createSaveViewButtonComponent(el: JQuery) { const $input = $form.find("input[type=hidden][name=group_id]"); if ((ev.target as HTMLInputElement)?.checked) { $input.attr("required", "required"); - if ($dropdown && $dropdown.attr && $dropdown.attr("placeholder") && $dropdown.attr("placeholder").match(/All [Uu]sers/)) $dropdown.addClass("select--required"); + if ($dropdown?.attr("placeholder")?.match(/All [Uu]sers/)) $dropdown.addClass("select--required"); } else { $input.removeAttr("required"); - if ($dropdown && $dropdown.attr && $dropdown.attr("placeholder") && $dropdown.attr("placeholder").match(/All [Uu]sers/)) $dropdown.removeClass("select--required"); + if ($dropdown?.attr("placeholder")?.match(/All [Uu]sers/)) $dropdown.removeClass("select--required"); } }); el.on("click", (ev) => { diff --git a/src/frontend/components/button/lib/submit-field-button.ts b/src/frontend/components/button/lib/submit-field-button.ts index 75ab86623..46bf3a4f1 100644 --- a/src/frontend/components/button/lib/submit-field-button.ts +++ b/src/frontend/components/button/lib/submit-field-button.ts @@ -131,7 +131,8 @@ export default class SubmitFieldButton { * @returns {string} The URL for the tree API */ private getURL(data: JQuery.PlainObject): string { - if (window.test) return ""; + // TS6 is a bit odd - it recognises window.test as a function and wants to call it so we resort to typechecks + if (typeof window.test !== "undefined") return ""; const devEndpoint = window.siteConfig && window.siteConfig.urls.treeApi; diff --git a/src/frontend/components/dashboard/lib/component.tsx b/src/frontend/components/dashboard/lib/component.tsx index 3cec7abff..2b503fc87 100644 --- a/src/frontend/components/dashboard/lib/component.tsx +++ b/src/frontend/components/dashboard/lib/component.tsx @@ -39,7 +39,7 @@ export default class DashboardComponent extends Component { const widgetsEls = Array.prototype.slice.call(document.querySelectorAll("#ld-app > div")); const widgets = widgetsEls.map((el: HTMLElement) => ({ html: el.innerHTML, - config: JSON.parse(el.getAttribute("data-grid")) + config: JSON.parse(el.getAttribute("data-grid")!) })); const api = new ApiClient(this.element.getAttribute("data-dashboard-endpoint") || ""); @@ -48,7 +48,7 @@ export default class DashboardComponent extends Component { root.render( { + const showEditForm = (id: string) => { setEditModalOpen(true); setLoadingEditHtml(true); setActiveItem(id); @@ -133,16 +133,16 @@ export default function App(props: AppProps): React.JSX.Element { */ const saveActiveWidget = async (event: any) => { event.preventDefault(); - const formEl = formRef.current.querySelector("form"); + const formEl = formRef.current!.querySelector("form"); if (!formEl) { console.error("No form element was found!"); return; } const form = serialize(formEl, { hash: true }); - const result = await props.api.saveWidget(formEl.getAttribute("action"), form); + const result = await props.api.saveWidget(formEl.getAttribute("action")!, form); if (result.is_error) { - setEditError(result.message); + setEditError(result.message!); return; } updateWidgetHtml(activeItem); @@ -181,7 +181,7 @@ export default function App(props: AppProps): React.JSX.Element { let x = 0; let y = 0; while (isGridConflict(x, y, w, h)) { - if ((x + w) < props.gridConfig.cols) { + if ((x + w) < props.gridConfig.cols!) { x += 1; } else { y += 1; @@ -203,7 +203,7 @@ export default function App(props: AppProps): React.JSX.Element { alert(result.message); return; } - const id = result.message; + const id = result.message!; const { x, y } = firstAvailableSpot(1, 1); const widgetLayout = { i: id, @@ -287,7 +287,7 @@ export default function App(props: AppProps): React.JSX.Element { * Initialize the Summernote component if it exists in the form */ const initializeSummernoteComponent = () => { - const summernoteEl = formRef.current.querySelector(".summernote"); + const summernoteEl = formRef.current!.querySelector(".summernote"); if (summernoteEl) { import(/* WebpackChunkName: "summernote" */ "../../../summernote/lib/component") .then(({ default: SummerNoteComponent }) => { @@ -311,7 +311,7 @@ export default function App(props: AppProps): React.JSX.Element { return (
{props.hideMenu ||
} loadingEditHtml={loadingEditHtml} saveActiveWidget={saveActiveWidget} /> {widgets.map(widget => (
- +
))} diff --git a/src/frontend/components/dashboard/lib/react/EditModal/EditModal.test.tsx b/src/frontend/components/dashboard/lib/react/EditModal/EditModal.test.tsx index 8604a45c4..d3bddb13e 100644 --- a/src/frontend/components/dashboard/lib/react/EditModal/EditModal.test.tsx +++ b/src/frontend/components/dashboard/lib/react/EditModal/EditModal.test.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { RefObject } from 'react'; import { render, screen } from '@testing-library/react'; import '@testing-library/dom'; import { describe, it, expect, jest } from '@jest/globals'; @@ -14,7 +14,7 @@ describe('EditModal', () => { editError:'', editHtml:'', editModalOpen:true, - formRef:React.createRef(), + formRef:React.createRef() as RefObject, loadingEditHtml:true, saveActiveWidget:()=>{} }; @@ -37,7 +37,7 @@ describe('EditModal', () => { editError:'', editHtml:'
Test
', editModalOpen:true, - formRef:React.createRef(), + formRef:React.createRef() as RefObject, loadingEditHtml:false, saveActiveWidget:()=>{} }; @@ -60,7 +60,7 @@ describe('EditModal', () => { editError:'Error', editHtml:'', editModalOpen:true, - formRef:React.createRef(), + formRef:React.createRef() as RefObject, loadingEditHtml:false, saveActiveWidget:()=>{} }; @@ -83,7 +83,7 @@ describe('EditModal', () => { editError:'', editHtml:'', editModalOpen:true, - formRef:React.createRef(), + formRef:React.createRef() as RefObject, loadingEditHtml:true, saveActiveWidget:jest.fn() }; @@ -106,7 +106,7 @@ describe('EditModal', () => { editError:'', editHtml:'', editModalOpen:true, - formRef:React.createRef(), + formRef:React.createRef() as RefObject, loadingEditHtml:true, saveActiveWidget:jest.fn() }; @@ -129,7 +129,7 @@ describe('EditModal', () => { editError:'', editHtml:'', editModalOpen:true, - formRef:React.createRef(), + formRef:React.createRef() as RefObject, loadingEditHtml:true, saveActiveWidget:jest.fn() }; diff --git a/src/frontend/components/dashboard/lib/react/EditModal/EditModal.tsx b/src/frontend/components/dashboard/lib/react/EditModal/EditModal.tsx index 6d077ab2e..69dac7b98 100644 --- a/src/frontend/components/dashboard/lib/react/EditModal/EditModal.tsx +++ b/src/frontend/components/dashboard/lib/react/EditModal/EditModal.tsx @@ -53,7 +53,7 @@ export default function EditModal({ editModalOpen, closeModal, editError, loadin {editError &&

{editError}

} {loadingEditHtml - ? Loading... :
} + ? Loading... :
}
diff --git a/src/frontend/components/dashboard/lib/react/Footer.test.tsx b/src/frontend/components/dashboard/lib/react/Footer.test.tsx index fad453e3d..b33275be0 100644 --- a/src/frontend/components/dashboard/lib/react/Footer.test.tsx +++ b/src/frontend/components/dashboard/lib/react/Footer.test.tsx @@ -6,6 +6,7 @@ import { describe, it, expect, jest } from '@jest/globals'; import Footer from './Footer'; import { FooterProps } from './types'; +//@ts-ignore import 'testing/extensions'; describe('Footer', () => { diff --git a/src/frontend/components/dashboard/lib/react/api/index.ts b/src/frontend/components/dashboard/lib/react/api/index.ts index c9ac6c778..1ad677901 100644 --- a/src/frontend/components/dashboard/lib/react/api/index.ts +++ b/src/frontend/components/dashboard/lib/react/api/index.ts @@ -71,7 +71,7 @@ export default class ApiClient { * @param {T} body The body of the request * @returns {Promise} A promise that resolves to the response of the fetch request. */ - POST(route: string, body: T): Promise { return this._fetch(route, "POST", body); } + POST(route: string, body: T | null): Promise { return this._fetch(route, "POST", body ?? {}); } /** * Perform a PUT request to the API. @@ -100,6 +100,7 @@ export default class ApiClient { const strippedLayout = layout.map(widget => ({ ...widget, moved: undefined })); return this.PUT(`/dashboard/${id}`, strippedLayout); } + return Promise.resolve(new Response()); }; /** @@ -127,7 +128,7 @@ export default class ApiClient { * @param {string} id The ID of the widget to delete. * @returns {Promise} A promise that resolves to the response of the delete request. */ - deleteWidget = (id: string): Promise => !this.isDev && this.DELETE(`/widget/${id}`); + deleteWidget = (id: string): Promise => this.isDev ? Promise.reject("Running in dev mode") : this.DELETE(`/widget/${id}`); /** * Get the edit form for a widget. diff --git a/src/frontend/components/form-group/input/lib/documentComponent.ts b/src/frontend/components/form-group/input/lib/documentComponent.ts index f3affaea1..eb69f7333 100644 --- a/src/frontend/components/form-group/input/lib/documentComponent.ts +++ b/src/frontend/components/form-group/input/lib/documentComponent.ts @@ -51,7 +51,7 @@ class DocumentComponent { readonly type = "document"; readonly el: JQuery; readonly fileInput: JQuery; - errors!: (string|Error)[]; + errors!: (string | Error)[]; handler!: ErrorHandler; /** @@ -102,11 +102,11 @@ class DocumentComponent { const file = ev.target.files![0]; if (!file || file === undefined || !file.name) return; const formData = formdataMapper({ file, csrf_token, column_id: columnId }); - upload(url, formData, "POST", (loaded, total) => this.showProgress(file.name, loaded, total)).then((data)=>{ + upload(url, formData, "POST", (loaded, total) => this.showProgress(file.name, loaded, total)).then((data) => { this.addFileToField({ id: data.id, name: data.filename }); }) .catch((e) => { - if(JSON.parse(e as string)?.message) + if (JSON.parse(e as string)?.message) e = JSON.parse(e as string).message; this.handler.addError(e); }); @@ -130,7 +130,7 @@ class DocumentComponent { this.createProgressBar(this.el, file); barContainer = this.el.find(".progress-bar__container[data-file-name=\"" + file + "\"]"); } - barContainer.css("width", undefined); + barContainer.css("width", 0); barContainer.find(".progress-bar__percentage").html(uploadProgression === 100 ? "complete" : `${uploadProgression}%`); barContainer.find(".progress-bar__progress").css("width", `${uploadProgression}%`); } @@ -174,7 +174,7 @@ class DocumentComponent { .hide(); } ).catch((e) => { - if(JSON.parse(e as string)?.message) + if (JSON.parse(e as string)?.message) e = JSON.parse(e as string).message; else if (typeof e == "object" && "message" in e) e = e.message; @@ -182,7 +182,7 @@ class DocumentComponent { $(this.el.find(".progress-bar__container[data-file-name=\"" + file.name + "\"]")) .hide(); }); - } catch (e) { + } catch (e: any) { this.showException(e instanceof Error || "message" in e ? e.message : e as string ?? e.toString()); } } @@ -254,12 +254,13 @@ class DocumentComponent { const { id, name } = data; this.addFileToField({ id, name }); } - } catch (error) { - let e=error; - if(JSON.parse(error as string)?.message) - e = JSON.parse(error as string).message; - else if (typeof error == "object" && "message" in error) + } catch (error: any) { + let e = error; + if (JSON.parse(e as string)?.message) { + e = JSON.parse(e as string).message; + } else if (typeof e == "object" && "message" in e) { e = e.message; + } this.showException(e); const current = $(`#current-${fileId}`); current.text(oldName); diff --git a/src/frontend/components/form-group/searchable-select/FilterSelectHelper.ts b/src/frontend/components/form-group/searchable-select/FilterSelectHelper.ts index db85b9871..a44f167a1 100644 --- a/src/frontend/components/form-group/searchable-select/FilterSelectHelper.ts +++ b/src/frontend/components/form-group/searchable-select/FilterSelectHelper.ts @@ -1,8 +1,8 @@ import "./JQuerySearchableSelect"; export const refreshSelects = (el: JQuery) => { - const ruleFilterSelects = []; - const operatorSelects = []; + const ruleFilterSelects: any[] = []; // I will make this it's proper type later + const operatorSelects: any[] = []; // I will make this it's proper type later el.on("afterCreateRuleFilters.queryBuilder", (e: JQuery.TriggeredEvent, rule: any) => { const ruleFilterSelect = $(rule.$el.find(`select[name=${rule.id}_filter]`)); @@ -28,26 +28,19 @@ export const refreshSelects = (el: JQuery) => { el.on("afterSetRules.queryBuilder", () => { for (const ruleFilterSelect of ruleFilterSelects) { - if (!ruleFilterSelect) { - continue; - } - $(ruleFilterSelect).getSearchableSelect() - .refresh(); + if (!ruleFilterSelect) continue; + $(ruleFilterSelect).getSearchableSelect()?.refresh(); } for (const operatorSelect of operatorSelects) { if (!operatorSelect) continue; - $(operatorSelect).getSearchableSelect() - .refresh(); + $(operatorSelect).getSearchableSelect()?.refresh(); } }); el.on("afterSetRuleOperator.queryBuilder", () => { for (const operatorSelect of operatorSelects) { - if (!operatorSelect) { - continue; - } - $(operatorSelect).getSearchableSelect() - .refresh(); + if (!operatorSelect) continue; + $(operatorSelect).getSearchableSelect()?.refresh(); } }); }; diff --git a/src/frontend/components/form-group/searchable-select/JQuerySearchableSelect.ts b/src/frontend/components/form-group/searchable-select/JQuerySearchableSelect.ts index 40cd689b4..941f563e3 100644 --- a/src/frontend/components/form-group/searchable-select/JQuerySearchableSelect.ts +++ b/src/frontend/components/form-group/searchable-select/JQuerySearchableSelect.ts @@ -6,7 +6,7 @@ if (typeof jQuery === "undefined") throw new Error("jQuery is not loaded. Please declare global { interface JQuery { searchableSelect: (options?: SearchableSelectOptions) => JQuery; - getSearchableSelect: () => SearchableSelect; + getSearchableSelect: () => SearchableSelect | null; } } diff --git a/src/frontend/components/form-group/searchable-select/lib/SearchableSelect.ts b/src/frontend/components/form-group/searchable-select/lib/SearchableSelect.ts index f1ba1ae6f..420272b79 100644 --- a/src/frontend/components/form-group/searchable-select/lib/SearchableSelect.ts +++ b/src/frontend/components/form-group/searchable-select/lib/SearchableSelect.ts @@ -5,8 +5,8 @@ import { SearchableSelectOptions } from "./options"; * SearchableSelect class provides a searchable dropdown interface for a standard HTML select element. */ export class SearchableSelect { - dropdown: HTMLDivElement = null; - button: HTMLElement = null; + dropdown!: HTMLDivElement; + button!: HTMLElement; target: HTMLElement; element: HTMLSelectElement; classList: string[]; diff --git a/src/frontend/components/help-view/lib/component.ts b/src/frontend/components/help-view/lib/component.ts index d080a35e7..a5cb53b9d 100644 --- a/src/frontend/components/help-view/lib/component.ts +++ b/src/frontend/components/help-view/lib/component.ts @@ -7,7 +7,7 @@ import { MarkDown } from "util/formatters/markdown"; */ export default class HelpView extends Component { // This is protected so that it can be accessed in tests. - protected $button: JQuery; + protected $button!: JQuery; /** * Create a new HelpView component. diff --git a/src/frontend/js/lib/util/errorHandler/lib/errorHandler.ts b/src/frontend/js/lib/util/errorHandler/lib/errorHandler.ts index f94455ba2..77df6d2a2 100644 --- a/src/frontend/js/lib/util/errorHandler/lib/errorHandler.ts +++ b/src/frontend/js/lib/util/errorHandler/lib/errorHandler.ts @@ -4,7 +4,7 @@ export class ErrorHandler { private el: JQuery; private errors: string[] = []; - errorContainer: JQuery; + errorContainer!: JQuery; /** * Create a new ErrorHandler instance diff --git a/src/frontend/js/lib/util/filedrag/index.ts b/src/frontend/js/lib/util/filedrag/index.ts index 7c80c9162..22e0dc044 100644 --- a/src/frontend/js/lib/util/filedrag/index.ts +++ b/src/frontend/js/lib/util/filedrag/index.ts @@ -28,12 +28,14 @@ if (typeof jQuery !== "undefined") { if (!this.data("filedrag")) { this.data("filedrag", "true"); - new FileDrag(this, options, (file, index, length) => { - if(index === undefined) index = 1; - if(length === undefined) length = 1; - if (options.debug) console.debug("fileDrop", file, index, length); - const event = $.Event("fileDrop", { file, index, length }); - this.trigger(event); + this.each((_, el) => { + new FileDrag(el, options, (file, index, length) => { + if(index === undefined) index = 1; + if(length === undefined) length = 1; + if (options.debug) console.debug("fileDrop", file, index, length); + const event = $.Event("fileDrop", { file, index, length }); + $(el).trigger(event); + }); }); } diff --git a/src/frontend/js/lib/util/filedrag/lib/filedrag.ts b/src/frontend/js/lib/util/filedrag/lib/filedrag.ts index bd974b01d..d0eaef153 100644 --- a/src/frontend/js/lib/util/filedrag/lib/filedrag.ts +++ b/src/frontend/js/lib/util/filedrag/lib/filedrag.ts @@ -24,7 +24,7 @@ export interface FileDragOptions { */ class FileDrag { private el: JQuery; - private dropZone: JQuery; + private dropZone!: JQuery; // for testing protected dragging: boolean = false; @@ -59,22 +59,26 @@ class FileDrag { }); this.dropZone.on("drop", (e) => { e.preventDefault(); + const files = e?.originalEvent?.dataTransfer?.files; + if(!files) return; if (!this.dragging) return; this.dragging = false; if(this.el.hasClass("dragging")) this.el.removeClass("dragging"); hideElement($(".drop-zone")); showElement($("[data-draggable=\"true\"]")); - if (this.options.debug) console.log(e.originalEvent.dataTransfer.files); + if (this.options.debug) console.log(files); showElement(this.el); - console.log(e.originalEvent.dataTransfer.files); + console.log(files); if (this.options.allowMultiple) { // For some reason the function will not accept a FileList, so we convert it to an array - const files = Array.from(e.originalEvent.dataTransfer.files); - files.forEach((file, index) => { - this.onDrop(file, index, files.length); + const f2 = Array.from(files); + f2.forEach((file, index) => { + this.onDrop(file, index, f2.length); }); } else { - this.onDrop(e.originalEvent.dataTransfer.files[0]); + const file = e?.originalEvent?.dataTransfer?.files?.[0]; + if(!file) return; + this.onDrop(file); } $(document).trigger("drop"); }); @@ -93,7 +97,7 @@ class FileDrag { }); $(document).on("dragleave", (e) => { if (!this.dragging) return; - if (e.originalEvent.pageX != 0 || e.originalEvent.pageY != 0) { + if (e.originalEvent?.pageX != 0 || e.originalEvent?.pageY != 0) { return false; } this.dragging = false; diff --git a/src/frontend/js/lib/util/gadsStorage/lib/GadsStorage.ts b/src/frontend/js/lib/util/gadsStorage/lib/GadsStorage.ts index f330ead65..340b2cc9e 100644 --- a/src/frontend/js/lib/util/gadsStorage/lib/GadsStorage.ts +++ b/src/frontend/js/lib/util/gadsStorage/lib/GadsStorage.ts @@ -26,7 +26,8 @@ export class GadsStorage implements AppStorage { * @returns {Promise} The storage key used to encrypt data. */ private async getStorageKey(): Promise { - if (window.test) { + // TS6 is a bit weird - it recognises window.test as a function and wants to call it so we resort to typechecks + if (typeof window.test !== "undefined") { this.storageKey = "test"; return; } diff --git a/src/frontend/js/lib/util/mapper/mapper.ts b/src/frontend/js/lib/util/mapper/mapper.ts index 65812c7a6..6120730bd 100644 --- a/src/frontend/js/lib/util/mapper/mapper.ts +++ b/src/frontend/js/lib/util/mapper/mapper.ts @@ -34,7 +34,7 @@ interface Record { * @returns {MappedResponse[]} The mapped response */ export const map: MapperFunction = (r: ScriptResponse):MappedResponse[] => { - const result = []; + const result: MappedResponse[] = []; let i = 0; r.records.forEach((record) => { if (record instanceof Object) { diff --git a/src/frontend/js/lib/util/scriptErrorHandler/index.ts b/src/frontend/js/lib/util/scriptErrorHandler/index.ts index 042e3f0ef..a47675956 100644 --- a/src/frontend/js/lib/util/scriptErrorHandler/index.ts +++ b/src/frontend/js/lib/util/scriptErrorHandler/index.ts @@ -1,6 +1,6 @@ import { uploadMessage } from "./lib/MessageUploader"; -const createErrorString = (message: string, source: any, lineno: number, colno: number, error: Error | string | null) => { +const createErrorString = (message: string, source: any, lineno: number, colno: number, error: Error | undefined) => { let errorString = `Error: ${message}\nSource: ${source}\nLine: ${lineno}, Column: ${colno}`; if (error && (error as Error)?.stack) { errorString += `\nStack: ${(error as Error)?.stack}`; @@ -8,19 +8,21 @@ const createErrorString = (message: string, source: any, lineno: number, colno: return errorString; }; -window.onerror = function (message: string, source: any, lineno: number, colno: number, error: Error | string | null) { - if (location.host === "localhost") { +window.onerror = function (message: string| Event, source: any, lineno: number | undefined, colno: number | undefined, error: Error | undefined) { + if (location.host === "localhost" || message instanceof Event) { // If we're on localhost, we log the error to the console. This is useful for development. console.error("Script error occurred:", message, source, lineno, colno, error); } + // We should never trigger this, but it's to stop TS6 arguing! + if (message instanceof Event) return; if (location.pathname === "/api/script_error" || location.pathname === "/login") { // If we're on the script error page, we don't want to log it again. console.error("Script error occurred but not logged to avoid recursion."); - console.error(createErrorString(message, source, lineno, colno, error)); + console.error(createErrorString(message, source, lineno || 0, colno || 0, error)); return; } - const description = createErrorString(message, source, lineno, colno, error); + const description = createErrorString(message, source, lineno || 0, colno || 0, error); uploadMessage(description) .catch(err => { From 64847ece22f0a77c3b623d55ec97a56173392b8e Mon Sep 17 00:00:00 2001 From: Dave Roberts Date: Tue, 15 Sep 2026 12:50:53 +0000 Subject: [PATCH 3/4] Add TS6, update tsconfig, and update code where required --- package.json | 4 +- .../components/bootstrap-popover/index.ts | 1 - ...n.test.js => remove-curval-button.test.ts} | 0 .../button/lib/remove-unload-button.ts | 4 +- .../components/button/lib/rename-button.ts | 18 +- .../button/lib/submit-field-button.ts | 4 +- .../components/dashboard/lib/react/App.tsx | 2 +- src/frontend/js/lib/component.d.ts | 4 +- src/frontend/js/lib/get-field-values.d.ts | 3 +- src/frontend/js/lib/util/filedrag/index.ts | 4 +- tsconfig.json | 29 ++-- yarn.lock | 155 ++++++++++++------ 12 files changed, 142 insertions(+), 86 deletions(-) rename src/frontend/components/button/lib/{remove-curval-button.test.js => remove-curval-button.test.ts} (100%) diff --git a/package.json b/package.json index 7f6c3066b..738b313d8 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,8 @@ "@stylistic/eslint-plugin": "^5.10.0", "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", + "@types/bootstrap": "^5.2.11", + "@types/form-serialize": "^0.7.4", "@types/jest": "^30.0.0", "@types/jquery": "^4.0.1", "@types/jstree": "^3.3.46", @@ -90,7 +92,7 @@ "sass-loader": "^17.0.0", "terser-webpack-plugin": "^5.6.1", "ts-loader": "~9.5.2", - "typescript": "~5.8.0", + "typescript": "~6.0.0", "typescript-eslint": "^8.61.1", "webpack": "^5.107.2", "webpack-cli": "^7.0.3" diff --git a/src/frontend/components/bootstrap-popover/index.ts b/src/frontend/components/bootstrap-popover/index.ts index 8a6539020..70dfabf08 100644 --- a/src/frontend/components/bootstrap-popover/index.ts +++ b/src/frontend/components/bootstrap-popover/index.ts @@ -2,6 +2,5 @@ import { initializeComponent } from "component"; import BootstrapPopoverComponent from "./lib/component"; export default (scope:any) =>{ - //@ts-expect-error Typings on initializeComponent are incorrect initializeComponent(scope, "[data-bs-toggle=\"popover\"]", BootstrapPopoverComponent); }; diff --git a/src/frontend/components/button/lib/remove-curval-button.test.js b/src/frontend/components/button/lib/remove-curval-button.test.ts similarity index 100% rename from src/frontend/components/button/lib/remove-curval-button.test.js rename to src/frontend/components/button/lib/remove-curval-button.test.ts diff --git a/src/frontend/components/button/lib/remove-unload-button.ts b/src/frontend/components/button/lib/remove-unload-button.ts index a1dc8020d..0b4215c50 100644 --- a/src/frontend/components/button/lib/remove-unload-button.ts +++ b/src/frontend/components/button/lib/remove-unload-button.ts @@ -3,7 +3,5 @@ * @param {JQuery} element The button element to add the click event to */ export default function createRemoveUnloadButton(element: JQuery) { - element.on("click", () => { - $(window).off("beforeunload"); - }); + element.on("click", () => $(window).off("beforeunload")); } diff --git a/src/frontend/components/button/lib/rename-button.ts b/src/frontend/components/button/lib/rename-button.ts index c365b27da..76d24648a 100644 --- a/src/frontend/components/button/lib/rename-button.ts +++ b/src/frontend/components/button/lib/rename-button.ts @@ -195,15 +195,13 @@ class RenameButton { } } -if (typeof jQuery !== "undefined") { - (function ($) { - $.fn.renameButton = function () { - return this.each(function (_: unknown, el: HTMLElement) { - if (!(el instanceof HTMLButtonElement)) return; - new RenameButton(el); - }); - }; - })(jQuery); -} +(function ($) { + $.fn.renameButton = function () { + return this.each(function (_: unknown, el: HTMLElement) { + if (!(el instanceof HTMLButtonElement)) return; + new RenameButton(el); + }); + }; +})(jQuery); export { RenameEvent }; diff --git a/src/frontend/components/button/lib/submit-field-button.ts b/src/frontend/components/button/lib/submit-field-button.ts index 46bf3a4f1..b2534fbc6 100644 --- a/src/frontend/components/button/lib/submit-field-button.ts +++ b/src/frontend/components/button/lib/submit-field-button.ts @@ -10,6 +10,7 @@ declare global { treeApi: string; } } + UpdateFilter?: (filterEl: JQuery | undefined, ev: JQuery.ClickEvent) => void; } interface JQuery { queryBuilder(operation: string): JQuery; @@ -85,7 +86,6 @@ export default class SubmitFieldButton { } if (bUpdateTree) { - //Bit of typecasting here, purely because the jstree plugin doesn't have types const v = $jstreeEl.jstree(true).get_json("#", { flat: false }); const mytext = JSON.stringify(v); const data = $jstreeEl.data(); @@ -100,9 +100,7 @@ export default class SubmitFieldButton { }); } - // @ts-expect-error - This is a global function if (bUpdateFilter && window.UpdateFilter) { - // @ts-expect-error - This is a global function window.UpdateFilter($filterEl, ev); } diff --git a/src/frontend/components/dashboard/lib/react/App.tsx b/src/frontend/components/dashboard/lib/react/App.tsx index 6b9bd7bb5..fdfdf74e1 100644 --- a/src/frontend/components/dashboard/lib/react/App.tsx +++ b/src/frontend/components/dashboard/lib/react/App.tsx @@ -311,7 +311,7 @@ export default function App(props: AppProps): React.JSX.Element { return (
{props.hideMenu ||
void; + const registerComponent: (componentInitializer: (...args: any[]) => any) => void; /** * Initialize all registered components in the defined scope @@ -55,6 +55,6 @@ declare module "component" { * @param componentClass The Component class to initialize * @returns An array of initialized components */ - const initializeComponent: (scope: HTMLElement, selector: string | Function, componentClass: T) => T[]; + const initializeComponent: (scope: HTMLElement, selector: string | ((...args: any[]) => any), componentClass: { new(element: HTMLElement): T }) => T[]; } diff --git a/src/frontend/js/lib/get-field-values.d.ts b/src/frontend/js/lib/get-field-values.d.ts index f42ee903b..1b2343c58 100644 --- a/src/frontend/js/lib/get-field-values.d.ts +++ b/src/frontend/js/lib/get-field-values.d.ts @@ -5,4 +5,5 @@ * @param for_code - Whether to return the value for code generation * @param for_autosave - Whether to return the value for autosave * @returns The value(s) of the field, formatted according to the field type - */declare const getFieldValues: ($depends: JQuery, filtered: boolean, for_code: boolean, for_autosave: boolean) => string | Array | object + */ +declare const getFieldValues: ($depends: JQuery, filtered: boolean, for_code: boolean, for_autosave: boolean) => string | Array | object; diff --git a/src/frontend/js/lib/util/filedrag/index.ts b/src/frontend/js/lib/util/filedrag/index.ts index 22e0dc044..9941058ce 100644 --- a/src/frontend/js/lib/util/filedrag/index.ts +++ b/src/frontend/js/lib/util/filedrag/index.ts @@ -33,8 +33,8 @@ if (typeof jQuery !== "undefined") { if(index === undefined) index = 1; if(length === undefined) length = 1; if (options.debug) console.debug("fileDrop", file, index, length); - const event = $.Event("fileDrop", { file, index, length }); - $(el).trigger(event); + const event = $.Event("fileDrop", { file, index, length }); + $(el).trigger(event); }); }); } diff --git a/tsconfig.json b/tsconfig.json index 327321344..a9ef8d576 100755 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,49 +1,48 @@ { "compilerOptions": { - "baseUrl": "./src/frontend", + "rootDir": ".", "outDir": "./dist/", "noImplicitAny": false, "module": "ES2020", "target": "ES2020", "jsx": "react", "allowJs": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "allowSyntheticDefaultImports": true, "esModuleInterop": true, - "types": ["node"], + "types": ["node", "jest", "jquery"], "paths": { "js/*": [ - "js/*" + "./src/frontend/js/*" ], "components/*": [ - "components/*" + "./src/frontend/components/*" ], "util/*": [ - "js/lib/util/*" + "./src/frontend/js/lib/util/*" ], "component": [ - "js/lib/component" + "./src/frontend/js/lib/component" ], "validation": [ - "js/lib/validation" + "./src/frontend/js/lib/validation" ], "logging": [ - "js/lib/logging" + "./src/frontend/js/lib/logging" ], "get-field-values": [ - "js/lib/get-field-values" + "./src/frontend/js/lib/get-field-values" ], "set-field-values": [ - "js/lib/set-field-values" + "./src/frontend/js/lib/set-field-values" ], "plotly/*": [ - "js/lib/plotly/*" + "./src/frontend/js/lib/plotly/*" ], "guid": [ - "js/lib/guid" + "./src/frontend/js/lib/guid" ], - }, - "types": ["node", "jest"] + } }, "include": [ "src/frontend/**/*" diff --git a/yarn.lock b/yarn.lock index bf88b320f..c33a76d40 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1755,7 +1755,7 @@ resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.3.6.tgz#3569708bd4be4d8870ba32bf1c456dac81600d97" integrity sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA== -"@popperjs/core@^2.11.8", "@popperjs/core@^2.9.0": +"@popperjs/core@^2.11.8", "@popperjs/core@^2.9.0", "@popperjs/core@^2.9.2": version "2.11.8" resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== @@ -1905,6 +1905,13 @@ dependencies: "@babel/types" "^7.28.2" +"@types/bootstrap@^5.2.11": + version "5.2.11" + resolved "https://registry.yarnpkg.com/@types/bootstrap/-/bootstrap-5.2.11.tgz#65916c7b3520e10e732802756414a16b5db9cd80" + integrity sha512-69ykbLiNzQxi6Q/y/aYJLrlzI5QkLQjhIYW2U+cxujBxAmaBtmXKtxnFes59Ttfpt936as6xJU0dYnmVpcwIwg== + dependencies: + "@popperjs/core" "^2.9.2" + "@types/esrecurse@^4.3.1": version "4.3.1" resolved "https://registry.yarnpkg.com/@types/esrecurse/-/esrecurse-4.3.1.tgz#6f636af962fbe6191b830bd676ba5986926bccec" @@ -1915,6 +1922,11 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.9.tgz#cf3f0e876d7bee15a93ab925b82bf570a3904a24" integrity sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg== +"@types/form-serialize@^0.7.4": + version "0.7.4" + resolved "https://registry.yarnpkg.com/@types/form-serialize/-/form-serialize-0.7.4.tgz#17696eff6e79eefb9e55a71ca0833663bbe9328d" + integrity sha512-9lmMvL6qLnT+VaumhbMNwqDyuO93reQK6AnqlNzhzS+HtDfgQKVRjXO3grbO1HL5NJsbqsfTxE2gSv8tgusEAw== + "@types/glob@^7.1.1": version "7.2.0" resolved "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz" @@ -1994,16 +2006,16 @@ integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== "@types/node@*": - version "26.4.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-26.4.0.tgz#4c4ca071c42241fe602741d02999f16b4e64c468" - integrity sha512-faiGnoIrLH/V8cibOMEAZ8pMw6oXqSukl29ra4mN8GdaB2ZewzeaLj+INpV5N+Z1eKWzY+IzaIZH2EIR6YZRNQ== + version "26.5.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-26.5.1.tgz#b19c390e15813f402a94b86e3af9042f792138be" + integrity sha512-CzNm2FezW4VR/LjG6yUdiEgLE/rAQ9Slj5gCu/C2VrdcW7I0ahNZ8DRbHT7zOZ6r3ONgd/bsQIeSaoDGrd1C6g== dependencies: - undici-types "~8.3.0" + undici-types "~8.9.0" "@types/node@^25.9.2": - version "25.9.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-25.9.5.tgz#0fefc09e6e82e94cde291bacf43522e989eb01a4" - integrity sha512-OScDchr2fwuUmWdf4kZ9h7PcJiYDVInhJizG/biAq3cAvqwYktuy/TYGGdZNMtNTFUP7rnb0NU4TUdm82kt4Rg== + version "25.9.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-25.9.6.tgz#47865018f9a5635f161ec69c041385edb15d78fe" + integrity sha512-JR6Q/PV5DKFvjrGFVqQJdeG0qvsqQQLDa3TzFrqVwhqRXqwNaxPo2KYCtCQXpOdIulCouKwT7a6in9nFthBAzw== dependencies: undici-types ">=7.24.0 <7.24.7" @@ -2013,9 +2025,9 @@ integrity sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw== "@types/react-dom@^19.2.3": - version "19.2.5" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.5.tgz#d4ecf8d704cab59901d3fbb0975b6f855306595f" - integrity sha512-fMPwH9v7r/pp43yUd2/Mbiex5KouJwwR3dzHkhLREUC6764VyDsqxhAxv6OFEYR1RhjOyD1naqba8ECDBe7ZQg== + version "19.3.0" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.3.0.tgz#90294a9266efd928a5266f2110d60d5dba5015d2" + integrity sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q== "@types/react-grid-layout@^1.0.0": version "1.3.6" @@ -2044,9 +2056,9 @@ csstype "^3.0.2" "@types/react@^19.2.17": - version "19.2.18" - resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.18.tgz#eb0b6a1fb635d1a9692d5f84a3495bd8ad153707" - integrity sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w== + version "19.3.0" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.3.0.tgz#3b3ce8d8c1627fb5ccd4dadd57ef445c5a153b77" + integrity sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg== dependencies: csstype "^3.2.2" @@ -2477,6 +2489,13 @@ ajv-formats@^2.1.1: dependencies: ajv "^8.0.0" +ajv-formats@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-3.0.1.tgz#3d5dc762bca17679c3c2ea7e90ad6b7532309578" + integrity sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ== + dependencies: + ajv "^8.0.0" + ajv-keywords@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" @@ -2494,7 +2513,7 @@ ajv@^6.14.0: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.9.0: +ajv@^8.0.0, ajv@^8.20.0, ajv@^8.9.0: version "8.20.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.20.0.tgz#304b3636add88ba7d936760dd50ece006dea95f9" integrity sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA== @@ -2848,11 +2867,16 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -baseline-browser-mapping@^2.10.44, baseline-browser-mapping@^2.11.12: +baseline-browser-mapping@^2.10.44: version "2.11.20" resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.11.20.tgz#26078c7a4b08299656ea7ddceaebec955dc44303" integrity sha512-H0ulySigv6icDJ1F7SjtdCD6PrhTpdYCmP0CactWy1+ekh0AFd0o1Wn5T8b+hnTmdBx19u9yhL6wvCylXMY7zw== +baseline-browser-mapping@^2.11.12, baseline-browser-mapping@^2.11.20: + version "2.11.23" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.11.23.tgz#304c980a35de0f460cf12985359d6e11494c1ab4" + integrity sha512-le521dGVfxM7yRX0EikCoSz+rOK+hHzdDt/E7mG1jOJB/6WAAUuwVroLwaB7ApaUsz5Q0kFlDXLSA9MheUIfRQ== + bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" @@ -2916,7 +2940,7 @@ braces@^3.0.3: dependencies: fill-range "^7.1.1" -browserslist@^4.24.0, browserslist@^4.28.1: +browserslist@^4.24.0: version "4.28.8" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.8.tgz#a3c79ceb70028527e5da7dafc887f3200b5168c0" integrity sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA== @@ -2927,6 +2951,17 @@ browserslist@^4.24.0, browserslist@^4.28.1: node-releases "^2.0.53" update-browserslist-db "^1.3.0" +browserslist@^4.28.1: + version "4.28.9" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.9.tgz#07ce6b449b90af880eb9bfb7cd39372cc4f71c8c" + integrity sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg== + dependencies: + baseline-browser-mapping "^2.11.20" + caniuse-lite "^1.0.30001810" + electron-to-chromium "^1.5.420" + node-releases "^2.0.54" + update-browserslist-db "^1.3.2" + browserslist@^4.28.6: version "4.28.7" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.7.tgz#409046517fccd2e51cdc20f077454b7141184028" @@ -3023,7 +3058,7 @@ camelcase@^6.3.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001806, caniuse-lite@^1.0.30001809: +caniuse-lite@^1.0.30001806, caniuse-lite@^1.0.30001809, caniuse-lite@^1.0.30001810: version "1.0.30001810" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz#4970b477dea3278374de9bc43aa8f5d39fc3cda2" integrity sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg== @@ -3627,11 +3662,16 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" -electron-to-chromium@^1.5.393, electron-to-chromium@^1.5.402: +electron-to-chromium@^1.5.393: version "1.5.418" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.418.tgz#40ccbf6d572447663041611e05924b53bc9a0331" integrity sha512-UzS26r3AEbG5wSoGVpJKqwHIU9zwQN7LHdVIThDrJpS0I5KdlXFMEb8543fhc9dVnIIAST6ar8rhwa00AL5MlA== +electron-to-chromium@^1.5.402, electron-to-chromium@^1.5.420: + version "1.5.428" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.428.tgz#36bdb39055d71ab08a54855508ef33940bc467e6" + integrity sha512-1JxbaFJj1bRKurj1uY3l4xxpU9kOUAUjcIgApj0qu1Pao5GhoIWI8iL0BeMYJ2njig1hBx0A7eKD9VGjH9wlHw== + emittery@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" @@ -3667,10 +3707,10 @@ enhanced-resolve@^5.0.0: graceful-fs "^4.2.4" tapable "^2.3.3" -enhanced-resolve@^5.24.4: - version "5.24.5" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz#b4dad3255b7545f07ba5535189868e9f85f47573" - integrity sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A== +enhanced-resolve@^5.25.0: + version "5.25.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.25.1.tgz#9d919bfa898b116e58f20e95127c047693158771" + integrity sha512-nGXts5znJzmWPu+mIE9izCOzdg63oJca2mDzGWWTth7sr4aCToKcoyFVBQwN75Ij5Pf6p510EwkTqViTRzDV+w== dependencies: graceful-fs "^4.2.4" tapable "^2.3.3" @@ -4142,9 +4182,9 @@ fast-levenshtein@^2.0.6: integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fast-uri@^3.0.1: - version "3.1.6" - resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.6.tgz#bfdd308ef763f835fed059f3c6c925f708e33e3a" - integrity sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q== + version "3.1.8" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.8.tgz#f7db8d942e20eead3cfe93b0beeb4eb0169e938b" + integrity sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg== fb-watchman@^2.0.2: version "2.0.2" @@ -5907,9 +5947,9 @@ minimist@^1.2.5, minimist@^1.2.8: integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== minimizer-webpack-plugin@^5.7.0: - version "5.8.0" - resolved "https://registry.yarnpkg.com/minimizer-webpack-plugin/-/minimizer-webpack-plugin-5.8.0.tgz#744f0e28da888aa1708e2be32b4ddcf2eeb09e58" - integrity sha512-2cT9+goJfBhtMz+gJqejSf09ClgmYhPhccRb/fb0ztVbixt0BkO8mRuI26FoPPuUNkRk6iEDryWAIouc5W8eJA== + version "5.10.1" + resolved "https://registry.yarnpkg.com/minimizer-webpack-plugin/-/minimizer-webpack-plugin-5.10.1.tgz#333ad27a53fcd51bbd868252a4733ee08480b021" + integrity sha512-+dsZEyTcy1lkq41lxdiOqvb26gXbYGgwY+30w37f9PqUVkuEBejBLDotsHOTjuga9xJyGLW2DqzKDUyJ4W/PMQ== dependencies: "@jridgewell/trace-mapping" "^0.3.31" jest-worker "^27.4.5" @@ -5961,11 +6001,16 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-releases@^2.0.51, node-releases@^2.0.53: +node-releases@^2.0.51: version "2.0.54" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.54.tgz#09af17d5647aa9f221ec5cf2becb95b68a981afe" integrity sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ== +node-releases@^2.0.53, node-releases@^2.0.54: + version "2.0.55" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.55.tgz#c52faedb68001dcfe77495b5ed1ac5827a1788fc" + integrity sha512-mIrE/Cw9y+9Au6dS5vDKDhQza9YvG6w+ZrS6X+ZzA7yFW/soAeaups4Qzn1bL6g5FVy8WtP79+0j82oPIbqRjQ== + normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -6783,7 +6828,7 @@ scheduler@^0.27.0: resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd" integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== -schema-utils@^4.0.0, schema-utils@^4.3.0, schema-utils@^4.3.3: +schema-utils@^4.0.0, schema-utils@^4.3.0: version "4.3.3" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.3.tgz#5b1850912fa31df90716963d45d9121fdfc09f46" integrity sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA== @@ -6803,6 +6848,16 @@ schema-utils@^4.2.0: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" +schema-utils@^4.3.3, schema-utils@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.5.0.tgz#aaf8d588bd2b19511175175a5222baef648c009c" + integrity sha512-zJlMCZ0cAR5p/Y4oVpRoqioDMJcGxaXRrQ/4rP4WyR84vc5z/DolXdbvXeDpTwbtocDFr2rhPqHPErDCUtz2kA== + dependencies: + "@types/json-schema" "^7.0.15" + ajv "^8.20.0" + ajv-formats "^3.0.1" + ajv-keywords "^5.1.0" + semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" @@ -7460,10 +7515,10 @@ typescript-eslint@^8.61.1: "@typescript-eslint/typescript-estree" "8.69.0" "@typescript-eslint/utils" "8.69.0" -typescript@~5.8.0: - version "5.8.3" - resolved "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz" - integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== +typescript@~6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-6.0.3.tgz#90251dc007916e972786cb94d74d15b185577d21" + integrity sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw== uglify-js@^3.1.4: version "3.17.4" @@ -7500,10 +7555,10 @@ uncontrollable@^8.0.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.24.6.tgz#61275b485d7fd4e9d269c7cf04ec2873c9cc0f91" integrity sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg== -undici-types@~8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-8.3.0.tgz#44e9fc9f3244648cdea35e4f9bb2d681e9410809" - integrity sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ== +undici-types@~8.9.0: + version "8.9.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-8.9.0.tgz#e240d97c8b5d85e5347ce73d25865c7906c1ec9f" + integrity sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg== unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.1" @@ -7568,7 +7623,7 @@ untildify@^4.0.0: resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== -update-browserslist-db@^1.2.3, update-browserslist-db@^1.3.0: +update-browserslist-db@^1.2.3: version "1.3.2" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz#9d99fbff56c50bb11ba5fd35cece5916da595836" integrity sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw== @@ -7576,6 +7631,14 @@ update-browserslist-db@^1.2.3, update-browserslist-db@^1.3.0: escalade "^3.2.0" picocolors "^1.1.1" +update-browserslist-db@^1.3.0, update-browserslist-db@^1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz#197e21fb2561fa89f8b94fad605a2b296a18993a" + integrity sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -7681,26 +7744,24 @@ webpack-sources@^3.5.1: integrity sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw== webpack@^5.107.2: - version "5.110.2" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.110.2.tgz#ef23a0e62fe5e1ba71b033e3505b7f005d8d5c6e" - integrity sha512-TciLrfM7zgEjqGdY851HkirDsSPQgTFsWQpl9oHqMAMYsHhEC0bKjscvjpnz+pzx10hLC8qISApGrsnrCP4UtQ== + version "5.111.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.111.0.tgz#65e7e0044d69373ea0129ef401c0299aaba52764" + integrity sha512-A2R74kfE6b3eLKS91iZiol03Ebx7avmJJBOyiP+4LePW9NNeNJGQkKeSQos4dk6R4H0YAMYlv+aOrs1CyWT3eA== dependencies: "@types/estree" "^1.0.8" "@types/json-schema" "^7.0.15" "@webassemblyjs/ast" "^1.14.1" "@webassemblyjs/wasm-edit" "^1.14.1" "@webassemblyjs/wasm-parser" "^1.14.1" - acorn "^8.16.0" browserslist "^4.28.1" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.24.4" + enhanced-resolve "^5.25.0" es-module-lexer "^2.1.0" events "^3.2.0" graceful-fs "^4.2.11" mime-db "^1.54.0" minimizer-webpack-plugin "^5.7.0" - neo-async "^2.6.2" - schema-utils "^4.3.3" + schema-utils "^4.5.0" tapable "^2.3.0" watchpack "^2.5.2" webpack-sources "^3.5.1" From b9368c282904fe1105710c3512fa99a40c15ea33 Mon Sep 17 00:00:00 2001 From: Dave Roberts Date: Tue, 15 Sep 2026 14:31:00 +0000 Subject: [PATCH 4/4] Fix missed change --- .../form-group/searchable-select/FilterSelectHelper.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/components/form-group/searchable-select/FilterSelectHelper.ts b/src/frontend/components/form-group/searchable-select/FilterSelectHelper.ts index a44f167a1..cb9aaf46a 100644 --- a/src/frontend/components/form-group/searchable-select/FilterSelectHelper.ts +++ b/src/frontend/components/form-group/searchable-select/FilterSelectHelper.ts @@ -1,8 +1,8 @@ import "./JQuerySearchableSelect"; export const refreshSelects = (el: JQuery) => { - const ruleFilterSelects: any[] = []; // I will make this it's proper type later - const operatorSelects: any[] = []; // I will make this it's proper type later + const ruleFilterSelects: HTMLSelectElement[] = []; + const operatorSelects: HTMLSelectElement[] = []; el.on("afterCreateRuleFilters.queryBuilder", (e: JQuery.TriggeredEvent, rule: any) => { const ruleFilterSelect = $(rule.$el.find(`select[name=${rule.id}_filter]`));