Switch from eslint to oxlint (#34166)

* Fix type imports

* Fix jsdoc

* Fixup types

* Fix stray awaits on non-thenables

* Fixup imports

* Fix splats

* Fix this-context on callbacks

* Memoise react contexts

* Prefer find/flatMap

* Make oxlint happier about our React keys

* Avoid unsafe default function params

* Fixup jsdoc

* Fixup contexts

* Switch from eslint to oxlint

* Some oxlint-related tweaks

* Iterate

* Partial revert to defer some changes and shrink diff

* Iterate

* Add eslint-plugin-element-call and enable the copyright rule

* Set strictStorePkgContentCheck

* Iterate

* Make sonar happy

* Fix new lints
This commit is contained in:
Michael Telatynski
2026-07-10 09:47:41 +00:00
committed by GitHub
parent 549dd1b7a8
commit 15c85ec50f
176 changed files with 1294 additions and 2876 deletions
@@ -68,7 +68,7 @@ export interface VirtualizedListProps<Item, Context> extends Omit<
/**
* Function to get the key to use for focusing an item.
* @param item - The item to get the key for
* @return The key to use for focusing the item
* @returns The key to use for focusing the item
*/
getItemKey: (item: Item) => string;
@@ -19,7 +19,7 @@
* you will end up with literal "<a>" in your output, rather than HTML. Note that you can also use variable
* substitution to insert React components, but you can't use it to translate text between tags.
*
* @return a React <span> component if any non-strings were used in substitutions, otherwise a string
* @returns a React <span> component if any non-strings were used in substitutions, otherwise a string
*/
import React from "react";
import { KEY_SEPARATOR } from "matrix-web-i18n";
@@ -204,7 +204,7 @@ export function lookupString(key: TranslationKey): string {
* @param {object} variables Variable substitutions, e.g { foo: 'bar' }
* @param {object} tags Tag substitutions e.g. { 'a': (sub) => <a>{sub}</a> }
*
* @return a React <span> component if any non-strings were used in substitutions
* @returns a React <span> component if any non-strings were used in substitutions
* or translation used a fallback locale, otherwise a string
*/
// eslint-next-line @typescript-eslint/naming-convention
@@ -243,7 +243,7 @@ export function sanitizeForTranslation(text: string): string {
* the substitution (e.g. return a React component). In case of a tag replacement, the function receives as
* the argument the text inside the element corresponding to the tag.
*
* @return a React <span> component if any non-strings were used in substitutions, otherwise a string
* @returns a React <span> component if any non-strings were used in substitutions, otherwise a string
*/
export function substitute(text: string, variables?: StringVariables): string;
export function substitute(text: string, variables?: RichVariables): React.ReactNode;
@@ -277,7 +277,7 @@ export function substitute(text: string, variables?: IVariables, tags?: Tags): s
* function which will receive as the argument the capture groups defined in the regexp. E.g.
* { 'Hello (.?) World': (sub) => sub.toUpperCase() }
*
* @return a React <span> component if any non-strings were used in substitutions, otherwise a string
* @returns a React <span> component if any non-strings were used in substitutions, otherwise a string
*/
export function replaceByRegexes(text: string, mapping: IVariables): string;
export function replaceByRegexes(text: string, mapping: Tags): React.ReactNode;
@@ -25,7 +25,7 @@ const Button = (props: HTMLAttributes<HTMLButtonElement>): React.JSX.Element =>
return <button {...props} onFocus={onFocus} tabIndex={isActive ? 0 : -1} ref={ref} />;
};
const checkTabIndexes = (buttons: NodeListOf<HTMLElement>, expectations: number[]): void => {
const expectTabIndexes = (buttons: NodeListOf<HTMLElement>, expectations: number[]): void => {
expect([...buttons].map((b) => b.tabIndex)).toStrictEqual(expectations);
};
@@ -89,16 +89,16 @@ describe("RovingTabIndex", () => {
</RovingTabIndexProvider>,
);
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
expectTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
act(() => container.querySelectorAll("button")[2].focus());
checkTabIndexes(container.querySelectorAll("button"), [-1, -1, 0]);
expectTabIndexes(container.querySelectorAll("button"), [-1, -1, 0]);
act(() => container.querySelectorAll("button")[1].focus());
checkTabIndexes(container.querySelectorAll("button"), [-1, 0, -1]);
expectTabIndexes(container.querySelectorAll("button"), [-1, 0, -1]);
act(() => container.querySelectorAll("button")[1].blur());
checkTabIndexes(container.querySelectorAll("button"), [-1, 0, -1]);
expectTabIndexes(container.querySelectorAll("button"), [-1, 0, -1]);
rerender(
<RovingTabIndexProvider>
@@ -112,7 +112,7 @@ describe("RovingTabIndex", () => {
)}
</RovingTabIndexProvider>,
);
checkTabIndexes(container.querySelectorAll("button"), [-1, -1, 0, -1]);
expectTabIndexes(container.querySelectorAll("button"), [-1, -1, 0, -1]);
rerender(
<RovingTabIndexProvider>
@@ -125,7 +125,7 @@ describe("RovingTabIndex", () => {
)}
</RovingTabIndexProvider>,
);
checkTabIndexes(container.querySelectorAll("button"), [-1, -1, 0]);
expectTabIndexes(container.querySelectorAll("button"), [-1, -1, 0]);
});
it("provides a ref to the dom element", () => {
@@ -165,10 +165,10 @@ describe("RovingTabIndex", () => {
</RovingTabIndexProvider>,
);
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
expectTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
act(() => container.querySelectorAll("button")[2].focus());
checkTabIndexes(container.querySelectorAll("button"), [-1, -1, 0]);
expectTabIndexes(container.querySelectorAll("button"), [-1, -1, 0]);
});
describe("reducer functions as expected", () => {
@@ -390,22 +390,22 @@ describe("RovingTabIndex", () => {
);
act(() => container.querySelectorAll("button")[0].focus());
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
expectTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
await userEvent.keyboard("[ArrowDown]");
checkTabIndexes(container.querySelectorAll("button"), [-1, 0, -1]);
expectTabIndexes(container.querySelectorAll("button"), [-1, 0, -1]);
await userEvent.keyboard("[ArrowDown]");
checkTabIndexes(container.querySelectorAll("button"), [-1, -1, 0]);
expectTabIndexes(container.querySelectorAll("button"), [-1, -1, 0]);
await userEvent.keyboard("[ArrowUp]");
checkTabIndexes(container.querySelectorAll("button"), [-1, 0, -1]);
expectTabIndexes(container.querySelectorAll("button"), [-1, 0, -1]);
await userEvent.keyboard("[ArrowUp]");
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
expectTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
await userEvent.keyboard("[ArrowUp]");
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
expectTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
});
it("handles left/right arrow keys when handleLeftRight=true", async () => {
@@ -420,10 +420,10 @@ describe("RovingTabIndex", () => {
act(() => container.querySelectorAll("button")[0].focus());
await userEvent.keyboard("[ArrowRight]");
checkTabIndexes(container.querySelectorAll("button"), [-1, 0, -1]);
expectTabIndexes(container.querySelectorAll("button"), [-1, 0, -1]);
await userEvent.keyboard("[ArrowLeft]");
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
expectTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
});
it("moves between multiple toolbar containers in one roving group", async () => {
@@ -446,15 +446,15 @@ describe("RovingTabIndex", () => {
const buttons = container.querySelectorAll("button");
act(() => buttons[1].focus());
checkTabIndexes(buttons, [-1, 0, -1, -1]);
expectTabIndexes(buttons, [-1, 0, -1, -1]);
await userEvent.keyboard("[ArrowRight]");
expect(buttons[2]).toHaveFocus();
checkTabIndexes(buttons, [-1, -1, 0, -1]);
expectTabIndexes(buttons, [-1, -1, 0, -1]);
await userEvent.keyboard("[ArrowLeft]");
expect(buttons[1]).toHaveFocus();
checkTabIndexes(buttons, [-1, 0, -1, -1]);
expectTabIndexes(buttons, [-1, 0, -1, -1]);
});
it("handles Home, End, and loop across multiple toolbar containers", async () => {
@@ -477,19 +477,19 @@ describe("RovingTabIndex", () => {
const buttons = container.querySelectorAll("button");
act(() => buttons[2].focus());
checkTabIndexes(buttons, [-1, -1, 0, -1]);
expectTabIndexes(buttons, [-1, -1, 0, -1]);
await userEvent.keyboard("[End]");
expect(buttons[3]).toHaveFocus();
checkTabIndexes(buttons, [-1, -1, -1, 0]);
expectTabIndexes(buttons, [-1, -1, -1, 0]);
await userEvent.keyboard("[ArrowRight]");
expect(buttons[0]).toHaveFocus();
checkTabIndexes(buttons, [0, -1, -1, -1]);
expectTabIndexes(buttons, [0, -1, -1, -1]);
await userEvent.keyboard("[Home]");
expect(buttons[0]).toHaveFocus();
checkTabIndexes(buttons, [0, -1, -1, -1]);
expectTabIndexes(buttons, [0, -1, -1, -1]);
});
it("handles Home and End when handleHomeEnd=true", async () => {
@@ -503,13 +503,13 @@ describe("RovingTabIndex", () => {
);
act(() => container.querySelectorAll("button")[1].focus());
checkTabIndexes(container.querySelectorAll("button"), [-1, 0, -1]);
expectTabIndexes(container.querySelectorAll("button"), [-1, 0, -1]);
await userEvent.keyboard("[End]");
checkTabIndexes(container.querySelectorAll("button"), [-1, -1, 0]);
expectTabIndexes(container.querySelectorAll("button"), [-1, -1, 0]);
await userEvent.keyboard("[Home]");
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
expectTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
});
it("loops when handleLoop=true", async () => {
@@ -524,10 +524,10 @@ describe("RovingTabIndex", () => {
act(() => container.querySelectorAll("button")[2].focus());
await userEvent.keyboard("[ArrowDown]");
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
expectTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
await userEvent.keyboard("[ArrowUp]");
checkTabIndexes(container.querySelectorAll("button"), [-1, -1, 0]);
expectTabIndexes(container.querySelectorAll("button"), [-1, -1, 0]);
});
it("uses a custom getAction mapper", async () => {
@@ -552,7 +552,7 @@ describe("RovingTabIndex", () => {
await userEvent.keyboard("j");
expect(getAction).toHaveBeenCalled();
checkTabIndexes(container.querySelectorAll("button"), [-1, 0, -1]);
expectTabIndexes(container.querySelectorAll("button"), [-1, 0, -1]);
});
it("handles input fields when handleInputFields=true", () => {
@@ -569,7 +569,7 @@ describe("RovingTabIndex", () => {
const input = getByRole("textbox", { name: "Search input" });
fireEvent.keyDown(input, { key: "ArrowDown" });
checkTabIndexes(container.querySelectorAll("button"), [-1, 0]);
expectTabIndexes(container.querySelectorAll("button"), [-1, 0]);
});
it("moves from an input field with Tab when handleInputFields=false", () => {
@@ -586,7 +586,7 @@ describe("RovingTabIndex", () => {
act(() => (input as HTMLElement).focus());
fireEvent.keyDown(input, { key: "Tab" });
checkTabIndexes(container.querySelectorAll("button"), [-1, 0]);
expectTabIndexes(container.querySelectorAll("button"), [-1, 0]);
});
it("stops provider processing when onKeyDown prevents default", () => {
@@ -606,7 +606,7 @@ describe("RovingTabIndex", () => {
fireEvent.keyDown(container.querySelector('[role="toolbar"]')!, { key: "ArrowDown" });
expect(onKeyDown).toHaveBeenCalled();
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
expectTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
});
it("calls scrollIntoView if specified", async () => {
@@ -620,7 +620,7 @@ describe("RovingTabIndex", () => {
);
act(() => container.querySelectorAll("button")[0].focus());
checkTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
expectTabIndexes(container.querySelectorAll("button"), [0, -1, -1]);
const button = container.querySelectorAll("button")[1];
const mock = vi.spyOn(button, "scrollIntoView");
@@ -153,7 +153,7 @@ export interface LinkedTextOptions {
* Generates a linkifyjs options object that is reasonably paired down
* to just the essentials required for an Element client.
*
* @return A `linkifyjs` `Opts` object. Used by `linkifyString` and `linkifyHtml
* @returns A `linkifyjs` `Opts` object. Used by `linkifyString` and `linkifyHtml
* @see {@link linkifyHtml}
* @see {@link linkifyString}
*/
@@ -9,8 +9,12 @@ import React, { type JSX, useEffect } from "react";
import { fn } from "storybook/test";
import type { Meta, StoryObj } from "@storybook/react-vite";
import type { Room } from "./RoomListItemView";
import { RoomListItemView, type RoomListItemViewSnapshot, type RoomListItemViewActions } from "./RoomListItemView";
import {
type Room,
RoomListItemView,
type RoomListItemViewSnapshot,
type RoomListItemViewActions,
} from "./RoomListItemView";
import { useMockedViewModel } from "../../../../core/viewmodel";
import { withViewDocs } from "../../../../../.storybook/withViewDocs";
import { defaultSnapshot } from "./default-snapshot";
@@ -17,9 +17,10 @@ import {
FlatVirtualizedList,
getContainerAccessibleProps,
type VirtualizedListContext,
GroupedVirtualizedList,
type GroupedVirtualizedListProps,
} from "../../core/VirtualizedList";
import type { RoomListViewSnapshot, RoomListViewModel } from "../RoomListView";
import { GroupedVirtualizedList, type GroupedVirtualizedListProps } from "../../core/VirtualizedList";
import { RoomListSectionHeaderView, RoomListStickySectionHeaderView } from "./RoomListSectionHeaderView";
import { RoomListSectionHeaderDragOverlayView } from "./RoomListSectionHeaderDragOverlayView";
import { RoomListItemWrapper } from "./RoomListItemWrapper";
@@ -26,7 +26,7 @@ export interface UploadButtonViewSnapshot {
}
export interface UploadButtonViewActions {
onUploadOptionSelected(type: string): void;
onUploadOptionSelected(this: void, type: string): void;
}
/**
@@ -40,7 +40,7 @@ export interface RoomAvatarEventViewActions {
/**
* Invoked when the user opens the avatar image.
*/
onAvatarClick(): void;
onAvatarClick(this: void): void;
}
export type RoomAvatarEventViewModel = ViewModel<RoomAvatarEventViewSnapshot, RoomAvatarEventViewActions>;
@@ -19,7 +19,6 @@ import {
type ImageBodyViewSnapshot,
} from "./ImageBodyView";
import imageSrc from "../../../../../../static/image-body/install-spinner.png";
import thumbnailSrc from "../../../../../../static/image-body/install-spinner.png";
import animatedGifSrc from "../../../../../../static/image-body/install-spinner.gif";
const demoBlurhash = "LEHV6nWB2yk8pyo0adR*.7kCMdnj";
const imageBodyViewStateOptions = [ImageBodyViewState.ERROR, ImageBodyViewState.HIDDEN, ImageBodyViewState.READY];
@@ -29,6 +28,8 @@ const imageBodyViewPlaceholderOptions = [
ImageBodyViewPlaceholder.BLURHASH,
];
const thumbnailSrc = imageSrc;
type ImageBodyViewProps = ImageBodyViewSnapshot &
ImageBodyViewActions & {
className?: string;
@@ -28,6 +28,8 @@ type SharedRenderOptions = RenderOptions & {
presentation?: Partial<EventPresentation>;
};
const i18nApi = new I18nApi();
const wrapWithTooltipProvider = (Wrapper: RenderOptions["wrapper"], presentation?: Partial<EventPresentation>) => {
return ({ children }: { children: React.ReactNode }) => {
const resolvedPresentation: EventPresentation | undefined = presentation
@@ -43,12 +45,12 @@ const wrapWithTooltipProvider = (Wrapper: RenderOptions["wrapper"], presentation
if (Wrapper) {
return (
<I18nContext.Provider value={new I18nApi()}>
<I18nContext.Provider value={i18nApi}>
<Wrapper>{content}</Wrapper>
</I18nContext.Provider>
);
} else {
return <I18nContext.Provider value={new I18nApi()}>{content}</I18nContext.Provider>;
return <I18nContext.Provider value={i18nApi}>{content}</I18nContext.Provider>;
}
};
};