* fix: Fix tab semantics for Settings page
* fix: Use new tabpanel markup for admin settings
* fix: Remove unused currentTab behavior
* fix: Remove Bootstrap tab JS dependency
* fix: Add tabpanel role to rate limit tab panels
* fix: Fix style of tabs
---------
Co-authored-by: SleeplessOne1917 <abias1122@gmail.com>
Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
+import classNames from "classnames";
import { Component, InfernoNode, linkEvent } from "inferno";
interface TabItem {
key: string;
import { Component, InfernoNode, linkEvent } from "inferno";
interface TabItem {
key: string;
- getNode: () => InfernoNode;
+ getNode: (isSelected: boolean) => InfernoNode;
render() {
return (
<div>
render() {
return (
<div>
- <ul className="nav nav-tabs mb-2">
+ <ul className="nav nav-tabs mb-2" role="tablist">
{this.props.tabs.map(({ key, label }) => (
<li key={key} className="nav-item">
<button
type="button"
{this.props.tabs.map(({ key, label }) => (
<li key={key} className="nav-item">
<button
type="button"
- className={`nav-link btn${
- this.state?.currentTab === key ? " active" : ""
- }`}
+ className={classNames("nav-link", {
+ active: this.state?.currentTab === key,
+ })}
onClick={linkEvent({ ctx: this, tab: key }, handleSwitchTab)}
onClick={linkEvent({ ctx: this, tab: key }, handleSwitchTab)}
+ aria-controls={`${key}-tab-pane`}
+ {...(this.state?.currentTab === key && {
+ ...{
+ "aria-current": "page",
+ "aria-selected": "true",
+ },
+ })}
>
{label}
</button>
</li>
))}
</ul>
>
{label}
</button>
</li>
))}
</ul>
- {this.props.tabs
- .find(tab => tab.key === this.state?.currentTab)
- ?.getNode()}
+ <div className="tab-content">
+ {this.props.tabs.map(({ key, getNode }) => {
+ return getNode(this.state?.currentTab === key);
+ })}
+ </div>
+import classNames from "classnames";
import { Component, linkEvent } from "inferno";
import {
BannedPersonsResponse,
import { Component, linkEvent } from "inferno";
import {
BannedPersonsResponse,
{
key: "site",
label: i18n.t("site"),
{
key: "site",
label: i18n.t("site"),
- getNode: () => (
- <div className="row">
- <div className="col-12 col-md-6">
- <SiteForm
- showLocal={showLocal(this.isoData)}
- allowedInstances={federationData?.allowed}
- blockedInstances={federationData?.blocked}
- onSaveSite={this.handleEditSite}
- siteRes={this.state.siteRes}
- themeList={this.state.themeList}
- loading={this.state.loading}
- />
- </div>
- <div className="col-12 col-md-6">
- {this.admins()}
- {this.bannedUsers()}
+ getNode: isSelected => (
+ <div
+ className={classNames("tab-pane show", {
+ active: isSelected,
+ })}
+ role="tabpanel"
+ id="site-tab-pane"
+ >
+ <div className="row">
+ <div className="col-12 col-md-6">
+ <SiteForm
+ showLocal={showLocal(this.isoData)}
+ allowedInstances={federationData?.allowed}
+ blockedInstances={federationData?.blocked}
+ onSaveSite={this.handleEditSite}
+ siteRes={this.state.siteRes}
+ themeList={this.state.themeList}
+ loading={this.state.loading}
+ />
+ </div>
+ <div className="col-12 col-md-6">
+ {this.admins()}
+ {this.bannedUsers()}
+ </div>
{
key: "rate_limiting",
label: "Rate Limiting",
{
key: "rate_limiting",
label: "Rate Limiting",
- getNode: () => (
- <RateLimitForm
- rateLimits={
- this.state.siteRes.site_view.local_site_rate_limit
- }
- onSaveSite={this.handleEditSite}
- loading={this.state.loading}
- />
+ getNode: isSelected => (
+ <div
+ className={classNames("tab-pane", {
+ active: isSelected,
+ })}
+ role="tabpanel"
+ id="rate_limiting-tab-pane"
+ >
+ <RateLimitForm
+ rateLimits={
+ this.state.siteRes.site_view.local_site_rate_limit
+ }
+ onSaveSite={this.handleEditSite}
+ loading={this.state.loading}
+ />
+ </div>
),
},
{
key: "taglines",
label: i18n.t("taglines"),
),
},
{
key: "taglines",
label: i18n.t("taglines"),
- getNode: () => (
- <div className="row">
- <TaglineForm
- taglines={this.state.siteRes.taglines}
- onSaveSite={this.handleEditSite}
- loading={this.state.loading}
- />
+ getNode: isSelected => (
+ <div
+ className={classNames("tab-pane", {
+ active: isSelected,
+ })}
+ role="tabpanel"
+ id="taglines-tab-pane"
+ >
+ <div className="row">
+ <TaglineForm
+ taglines={this.state.siteRes.taglines}
+ onSaveSite={this.handleEditSite}
+ loading={this.state.loading}
+ />
+ </div>
</div>
),
},
{
key: "emojis",
label: i18n.t("emojis"),
</div>
),
},
{
key: "emojis",
label: i18n.t("emojis"),
- getNode: () => (
- <div className="row">
- <EmojiForm
- onCreate={this.handleCreateEmoji}
- onDelete={this.handleDeleteEmoji}
- onEdit={this.handleEditEmoji}
- loading={this.state.emojiLoading}
- />
+ getNode: isSelected => (
+ <div
+ className={classNames("tab-pane", {
+ active: isSelected,
+ })}
+ role="tabpanel"
+ id="emojis-tab-pane"
+ >
+ <div className="row">
+ <EmojiForm
+ onCreate={this.handleCreateEmoji}
+ onDelete={this.handleDeleteEmoji}
+ onEdit={this.handleEditEmoji}
+ loading={this.state.emojiLoading}
+ />
+ </div>
+import classNames from "classnames";
import { Component, FormEventHandler, linkEvent } from "inferno";
import { EditSite, LocalSiteRateLimit } from "lemmy-js-client";
import { i18n } from "../../i18next";
import { Component, FormEventHandler, linkEvent } from "inferno";
import { EditSite, LocalSiteRateLimit } from "lemmy-js-client";
import { i18n } from "../../i18next";
handleRateLimitPerSecond: FormEventHandler<HTMLInputElement>;
rateLimitValue?: number;
rateLimitPerSecondValue?: number;
handleRateLimitPerSecond: FormEventHandler<HTMLInputElement>;
rateLimitValue?: number;
rateLimitPerSecondValue?: number;
}
interface RateLimitFormProps {
}
interface RateLimitFormProps {
handleRateLimitPerSecond,
rateLimitPerSecondValue,
rateLimitValue,
handleRateLimitPerSecond,
rateLimitPerSecondValue,
rateLimitValue,
}: RateLimitsProps) {
return (
}: RateLimitsProps) {
return (
- <div className="mb-3 row">
+ <div role="tabpanel" className={classNames("mb-3 row", className)}>
<div className="col-md-6">
<label htmlFor="rate-limit">{i18n.t("rate_limit")}</label>
<input
<div className="col-md-6">
<label htmlFor="rate-limit">{i18n.t("rate_limit")}</label>
<input
tabs={rateLimitTypes.map(rateLimitType => ({
key: rateLimitType,
label: i18n.t(`rate_limit_${rateLimitType}`),
tabs={rateLimitTypes.map(rateLimitType => ({
key: rateLimitType,
label: i18n.t(`rate_limit_${rateLimitType}`),
+ getNode: isSelected => (
+ className={classNames("tab-pane show", {
+ active: isSelected,
+ })}
handleRateLimit={linkEvent(
{ rateLimitType, ctx: this },
handleRateLimitChange
handleRateLimit={linkEvent(
{ rateLimitType, ctx: this },
handleRateLimitChange
import { debounce } from "@utils/helpers";
import { debounce } from "@utils/helpers";
+import classNames from "classnames";
import { NoOptionI18nKeys } from "i18next";
import { Component, linkEvent } from "inferno";
import {
import { NoOptionI18nKeys } from "i18next";
import { Component, linkEvent } from "inferno";
import {
+ userSettings(isSelected) {
- <div className="row">
- <div className="col-12 col-md-6">
- <div className="card border-secondary mb-3">
- <div className="card-body">{this.saveUserSettingsHtmlForm()}</div>
+ <div
+ className={classNames("tab-pane show", {
+ active: isSelected,
+ })}
+ role="tabpanel"
+ id="settings-tab-pane"
+ >
+ <div className="row">
+ <div className="col-12 col-md-6">
+ <div className="card border-secondary mb-3">
+ <div className="card-body">{this.saveUserSettingsHtmlForm()}</div>
+ </div>
- </div>
- <div className="col-12 col-md-6">
- <div className="card border-secondary mb-3">
- <div className="card-body">{this.changePasswordHtmlForm()}</div>
+ <div className="col-12 col-md-6">
+ <div className="card border-secondary mb-3">
+ <div className="card-body">{this.changePasswordHtmlForm()}</div>
+ </div>
</div>
</div>
</div>
);
}
</div>
</div>
</div>
);
}
+ blockCards(isSelected) {
- <div className="row">
- <div className="col-12 col-md-6">
- <div className="card border-secondary mb-3">
- <div className="card-body">{this.blockUserCard()}</div>
+ <div
+ className={classNames("tab-pane", {
+ active: isSelected,
+ })}
+ role="tabpanel"
+ id="blocks-tab-pane"
+ >
+ <div className="row">
+ <div className="col-12 col-md-6">
+ <div className="card border-secondary mb-3">
+ <div className="card-body">{this.blockUserCard()}</div>
+ </div>
- </div>
- <div className="col-12 col-md-6">
- <div className="card border-secondary mb-3">
- <div className="card-body">{this.blockCommunityCard()}</div>
+ <div className="col-12 col-md-6">
+ <div className="card border-secondary mb-3">
+ <div className="card-body">{this.blockCommunityCard()}</div>
+ </div>