diff --git a/web_src/js/components/DiffCommitSelector.vue b/web_src/js/components/DiffCommitSelector.vue index b95e18796c6..c78531cf9f3 100644 --- a/web_src/js/components/DiffCommitSelector.vue +++ b/web_src/js/components/DiffCommitSelector.vue @@ -142,11 +142,11 @@ export default { Object.assign(this.locale, results.locale); }, showAllChanges() { - window.location = `${this.issueLink}/files${this.queryParams}`; + window.location.assign(`${this.issueLink}/files${this.queryParams}`); }, /** Called when user clicks on since last review */ changesSinceLastReviewClick() { - window.location = `${this.issueLink}/files/${this.lastReviewCommitSha}..${this.commits.at(-1).id}${this.queryParams}`; + window.location.assign(`${this.issueLink}/files/${this.lastReviewCommitSha}..${this.commits.at(-1).id}${this.queryParams}`); }, /** Clicking on a single commit opens this specific commit */ commitClicked(commitId, newWindow = false) { @@ -154,7 +154,7 @@ export default { if (newWindow) { window.open(url); } else { - window.location = url; + window.location.assign(url); } }, /** @@ -176,14 +176,14 @@ export default { const lastCommitIdx = this.commits.findLastIndex((x) => x.selected); if (lastCommitIdx === this.commits.length - 1) { // user selected all commits - just show the normal diff page - window.location = `${this.issueLink}/files${this.queryParams}`; + window.location.assign(`${this.issueLink}/files${this.queryParams}`); } else { - window.location = `${this.issueLink}/files/${this.commits[lastCommitIdx].id}${this.queryParams}`; + window.location.assign(`${this.issueLink}/files/${this.commits[lastCommitIdx].id}${this.queryParams}`); } } else { const start = this.commits[this.commits.findIndex((x) => x.selected) - 1].id; const end = this.commits.findLast((x) => x.selected).id; - window.location = `${this.issueLink}/files/${start}..${end}${this.queryParams}`; + window.location.assign(`${this.issueLink}/files/${start}..${end}${this.queryParams}`); } } }, diff --git a/web_src/js/features/repo-issue.ts b/web_src/js/features/repo-issue.ts index 5d5e3568b92..520f2081b0b 100644 --- a/web_src/js/features/repo-issue.ts +++ b/web_src/js/features/repo-issue.ts @@ -97,7 +97,7 @@ function excludeLabel(item) { const regStr = `labels=((?:-?[0-9]+%2c)*)(${id})((?:%2c-?[0-9]+)*)&`; const newStr = 'labels=$1-$2$3&'; - window.location = href.replace(new RegExp(regStr), newStr); + window.location.assign(href.replace(new RegExp(regStr), newStr)); } export function initRepoIssueSidebarList() { diff --git a/web_src/js/utils/dom.ts b/web_src/js/utils/dom.ts index 0e0830c9540..a6e0fe28543 100644 --- a/web_src/js/utils/dom.ts +++ b/web_src/js/utils/dom.ts @@ -92,7 +92,7 @@ export function onDomReady(cb: () => Promisable) { // checks whether an element is owned by the current document, and whether it is a document fragment or element node // if it is, it means it is a "normal" element managed by us, which can be modified safely. -export function isDocumentFragmentOrElementNode(el: Element) { +export function isDocumentFragmentOrElementNode(el: Element | Node) { try { return el.ownerDocument === document && el.nodeType === Node.ELEMENT_NODE || el.nodeType === Node.DOCUMENT_FRAGMENT_NODE; } catch { diff --git a/web_src/js/vendor/jquery.are-you-sure.ts b/web_src/js/vendor/jquery.are-you-sure.ts index e7bb203c5a9..858f9871b8b 100644 --- a/web_src/js/vendor/jquery.are-you-sure.ts +++ b/web_src/js/vendor/jquery.are-you-sure.ts @@ -1,3 +1,4 @@ +// @ts-nocheck // Fork of the upstream module. The only changes are: // * use export to make it work with ES6 modules. // * the addition of `const` to make it strict mode compatible. diff --git a/web_src/js/webcomponents/absolute-date.ts b/web_src/js/webcomponents/absolute-date.ts index 6a053c6a55c..8eb1c3e37ea 100644 --- a/web_src/js/webcomponents/absolute-date.ts +++ b/web_src/js/webcomponents/absolute-date.ts @@ -26,7 +26,7 @@ window.customElements.define('absolute-date', class extends HTMLElement { this.shadowRoot.textContent = toAbsoluteLocaleDate(this.getAttribute('date'), lang, opt); }; - attributeChangedCallback(_name, oldValue, newValue) { + attributeChangedCallback(_name: string, oldValue: string | null, newValue: string | null) { if (!this.initialized || oldValue === newValue) return; this.update(); } diff --git a/web_src/js/webcomponents/origin-url.test.ts b/web_src/js/webcomponents/origin-url.test.ts index 4082e53aea9..19cc467d7df 100644 --- a/web_src/js/webcomponents/origin-url.test.ts +++ b/web_src/js/webcomponents/origin-url.test.ts @@ -1,9 +1,9 @@ import {toOriginUrl} from './origin-url.ts'; test('toOriginUrl', () => { - const oldLocation = window.location; + const oldLocation = String(window.location); for (const origin of ['https://example.com', 'https://example.com:3000']) { - window.location = new URL(`${origin}/`); + window.location.assign(`${origin}/`); expect(toOriginUrl('/')).toEqual(`${origin}/`); expect(toOriginUrl('/org/repo.git')).toEqual(`${origin}/org/repo.git`); expect(toOriginUrl('https://another.com')).toEqual(`${origin}/`); @@ -13,5 +13,5 @@ test('toOriginUrl', () => { expect(toOriginUrl('https://another.com:4000/')).toEqual(`${origin}/`); expect(toOriginUrl('https://another.com:4000/org/repo.git')).toEqual(`${origin}/org/repo.git`); } - window.location = oldLocation; + window.location.assign(oldLocation); }); diff --git a/web_src/js/webcomponents/origin-url.ts b/web_src/js/webcomponents/origin-url.ts index 09aa77f2c0f..d407fe0dff7 100644 --- a/web_src/js/webcomponents/origin-url.ts +++ b/web_src/js/webcomponents/origin-url.ts @@ -1,7 +1,7 @@ // Convert an absolute or relative URL to an absolute URL with the current origin. It only // processes absolute HTTP/HTTPS URLs or relative URLs like '/xxx' or '//host/xxx'. // NOTE: Keep this function in sync with clone_script.tmpl -export function toOriginUrl(urlStr) { +export function toOriginUrl(urlStr: string) { try { if (urlStr.startsWith('http://') || urlStr.startsWith('https://') || urlStr.startsWith('/')) { const {origin, protocol, hostname, port} = window.location; diff --git a/web_src/js/webcomponents/overflow-menu.ts b/web_src/js/webcomponents/overflow-menu.ts index 2efeb9222b0..777d7dc65dd 100644 --- a/web_src/js/webcomponents/overflow-menu.ts +++ b/web_src/js/webcomponents/overflow-menu.ts @@ -4,6 +4,14 @@ import {isDocumentFragmentOrElementNode} from '../utils/dom.ts'; import octiconKebabHorizontal from '../../../public/assets/img/svg/octicon-kebab-horizontal.svg'; window.customElements.define('overflow-menu', class extends HTMLElement { + tippyContent: HTMLDivElement; + tippyItems: Array; + button: HTMLButtonElement; + menuItemsEl: HTMLElement; + resizeObserver: ResizeObserver; + mutationObserver: MutationObserver; + lastWidth: number; + updateItems = throttle(100, () => { // eslint-disable-line unicorn/consistent-function-scoping -- https://github.com/sindresorhus/eslint-plugin-unicorn/issues/2088 if (!this.tippyContent) { const div = document.createElement('div'); @@ -11,7 +19,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement { div.tabIndex = -1; // for initial focus, programmatic focus only div.addEventListener('keydown', (e) => { if (e.key === 'Tab') { - const items = this.tippyContent.querySelectorAll('[role="menuitem"]'); + const items = this.tippyContent.querySelectorAll('[role="menuitem"]'); if (e.shiftKey) { if (document.activeElement === items[0]) { e.preventDefault(); @@ -32,27 +40,27 @@ window.customElements.define('overflow-menu', class extends HTMLElement { if (document.activeElement?.matches('[role="menuitem"]')) { e.preventDefault(); e.stopPropagation(); - document.activeElement.click(); + (document.activeElement as HTMLElement).click(); } } else if (e.key === 'ArrowDown') { if (document.activeElement?.matches('.tippy-target')) { e.preventDefault(); e.stopPropagation(); - document.activeElement.querySelector('[role="menuitem"]:first-of-type').focus(); + document.activeElement.querySelector('[role="menuitem"]:first-of-type').focus(); } else if (document.activeElement?.matches('[role="menuitem"]')) { e.preventDefault(); e.stopPropagation(); - document.activeElement.nextElementSibling?.focus(); + (document.activeElement.nextElementSibling as HTMLElement)?.focus(); } } else if (e.key === 'ArrowUp') { if (document.activeElement?.matches('.tippy-target')) { e.preventDefault(); e.stopPropagation(); - document.activeElement.querySelector('[role="menuitem"]:last-of-type').focus(); + document.activeElement.querySelector('[role="menuitem"]:last-of-type').focus(); } else if (document.activeElement?.matches('[role="menuitem"]')) { e.preventDefault(); e.stopPropagation(); - document.activeElement.previousElementSibling?.focus(); + (document.activeElement.previousElementSibling as HTMLElement)?.focus(); } } }); @@ -60,8 +68,8 @@ window.customElements.define('overflow-menu', class extends HTMLElement { this.tippyContent = div; } - const itemFlexSpace = this.menuItemsEl.querySelector('.item-flex-space'); - const itemOverFlowMenuButton = this.querySelector('.overflow-menu-button'); + const itemFlexSpace = this.menuItemsEl.querySelector('.item-flex-space'); + const itemOverFlowMenuButton = this.querySelector('.overflow-menu-button'); // move items in tippy back into the menu items for subsequent measurement for (const item of this.tippyItems || []) { @@ -78,7 +86,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement { itemOverFlowMenuButton?.style.setProperty('display', 'none', 'important'); this.tippyItems = []; const menuRight = this.offsetLeft + this.offsetWidth; - const menuItems = this.menuItemsEl.querySelectorAll('.item, .item-flex-space'); + const menuItems = this.menuItemsEl.querySelectorAll('.item, .item-flex-space'); let afterFlexSpace = false; for (const item of menuItems) { if (item.classList.contains('item-flex-space')) { @@ -189,14 +197,14 @@ window.customElements.define('overflow-menu', class extends HTMLElement { // template rendering, wait for its addition. // The eslint rule is not sophisticated enough or aware of this problem, see // https://github.com/43081j/eslint-plugin-wc/pull/130 - const menuItemsEl = this.querySelector('.overflow-menu-items'); // eslint-disable-line wc/no-child-traversal-in-connectedcallback + const menuItemsEl = this.querySelector('.overflow-menu-items'); // eslint-disable-line wc/no-child-traversal-in-connectedcallback if (menuItemsEl) { this.menuItemsEl = menuItemsEl; this.init(); } else { this.mutationObserver = new MutationObserver((mutations) => { for (const mutation of mutations) { - for (const node of mutation.addedNodes) { + for (const node of mutation.addedNodes as NodeListOf) { if (!isDocumentFragmentOrElementNode(node)) continue; if (node.classList.contains('overflow-menu-items')) { this.menuItemsEl = node; diff --git a/web_src/js/webcomponents/polyfills.ts b/web_src/js/webcomponents/polyfills.ts index 38f50fa02f5..4a84ee9562b 100644 --- a/web_src/js/webcomponents/polyfills.ts +++ b/web_src/js/webcomponents/polyfills.ts @@ -4,10 +4,11 @@ try { new Intl.NumberFormat('en', {style: 'unit', unit: 'minute'}).format(1); } catch { const intlNumberFormat = Intl.NumberFormat; - Intl.NumberFormat = function(locales, options) { + // @ts-expect-error - polyfill is incomplete + Intl.NumberFormat = function(locales: string | string[], options: Intl.NumberFormatOptions) { if (options.style === 'unit') { return { - format(value) { + format(value: number | bigint | string) { return ` ${value} ${options.unit}`; }, };