mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
[bugfix] Fixes to tablist, fileinput, checkbox (#4139)
Some fixes to various frontend things: - Fix signup checkbox being height 0 on webkit - closes https://codeberg.org/superseriousbusiness/gotosocial/issues/4136 - Fix wonky file input on chrome and webkit - closes https://codeberg.org/superseriousbusiness/gotosocial/issues/4138 - Make tablist in interaction policies keyboard accessible with proper left/right + focus handling, see https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/tablist_role Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4139 Co-authored-by: tobi <tobi.smethurst@protonmail.com> Co-committed-by: tobi <tobi.smethurst@protonmail.com>
This commit is contained in:
@ -493,9 +493,8 @@ section.with-form {
|
||||
gap: 0.4rem;
|
||||
|
||||
& > input {
|
||||
height: 100%;
|
||||
width: 5%;
|
||||
min-width: 1.2rem;
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
|
@ -122,10 +122,6 @@ export function FileInput({ label, field, ...props }: FileInputProps) {
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
const { onChange, infoComponent } = field;
|
||||
const id = nanoid();
|
||||
const onClick = (e) => {
|
||||
e.preventDefault();
|
||||
ref.current?.click();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="form-field file">
|
||||
@ -133,11 +129,9 @@ export function FileInput({ label, field, ...props }: FileInputProps) {
|
||||
className="label-wrapper"
|
||||
htmlFor={id}
|
||||
tabIndex={0}
|
||||
onClick={onClick}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
onClick(e);
|
||||
ref.current?.click();
|
||||
}
|
||||
}}
|
||||
role="button"
|
||||
|
@ -71,9 +71,6 @@ export default function ImportExportForm({ form, submitParse, parseResult }: Imp
|
||||
}, [exportResult]);
|
||||
|
||||
const importFileRef = useRef<HTMLInputElement>(null);
|
||||
const importFileOnClick = () => {
|
||||
importFileRef.current?.click();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -109,11 +106,9 @@ export default function ImportExportForm({ form, submitParse, parseResult }: Imp
|
||||
<label
|
||||
className={`button with-icon${form.permType.value === undefined || form.permType.value.length === 0 ? " disabled" : ""}`}
|
||||
tabIndex={0}
|
||||
onClick={importFileOnClick}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
importFileOnClick();
|
||||
importFileRef.current?.click();
|
||||
}
|
||||
}}
|
||||
role="button"
|
||||
|
@ -17,7 +17,7 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useMemo } from "react";
|
||||
import React, { forwardRef, useCallback, useMemo, useRef } from "react";
|
||||
import {
|
||||
useDefaultInteractionPoliciesQuery,
|
||||
useResetDefaultInteractionPoliciesMutation,
|
||||
@ -191,57 +191,109 @@ function InteractionPoliciesForm({ defaultPolicies }: InteractionPoliciesFormPro
|
||||
|
||||
// A tablist of tab buttons, one for each visibility.
|
||||
function PolicyPanelsTablist({ selectedVis }: { selectedVis: TextFormInputHook}) {
|
||||
const publicRef = useRef<HTMLButtonElement>(null);
|
||||
const unlistedRef = useRef<HTMLButtonElement>(null);
|
||||
const privateRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
return (
|
||||
<div className="tab-buttons" role="tablist">
|
||||
<Tab
|
||||
thisVisibility="public"
|
||||
label="Public"
|
||||
selectedVis={selectedVis}
|
||||
prevVis="private"
|
||||
thisVis="public"
|
||||
nextVis="unlisted"
|
||||
prevRef={privateRef}
|
||||
thisRef={publicRef}
|
||||
nextRef={unlistedRef}
|
||||
/>
|
||||
<Tab
|
||||
thisVisibility="unlisted"
|
||||
label="Unlisted"
|
||||
selectedVis={selectedVis}
|
||||
prevVis="public"
|
||||
thisVis="unlisted"
|
||||
nextVis="private"
|
||||
prevRef={publicRef}
|
||||
thisRef={unlistedRef}
|
||||
nextRef={privateRef}
|
||||
/>
|
||||
<Tab
|
||||
thisVisibility="private"
|
||||
label="Followers-only"
|
||||
selectedVis={selectedVis}
|
||||
prevVis="unlisted"
|
||||
thisVis="private"
|
||||
nextVis="public"
|
||||
prevRef={unlistedRef}
|
||||
thisRef={privateRef}
|
||||
nextRef={publicRef}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface TabProps {
|
||||
thisVisibility: string;
|
||||
label: string,
|
||||
selectedVis: TextFormInputHook
|
||||
label: string;
|
||||
selectedVis: TextFormInputHook;
|
||||
prevVis: string;
|
||||
thisVis: string;
|
||||
nextVis: string;
|
||||
prevRef: React.RefObject<HTMLButtonElement>;
|
||||
thisRef: React.RefObject<HTMLButtonElement>;
|
||||
nextRef: React.RefObject<HTMLButtonElement>;
|
||||
}
|
||||
|
||||
// One tab in a tablist, corresponding to the given thisVisibility.
|
||||
function Tab({ thisVisibility, label, selectedVis }: TabProps) {
|
||||
const Tab = forwardRef(
|
||||
function Tab({
|
||||
label,
|
||||
selectedVis,
|
||||
prevVis,
|
||||
thisVis,
|
||||
nextVis,
|
||||
prevRef,
|
||||
thisRef,
|
||||
nextRef,
|
||||
}: TabProps) {
|
||||
const selected = useMemo(() => {
|
||||
return selectedVis.value === thisVisibility;
|
||||
}, [selectedVis, thisVisibility]);
|
||||
return selectedVis.value === thisVis;
|
||||
}, [selectedVis, thisVis]);
|
||||
|
||||
return (
|
||||
<button
|
||||
id={`tab-${thisVisibility}`}
|
||||
id={`tab-${thisVis}`}
|
||||
title={label}
|
||||
role="tab"
|
||||
ref={thisRef}
|
||||
className={`tab-button ${selected && "active"}`}
|
||||
onClick={(e) => {
|
||||
// Allow tab to be clicked.
|
||||
e.preventDefault();
|
||||
selectedVis.setter(thisVisibility);
|
||||
selectedVis.setter(thisVis);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
// Allow cycling through
|
||||
// tabs with arrow keys.
|
||||
if (e.key === "ArrowLeft") {
|
||||
// Select and set
|
||||
// focus on previous tab.
|
||||
selectedVis.setter(prevVis);
|
||||
prevRef.current?.focus();
|
||||
} else if (e.key === "ArrowRight") {
|
||||
// Select and set
|
||||
// focus on next tab.
|
||||
selectedVis.setter(nextVis);
|
||||
nextRef.current?.focus();
|
||||
}
|
||||
}}
|
||||
aria-selected={selected}
|
||||
aria-controls={`panel-${thisVisibility}`}
|
||||
aria-controls={`panel-${thisVis}`}
|
||||
tabIndex={selected ? 0 : -1}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
interface PolicyPanelProps {
|
||||
policyForm: PolicyForm;
|
||||
|
Reference in New Issue
Block a user