Enable some more oxlint rules (#34193)
* Enable some more oxlint rules * Iterate * Iterate * Update packages/shared-components/src/room/timeline/event-tile/EventTileView/E2ePadlock/E2ePadlock.tsx Co-authored-by: Hubert Chathi <hubert@uhoreg.ca> --------- Co-authored-by: Hubert Chathi <hubert@uhoreg.ca>
This commit is contained in:
co-authored by
Hubert Chathi
parent
4807ff1568
commit
d54cddcb1d
@@ -41,6 +41,7 @@ export default class NonUrgentToastContainer extends React.PureComponent<EmptyOb
|
||||
public render(): React.ReactNode {
|
||||
const toasts = this.state.toasts.map((t, i) => {
|
||||
return (
|
||||
// oxlint-disable-next-line react/no-array-index-key
|
||||
<div className="mx_NonUrgentToastContainer_toast" key={`toast-${i}`}>
|
||||
{React.createElement(t, {})}
|
||||
</div>
|
||||
|
||||
@@ -43,6 +43,7 @@ export default class Waveform extends React.PureComponent<IProps> {
|
||||
});
|
||||
return (
|
||||
<span
|
||||
// oxlint-disable-next-line react/no-array-index-key
|
||||
key={i}
|
||||
style={
|
||||
{
|
||||
|
||||
@@ -264,34 +264,50 @@ export default class LoginWithQRFlow extends React.Component<Props> {
|
||||
);
|
||||
break;
|
||||
case Phase.ShowingQR: {
|
||||
let steps: ReactNode[];
|
||||
let steps: ReactNode;
|
||||
if (this.props.intent === RendezvousIntent.LOGIN_ON_NEW_DEVICE) {
|
||||
steps = [
|
||||
_t("auth|qr_code_login|open_element_mobile_device", {
|
||||
brand: SdkConfig.get().brand,
|
||||
}),
|
||||
_t("auth|qr_code_login|tap_avatar_link_new_device", {
|
||||
linkNewDevice: <strong>{_t("settings|sessions|sign_in_with_qr")}</strong>,
|
||||
}),
|
||||
_t("auth|qr_code_login|choose_desktop_computer", {
|
||||
desktopComputer: <strong>{_t("auth|qr_code_login|desktop_computer")}</strong>,
|
||||
}),
|
||||
_t("auth|qr_code_login|select_ready_to_scan", {
|
||||
readyToScan: <strong>{_t("auth|qr_code_login|ready_to_scan")}</strong>,
|
||||
}),
|
||||
_t("auth|qr_code_login|follow_remaining_instructions"),
|
||||
];
|
||||
steps = (
|
||||
<>
|
||||
<li>
|
||||
{_t("auth|qr_code_login|open_element_mobile_device", {
|
||||
brand: SdkConfig.get().brand,
|
||||
})}
|
||||
</li>
|
||||
<li>
|
||||
{_t("auth|qr_code_login|tap_avatar_link_new_device", {
|
||||
linkNewDevice: <strong>{_t("settings|sessions|sign_in_with_qr")}</strong>,
|
||||
})}
|
||||
</li>
|
||||
<li>
|
||||
{_t("auth|qr_code_login|choose_desktop_computer", {
|
||||
desktopComputer: <strong>{_t("auth|qr_code_login|desktop_computer")}</strong>,
|
||||
})}
|
||||
</li>
|
||||
<li>
|
||||
{_t("auth|qr_code_login|select_ready_to_scan", {
|
||||
readyToScan: <strong>{_t("auth|qr_code_login|ready_to_scan")}</strong>,
|
||||
})}
|
||||
</li>
|
||||
<li>{_t("auth|qr_code_login|follow_remaining_instructions")}</li>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
steps = [
|
||||
_t("auth|qr_code_login|open_element_other_device", {
|
||||
brand: SdkConfig.get().brand,
|
||||
}),
|
||||
_t("auth|qr_code_login|select_qr_code", {
|
||||
scanQRCode: <strong>{_t("auth|qr_code_login|scan_qr_code")}</strong>,
|
||||
}),
|
||||
_t("auth|qr_code_login|point_the_camera"),
|
||||
_t("auth|qr_code_login|follow_remaining_instructions"),
|
||||
];
|
||||
steps = (
|
||||
<>
|
||||
<li>
|
||||
{_t("auth|qr_code_login|open_element_other_device", {
|
||||
brand: SdkConfig.get().brand,
|
||||
})}
|
||||
</li>
|
||||
<li>
|
||||
{_t("auth|qr_code_login|select_qr_code", {
|
||||
scanQRCode: <strong>{_t("auth|qr_code_login|scan_qr_code")}</strong>,
|
||||
})}
|
||||
</li>
|
||||
<li>{_t("auth|qr_code_login|point_the_camera")}</li>
|
||||
<li>{_t("auth|qr_code_login|follow_remaining_instructions")}</li>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
main = (
|
||||
@@ -308,11 +324,7 @@ export default class LoginWithQRFlow extends React.Component<Props> {
|
||||
<Spinner />
|
||||
)}
|
||||
</div>
|
||||
<ol>
|
||||
{steps.map((step, i) => (
|
||||
<li key={this.props.intent + i}>{step}</li>
|
||||
))}
|
||||
</ol>
|
||||
<ol>{steps}</ol>
|
||||
</>
|
||||
);
|
||||
break;
|
||||
|
||||
@@ -39,7 +39,7 @@ export default class ServerOfflineDialog extends React.PureComponent<IProps> {
|
||||
};
|
||||
|
||||
private renderTimeline(): ReactNode[] {
|
||||
return EchoStore.instance.contexts.map((c, i) => {
|
||||
return EchoStore.instance.contexts.map((c) => {
|
||||
if (!c.firstFailedTime) return null; // not useful
|
||||
if (!(c instanceof RoomEchoContext))
|
||||
throw new Error("Cannot render unknown context: " + c.constructor.name);
|
||||
@@ -51,7 +51,7 @@ export default class ServerOfflineDialog extends React.PureComponent<IProps> {
|
||||
);
|
||||
const entries = c.transactions
|
||||
.filter((t) => t.status === TransactionStatus.Error || t.didPreviouslyFail)
|
||||
.map((t, j) => {
|
||||
.map((t) => {
|
||||
let button = <Spinner size={19} />;
|
||||
if (t.status === TransactionStatus.Error) {
|
||||
button = (
|
||||
@@ -61,14 +61,14 @@ export default class ServerOfflineDialog extends React.PureComponent<IProps> {
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="mx_ServerOfflineDialog_content_context_txn" key={`txn-${j}`}>
|
||||
<div className="mx_ServerOfflineDialog_content_context_txn" key={t.auditName}>
|
||||
<span className="mx_ServerOfflineDialog_content_context_txn_desc">{t.auditName}</span>
|
||||
{button}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<div className="mx_ServerOfflineDialog_content_context" key={`context-${i}`}>
|
||||
<div className="mx_ServerOfflineDialog_content_context" key={c.room.roomId}>
|
||||
<div className="mx_ServerOfflineDialog_content_context_timestamp">
|
||||
{formatTime(c.firstFailedTime, SettingsStore.getValue("showTwelveHourTimestamps"))}
|
||||
</div>
|
||||
|
||||
@@ -92,11 +92,11 @@ export default class WidgetCapabilitiesPromptDialog extends React.PureComponent<
|
||||
// Both are the same type (both timeline or both non-timeline), sort lexicographically
|
||||
return lexicographicCompare(capA, capB);
|
||||
});
|
||||
const checkboxRows = orderedCapabilities.map(([cap, isChecked], i) => {
|
||||
const checkboxRows = orderedCapabilities.map(([cap, isChecked]) => {
|
||||
const text = CapabilityText.for(cap, this.props.widgetKind);
|
||||
|
||||
return (
|
||||
<div className="mx_WidgetCapabilitiesPromptDialog_cap" key={cap + i}>
|
||||
<div className="mx_WidgetCapabilitiesPromptDialog_cap" key={cap}>
|
||||
<StyledCheckbox
|
||||
checked={isChecked}
|
||||
onChange={() => this.onToggle(cap)}
|
||||
|
||||
@@ -215,6 +215,7 @@ export default class PollCreateDialog extends ScrollableBaseModal<IProps, IState
|
||||
/>
|
||||
<h2>{_t("poll|options_heading")}</h2>
|
||||
{this.state.options.map((op, i) => (
|
||||
// oxlint-disable-next-line react/no-array-index-key
|
||||
<div key={`option_${i}`} className="mx_PollCreateDialog_option">
|
||||
<Field
|
||||
id={`pollcreate_option_${i}`}
|
||||
|
||||
@@ -495,6 +495,7 @@ export class ImageBodyBaseInner extends React.Component<ImageBodyBaseProps, ISta
|
||||
<div
|
||||
className="mx_MImageBody_thumbnail_container"
|
||||
style={{ maxHeight, maxWidth, aspectRatio: `${infoWidth}/${infoHeight}` }}
|
||||
// oxlint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
||||
tabIndex={tooltipProps ? 0 : undefined}
|
||||
>
|
||||
{placeholder}
|
||||
|
||||
@@ -347,10 +347,10 @@ export default class AliasSettings extends React.Component<IProps, IState> {
|
||||
<option value="" key="unset">
|
||||
{_t("room_settings|alias_not_specified")}
|
||||
</option>
|
||||
{this.getAliases().map((alias, i) => {
|
||||
{this.getAliases().map((alias) => {
|
||||
if (alias === this.state.canonicalAlias) found = true;
|
||||
return (
|
||||
<option value={alias} key={i}>
|
||||
<option value={alias} key={alias}>
|
||||
{alias}
|
||||
</option>
|
||||
);
|
||||
@@ -414,7 +414,7 @@ export default class AliasSettings extends React.Component<IProps, IState> {
|
||||
)}
|
||||
<datalist id="mx_AliasSettings_altRecommendations">
|
||||
{this.getLocalNonAltAliases().map((alias) => {
|
||||
return <option value={alias} key={alias} />;
|
||||
return <option value={alias} key={alias} aria-label={alias} />;
|
||||
})}
|
||||
</datalist>
|
||||
<EditableAliasesList
|
||||
|
||||
@@ -314,7 +314,11 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
|
||||
});
|
||||
|
||||
return completions.length > 0 ? (
|
||||
<div key={i} className="mx_Autocomplete_ProviderSection" role="presentation">
|
||||
<div
|
||||
key={completionResult.provider.getName()}
|
||||
className="mx_Autocomplete_ProviderSection"
|
||||
role="presentation"
|
||||
>
|
||||
<div className="mx_Autocomplete_provider_name">{completionResult.provider.getName()}</div>
|
||||
{completionResult.provider.renderCompletions(completions)}
|
||||
</div>
|
||||
|
||||
@@ -218,6 +218,7 @@ function Indicators({ count, currentIndex }: IndicatorsProps): JSX.Element {
|
||||
return (
|
||||
<div className="mx_PinnedMessageBanner_Indicators">
|
||||
{Array.from({ length: numberOfIndicators }).map((_, i) => (
|
||||
// oxlint-disable-next-line react/no-array-index-key
|
||||
<Indicator key={i} active={i === index} hidden={isLastCycle && lastCycleIndex <= i} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -655,6 +655,7 @@ class RoomPreviewBar extends React.Component<IProps, IState> {
|
||||
if (!Array.isArray(subTitle)) {
|
||||
subTitle = [subTitle];
|
||||
}
|
||||
// oxlint-disable-next-line react/no-array-index-key
|
||||
subTitleElements = subTitle.map((t, i) => <p key={`subTitle${i}`}>{t}</p>);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import React, { type CSSProperties, memo, type RefObject, type ReactNode } from
|
||||
|
||||
import { useIsExpanded } from "../hooks/useIsExpanded";
|
||||
import { useSelection } from "../hooks/useSelection";
|
||||
import { _t } from "../../../../../i18n";
|
||||
|
||||
const HEIGHT_BREAKING_POINT = 24;
|
||||
|
||||
@@ -38,6 +39,7 @@ export const Editor = memo(function Editor({ disabled, placeholder, leftComponen
|
||||
ref={ref}
|
||||
contentEditable={!disabled}
|
||||
role="textbox"
|
||||
aria-label={_t("a11y|message_composer")}
|
||||
aria-multiline="true"
|
||||
aria-autocomplete="list"
|
||||
aria-haspopup="listbox"
|
||||
|
||||
@@ -124,10 +124,10 @@ const DeviceDetails: React.FC<Props> = ({
|
||||
{!accountManagementEndpoint && (
|
||||
<section className="mx_DeviceDetails_section">
|
||||
<p className="mx_DeviceDetails_sectionHeading">{_t("settings|sessions|details_heading")}</p>
|
||||
{metadata.map(({ heading, values, id }, index) => (
|
||||
{metadata.map(({ heading, values, id }) => (
|
||||
<table
|
||||
className="mx_DeviceDetails_metadataTable"
|
||||
key={index}
|
||||
key={id}
|
||||
data-testid={`device-detail-metadata-${id}`}
|
||||
>
|
||||
{heading && (
|
||||
|
||||
@@ -387,7 +387,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps, RolesR
|
||||
}
|
||||
|
||||
const powerSelectors = Object.keys(powerLevelDescriptors)
|
||||
.map((key, index) => {
|
||||
.map((key) => {
|
||||
const descriptor = powerLevelDescriptors[key];
|
||||
if (isSpaceRoom && descriptor.hideForSpace) {
|
||||
return null;
|
||||
@@ -395,7 +395,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps, RolesR
|
||||
|
||||
const value = parseIntWithDefault(get(plContent, key), descriptor.defaultValue);
|
||||
return (
|
||||
<div key={index} className="">
|
||||
<div key={key}>
|
||||
<PowerSelector
|
||||
label={descriptor.desc}
|
||||
value={value}
|
||||
|
||||
@@ -45,6 +45,7 @@ export const SpaceAvatar: React.FC<Pick<IProps, "avatarUrl" | "avatarDisabled" |
|
||||
if (avatar) {
|
||||
avatarSection = (
|
||||
<React.Fragment>
|
||||
{/* oxlint-disable-next-line jsx-a11y/control-has-associated-label */}
|
||||
<AccessibleButton
|
||||
className="mx_SpaceBasicSettings_avatar"
|
||||
onClick={() => avatarUploadRef.current?.click()}
|
||||
|
||||
@@ -65,8 +65,13 @@ export default class VerificationShowSas extends React.Component<IProps, IState>
|
||||
? _t("encryption|verification|confirm_the_emojis")
|
||||
: _t("encryption|verification|sas_emoji_caption_user");
|
||||
} else if (this.props.sas.decimal) {
|
||||
const numberBlocks = this.props.sas.decimal.map((num, i) => <span key={i}>{num}</span>);
|
||||
sasDisplay = <div className="mx_VerificationShowSas_decimalSas">{numberBlocks}</div>;
|
||||
sasDisplay = (
|
||||
<div className="mx_VerificationShowSas_decimalSas">
|
||||
<span>{this.props.sas.decimal[0]}</span>
|
||||
<span>{this.props.sas.decimal[1]}</span>
|
||||
<span>{this.props.sas.decimal[2]}</span>
|
||||
</div>
|
||||
);
|
||||
sasCaption = this.props.isSelf
|
||||
? _t("encryption|verification|sas_caption_self")
|
||||
: _t("encryption|verification|sas_caption_user");
|
||||
|
||||
@@ -44,8 +44,8 @@ export default class AudioFeedArrayForLegacyCall extends React.Component<IProps,
|
||||
};
|
||||
|
||||
public render(): JSX.Element[] {
|
||||
return this.state.feeds.map((feed, i) => {
|
||||
return <AudioFeed feed={feed} key={i} />;
|
||||
return this.state.feeds.map((feed) => {
|
||||
return <AudioFeed feed={feed} key={feed.deviceId + feed.userId} />;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ export function jsxJoin(array: ReactNode[], joiner?: string | JSX.Element): JSX.
|
||||
return (
|
||||
<>
|
||||
{array.map((element, index) => (
|
||||
// oxlint-disable-next-line react/no-array-index-key
|
||||
<React.Fragment key={index}>
|
||||
{element}
|
||||
{index === array.length - 1 ? null : joiner}
|
||||
|
||||
@@ -16,18 +16,18 @@ describe("KeyboardLandmarkUtils", () => {
|
||||
it("Landmarks are cycled through correctly without an opened room", () => {
|
||||
render(
|
||||
<div>
|
||||
<div tabIndex={0} className="mx_SpaceButton_active" data-testid="mx_SpaceButton_active">
|
||||
<button className="mx_SpaceButton_active" data-testid="mx_SpaceButton_active">
|
||||
SPACE_BUTTON
|
||||
</div>
|
||||
<div tabIndex={0} id="room-list-search-button" data-testid="mx_RoomListSearch_search">
|
||||
</button>
|
||||
<button id="room-list-search-button" data-testid="mx_RoomListSearch_search">
|
||||
ROOM_SEARCH
|
||||
</div>
|
||||
<div tabIndex={0} className="mx_RoomListItemView" data-testid="mx_RoomListItemView">
|
||||
</button>
|
||||
<button className="mx_RoomListItemView" data-testid="mx_RoomListItemView">
|
||||
ROOM_TILE
|
||||
</div>
|
||||
<div tabIndex={0} className="mx_HomePage" data-testid="mx_HomePage">
|
||||
</button>
|
||||
<button className="mx_HomePage" data-testid="mx_HomePage">
|
||||
HOME_PAGE
|
||||
</div>
|
||||
</button>
|
||||
</div>,
|
||||
);
|
||||
// ACTIVE_SPACE_BUTTON <-> ROOM_SEARCH <-> ROOM_LIST <-> HOME <-> ACTIVE_SPACE_BUTTON
|
||||
@@ -69,21 +69,19 @@ describe("KeyboardLandmarkUtils", () => {
|
||||
defaultDispatcher.register(callback);
|
||||
render(
|
||||
<div>
|
||||
<div tabIndex={0} className="mx_SpaceButton_active" data-testid="mx_SpaceButton_active">
|
||||
<button className="mx_SpaceButton_active" data-testid="mx_SpaceButton_active">
|
||||
SPACE_BUTTON
|
||||
</div>
|
||||
<div tabIndex={0} id="room-list-search-button" data-testid="mx_RoomListSearch_search">
|
||||
</button>
|
||||
<button id="room-list-search-button" data-testid="mx_RoomListSearch_search">
|
||||
ROOM_SEARCH
|
||||
</div>
|
||||
<div tabIndex={0} className="mx_RoomListItemView_selected" data-testid="mx_RoomListItemView_selected">
|
||||
</button>
|
||||
<button className="mx_RoomListItemView_selected" data-testid="mx_RoomListItemView_selected">
|
||||
ROOM_TILE
|
||||
</div>
|
||||
<div tabIndex={0} className="mx_Room" data-testid="mx_Room">
|
||||
</button>
|
||||
<button className="mx_Room" data-testid="mx_Room">
|
||||
ROOM
|
||||
<div tabIndex={0} className="mx_MessageComposer">
|
||||
COMPOSER
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx_MessageComposer">COMPOSER</div>
|
||||
</button>
|
||||
</div>,
|
||||
);
|
||||
// ACTIVE_SPACE_BUTTON <-> ROOM_SEARCH <-> ROOM_LIST <-> MESSAGE_COMPOSER <-> ACTIVE_SPACE_BUTTON
|
||||
|
||||
+10
-6
@@ -205,6 +205,14 @@ export default defineConfig({
|
||||
// Prevent unnecessary runtime dependencies between files
|
||||
"typescript/consistent-type-imports": ["error", { fixStyle: "inline-type-imports" }],
|
||||
|
||||
"jsx-a11y/control-has-associated-label": [
|
||||
"error",
|
||||
{
|
||||
labelAttributes: ["label", "value"],
|
||||
depth: 3,
|
||||
},
|
||||
],
|
||||
|
||||
// Disable some perf rules
|
||||
"no-await-in-loop": "off",
|
||||
|
||||
@@ -212,7 +220,7 @@ export default defineConfig({
|
||||
"unicorn/switch-case-braces": "off",
|
||||
"sort-keys": "off",
|
||||
"typescript/require-array-sort-compare": "off",
|
||||
"eslint/no-extra-boolean-cast": "off",
|
||||
"no-extra-boolean-cast": "off",
|
||||
|
||||
// These would be nice to enable at some point
|
||||
"unicorn/prefer-set-has": "off",
|
||||
@@ -232,21 +240,17 @@ export default defineConfig({
|
||||
"typescript/no-implied-eval": "off",
|
||||
"typescript/no-misused-spread": "off",
|
||||
"promise/valid-params": "off",
|
||||
"no-extra-boolean-cast": "off",
|
||||
"react-perf/jsx-no-new-function-as-prop": "off",
|
||||
"react-perf/jsx-no-new-object-as-prop": "off",
|
||||
"react-perf/jsx-no-jsx-as-prop": "off",
|
||||
"jsx-a11y/prefer-tag-over-role": "off",
|
||||
"jsx-a11y/no-autofocus": "off",
|
||||
"react/no-children-prop": "off",
|
||||
"jsx-a11y/no-noninteractive-tabindex": "off",
|
||||
"react-perf/jsx-no-new-array-as-prop": "off",
|
||||
"react/no-did-update-set-state": "off",
|
||||
"react/no-did-mount-set-state": "off",
|
||||
"jsx-a11y/no-static-element-interactions": "off",
|
||||
"jsx-a11y/no-noninteractive-element-interactions": "off",
|
||||
"react/no-array-index-key": "off",
|
||||
"jsx-a11y/control-has-associated-label": "off",
|
||||
"jsx-a11y/media-has-caption": "off",
|
||||
"jsx-a11y/no-noninteractive-element-to-interactive-role": "off",
|
||||
"jsx-a11y/aria-activedescendant-has-tabindex": "off",
|
||||
@@ -522,7 +526,6 @@ export default defineConfig({
|
||||
"typescript/no-empty-object-type": "off",
|
||||
"typescript/unbound-method": "off",
|
||||
"typescript/no-floating-promises": "off",
|
||||
"typescript/no-misused-spread": "off",
|
||||
"vitest/require-mock-type-parameters": "off",
|
||||
"vitest/no-disabled-tests": "off",
|
||||
"vitest/no-conditional-expect": "off",
|
||||
@@ -565,6 +568,7 @@ export default defineConfig({
|
||||
"no-new": "off",
|
||||
"react/iframe-missing-sandbox": "off",
|
||||
"promise/no-promise-in-callback": "off",
|
||||
|
||||
// This would be good to enable in the future
|
||||
"typescript/await-thenable": "off",
|
||||
"promise/no-callback-in-promise": "off",
|
||||
|
||||
@@ -165,6 +165,7 @@ const groupedVariant: ListTestVariant = {
|
||||
<div
|
||||
className="mx_group_header"
|
||||
data-testid={`group-header-${header.id}`}
|
||||
// oxlint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
||||
tabIndex={context.tabIndexKey === header.id ? 0 : -1}
|
||||
onFocus={(e) => onFocus(header, e)}
|
||||
>
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type JSX } from "react";
|
||||
import { type JSX } from "react";
|
||||
import { Text, type HTMLReactParserOptions } from "html-react-parser";
|
||||
|
||||
type Replacer = HTMLReactParserOptions["replace"];
|
||||
@@ -28,7 +28,7 @@ export function applyReplacerOnString(
|
||||
const arr = Array.isArray(input) ? input : [input];
|
||||
return arr.map((item, index): JSX.Element => {
|
||||
if (typeof item === "string") {
|
||||
return <React.Fragment key={index}>{(replacer(new Text(item), 0) as JSX.Element) || item}</React.Fragment>;
|
||||
return (replacer(new Text(item), 0) as JSX.Element) || item;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
@@ -31,6 +31,7 @@ export function SasEmoji({ emoji, className }: Props): JSX.Element {
|
||||
const { language } = useI18n();
|
||||
|
||||
const emojiBlocks = emoji.map((emoji, i) => (
|
||||
// oxlint-disable-next-line react/no-array-index-key
|
||||
<div className={styles.segment} key={i}>
|
||||
<div className={styles.emoji} aria-hidden={true}>
|
||||
{emoji}
|
||||
|
||||
+1
@@ -102,6 +102,7 @@ export const RoomListPrimaryFilters = memo(function RoomListPrimaryFilters({
|
||||
>
|
||||
{visibleFilterIds.map((filterId, index) => (
|
||||
<ChatFilter
|
||||
// oxlint-disable-next-line react/no-array-index-key
|
||||
key={`${filterId}-${index}`}
|
||||
role="option"
|
||||
tabIndex={0}
|
||||
|
||||
+2
@@ -69,6 +69,8 @@ export function E2ePadlock({ icon, title, className }: Readonly<E2ePadlockProps>
|
||||
data-testid="e2e-padlock"
|
||||
className={classNames(styles.e2ePadlock, iconClasses[icon], className)}
|
||||
role="img"
|
||||
// This is only tab indexed to act as a tooltip trigger
|
||||
// oxlint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
||||
tabIndex={0}
|
||||
aria-label={_t("timeline|e2e_state")}
|
||||
>
|
||||
|
||||
+1
@@ -257,6 +257,7 @@ export function FileBodyView({ vm, refIFrame, refLink, className }: Readonly<Fil
|
||||
* We'll use it to learn how the download button
|
||||
* would have been styled if it was rendered inline.
|
||||
* this violates multiple eslint rules so ignore it completely */}
|
||||
{/* oxlint-disable-next-line jsx-a11y/control-has-associated-label */}
|
||||
<Button size="md" kind="secondary" Icon={DownloadIcon} as="a" ref={refLink} />
|
||||
</div>
|
||||
{/*
|
||||
|
||||
Reference in New Issue
Block a user