🔘 a11y: Switch Contrast and File Input Key Events to WCAG (#4536)

* 🔘 a11y: Improve Contrast of Switch/Toggles to WCAG Standard

* refactor: Improve file attachment accessibility in Chat Input component

* refactor: clear input ref value before clicks
This commit is contained in:
Danny Avila 2024-10-24 09:12:49 -04:00 committed by GitHub
parent 655f63714b
commit 2996058fa2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 46 additions and 39 deletions

View file

@ -1,4 +1,4 @@
import React from 'react'; import React, { useRef } from 'react';
import { FileUpload, TooltipAnchor } from '~/components/ui'; import { FileUpload, TooltipAnchor } from '~/components/ui';
import { AttachmentIcon } from '~/components/svg'; import { AttachmentIcon } from '~/components/svg';
import { useLocalize } from '~/hooks'; import { useLocalize } from '~/hooks';
@ -14,19 +14,37 @@ const AttachFile = ({
handleFileChange: (event: React.ChangeEvent<HTMLInputElement>) => void; handleFileChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
}) => { }) => {
const localize = useLocalize(); const localize = useLocalize();
const inputRef = useRef<HTMLInputElement>(null);
const isUploadDisabled = disabled ?? false; const isUploadDisabled = disabled ?? false;
return ( return (
<FileUpload handleFileChange={handleFileChange} className="flex"> <FileUpload ref={inputRef} handleFileChange={handleFileChange}>
<TooltipAnchor <TooltipAnchor
id="audio-recorder" role="button"
id="attach-file"
aria-label={localize('com_sidepanel_attach_files')} aria-label={localize('com_sidepanel_attach_files')}
disabled={isUploadDisabled} disabled={isUploadDisabled}
className={cn( className={cn(
'absolute flex size-[35px] items-center justify-center rounded-full p-1 transition-colors hover:bg-surface-hover', 'absolute flex size-[35px] items-center justify-center rounded-full p-1 transition-colors hover:bg-surface-hover focus:outline-none focus:ring-2 focus:ring-primary focus:ring-opacity-50',
isRTL ? 'bottom-2 right-2' : 'bottom-2 left-1 md:left-2', isRTL ? 'bottom-2 right-2' : 'bottom-2 left-1 md:left-2',
)} )}
description={localize('com_sidepanel_attach_files')} description={localize('com_sidepanel_attach_files')}
onKeyDownCapture={(e) => {
if (!inputRef.current) {
return;
}
if (e.key === 'Enter' || e.key === ' ') {
inputRef.current.value = '';
inputRef.current.click();
}
}}
onClick={() => {
if (!inputRef.current) {
return;
}
inputRef.current.value = '';
inputRef.current.click();
}}
> >
<div className="flex w-full items-center justify-center gap-2"> <div className="flex w-full items-center justify-center gap-2">
<AttachmentIcon /> <AttachmentIcon />

View file

@ -1,43 +1,29 @@
import React, { useRef } from 'react'; import React, { forwardRef } from 'react';
type FileUploadProps = { type FileUploadProps = {
handleFileChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
onClick?: () => void;
className?: string; className?: string;
onClick?: () => void;
children: React.ReactNode; children: React.ReactNode;
handleFileChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
}; };
const FileUpload: React.FC<FileUploadProps> = ({ const FileUpload = forwardRef<HTMLInputElement, FileUploadProps>(
handleFileChange, ({ children, handleFileChange }, ref) => {
children,
onClick,
className = '',
}) => {
const fileInputRef = useRef<HTMLInputElement>(null);
const handleButtonClick = () => {
if (onClick) {
onClick();
}
// necessary to reset the input
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
fileInputRef.current?.click();
};
return ( return (
<div onClick={handleButtonClick} style={{ cursor: 'pointer' }} className={className}> <>
{children} {children}
<input <input
ref={fileInputRef} ref={ref}
multiple multiple
type="file" type="file"
style={{ display: 'none' }} style={{ display: 'none' }}
onChange={handleFileChange} onChange={handleFileChange}
/> />
</div> </>
); );
}; },
);
FileUpload.displayName = 'FileUpload';
export default FileUpload; export default FileUpload;

View file

@ -8,7 +8,7 @@ const Switch = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<SwitchPrimitives.Root <SwitchPrimitives.Root
className={cn( className={cn(
'peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input', 'peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-switch-unchecked',
className, className,
)} )}
{...props} {...props}

View file

@ -88,6 +88,7 @@ html {
--chart-3: 197 37% 24%; --chart-3: 197 37% 24%;
--chart-4: 43 74% 66%; --chart-4: 43 74% 66%;
--chart-5: 27 87% 67%; --chart-5: 27 87% 67%;
--switch-unchecked: 0 0% 58%;
} }
.dark { .dark {
--text-primary: var(--gray-100); --text-primary: var(--gray-100);
@ -137,6 +138,7 @@ html {
--chart-3: 30 80% 55%; --chart-3: 30 80% 55%;
--chart-4: 280 65% 60%; --chart-4: 280 65% 60%;
--chart-5: 340 75% 55%; --chart-5: 340 75% 55%;
--switch-unchecked: 0 0% 40%;
} }
.gizmo { .gizmo {
--text-primary: var(--gizmo-gray-950); --text-primary: var(--gizmo-gray-950);

View file

@ -88,6 +88,7 @@ module.exports = {
/* These are test styles */ /* These are test styles */
border: 'hsl(var(--border))', border: 'hsl(var(--border))',
input: 'hsl(var(--input))', input: 'hsl(var(--input))',
['switch-unchecked']: 'hsl(var(--switch-unchecked))',
ring: 'hsl(var(--ring))', ring: 'hsl(var(--ring))',
background: 'hsl(var(--background))', background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))', foreground: 'hsl(var(--foreground))',