a11y button (#335)

fixes issue #333
This commit is contained in:
Jing Hua 2023-07-19 20:48:08 -07:00 committed by GitHub
parent 2b0280a479
commit 5b642f043f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 139 additions and 21 deletions

View file

@ -132,6 +132,7 @@ const ApiEndpointSelector = ({
<button <button
className='btn btn-neutral btn-small flex justify-between w-full' className='btn btn-neutral btn-small flex justify-between w-full'
type='button' type='button'
aria-label='expand api menu'
onClick={() => setDropDown((prev) => !prev)} onClick={() => setDropDown((prev) => !prev)}
> >
<span className='truncate'>{_apiEndpoint}</span> <span className='truncate'>{_apiEndpoint}</span>

View file

@ -44,7 +44,11 @@ const CloneChat = React.memo(() => {
}; };
return ( return (
<button className='btn btn-neutral flex gap-1' onClick={cloneChat}> <button
className='btn btn-neutral flex gap-1'
aria-label={t('cloneChat') as string}
onClick={cloneChat}
>
{cloned ? ( {cloned ? (
<> <>
<TickIcon /> {t('cloned')} <TickIcon /> {t('cloned')}

View file

@ -24,6 +24,7 @@ const DownloadChat = React.memo(
<> <>
<button <button
className='btn btn-neutral' className='btn btn-neutral'
aria-label={t('downloadChat') as string}
onClick={() => { onClick={() => {
setIsModalOpen(true); setIsModalOpen(true);
}} }}
@ -39,6 +40,7 @@ const DownloadChat = React.memo(
<div className='p-6 border-b border-gray-200 dark:border-gray-600 flex gap-4'> <div className='p-6 border-b border-gray-200 dark:border-gray-600 flex gap-4'>
<button <button
className='btn btn-neutral gap-2' className='btn btn-neutral gap-2'
aria-label='image'
onClick={async () => { onClick={async () => {
if (saveRef && saveRef.current) { if (saveRef && saveRef.current) {
const imgData = await htmlToImg(saveRef.current); const imgData = await htmlToImg(saveRef.current);
@ -82,6 +84,7 @@ const DownloadChat = React.memo(
</button> */} </button> */}
<button <button
className='btn btn-neutral gap-2' className='btn btn-neutral gap-2'
aria-label='markdown'
onClick={async () => { onClick={async () => {
if (saveRef && saveRef.current) { if (saveRef && saveRef.current) {
const chats = useStore.getState().chats; const chats = useStore.getState().chats;
@ -106,6 +109,7 @@ const DownloadChat = React.memo(
</button> </button>
<button <button
className='btn btn-neutral gap-2' className='btn btn-neutral gap-2'
aria-label='json'
onClick={async () => { onClick={async () => {
const chats = useStore.getState().chats; const chats = useStore.getState().chats;
if (chats) { if (chats) {

View file

@ -38,6 +38,7 @@ const CodeBar = React.memo(
<span className=''>{lang}</span> <span className=''>{lang}</span>
<button <button
className='flex ml-auto gap-2' className='flex ml-auto gap-2'
aria-label='copy codeblock'
onClick={async () => { onClick={async () => {
const codeString = codeRef.current?.textContent; const codeString = codeRef.current?.textContent;
if (codeString) if (codeString)

View file

@ -20,14 +20,13 @@ const CommandPrompt = ({
const [dropDown, setDropDown, dropDownRef] = useHideOnOutsideClick(); const [dropDown, setDropDown, dropDownRef] = useHideOnOutsideClick();
useEffect(() => { useEffect(() => {
if (dropDown && inputRef.current) { if (dropDown && inputRef.current) {
// When dropdown is visible, focus the input // When dropdown is visible, focus the input
inputRef.current.focus(); inputRef.current.focus();
} }
}, [dropDown]); }, [dropDown]);
useEffect(() => { useEffect(() => {
const filteredPrompts = matchSorter(useStore.getState().prompts, input, { const filteredPrompts = matchSorter(useStore.getState().prompts, input, {
keys: ['name'], keys: ['name'],
@ -44,6 +43,7 @@ const CommandPrompt = ({
<div className='relative max-wd-sm' ref={dropDownRef}> <div className='relative max-wd-sm' ref={dropDownRef}>
<button <button
className='btn btn-neutral btn-small' className='btn btn-neutral btn-small'
aria-label='prompt library'
onClick={() => setDropDown(!dropDown)} onClick={() => setDropDown(!dropDown)}
> >
/ /

View file

@ -46,7 +46,11 @@ const NewMessageButton = React.memo(
}; };
return ( return (
<div className='h-0 w-0 relative' key={messageIndex}> <div
className='h-0 w-0 relative'
key={messageIndex}
aria-label='insert message'
>
<div <div
className='absolute top-0 right-0 translate-x-1/2 translate-y-[-50%] text-gray-600 dark:text-white cursor-pointer bg-gray-200 dark:bg-gray-600/80 rounded-full p-1 text-sm hover:bg-gray-300 dark:hover:bg-gray-800/80 transition-bg duration-200' className='absolute top-0 right-0 translate-x-1/2 translate-y-[-50%] text-gray-600 dark:text-white cursor-pointer bg-gray-200 dark:bg-gray-600/80 rounded-full p-1 text-sm hover:bg-gray-300 dark:hover:bg-gray-800/80 transition-bg duration-200'
onClick={addMessage} onClick={addMessage}

View file

@ -28,6 +28,7 @@ const RoleSelector = React.memo(
<div className='prose dark:prose-invert relative'> <div className='prose dark:prose-invert relative'>
<button <button
className='btn btn-neutral btn-small flex gap-1' className='btn btn-neutral btn-small flex gap-1'
aria-label={t(role) as string}
type='button' type='button'
onClick={() => setDropDown((prev) => !prev)} onClick={() => setDropDown((prev) => !prev)}
> >

View file

@ -3,15 +3,18 @@ import React from 'react';
const BaseButton = ({ const BaseButton = ({
onClick, onClick,
icon, icon,
buttonProps,
}: { }: {
onClick: React.MouseEventHandler<HTMLButtonElement>; onClick: React.MouseEventHandler<HTMLButtonElement>;
icon: React.ReactElement; icon: React.ReactElement;
buttonProps?: React.ButtonHTMLAttributes<HTMLButtonElement>;
}) => { }) => {
return ( return (
<div className='text-gray-400 flex self-end lg:self-center justify-center gap-3 md:gap-4 visible'> <div className='text-gray-400 flex self-end lg:self-center justify-center gap-3 md:gap-4 visible'>
<button <button
className='p-1 rounded-md hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200 disabled:dark:hover:text-gray-400 md:invisible md:group-hover:visible' className='p-1 rounded-md hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200 disabled:dark:hover:text-gray-400 md:invisible md:group-hover:visible'
onClick={onClick} onClick={onClick}
{...buttonProps}
> >
{icon} {icon}
</button> </button>

View file

@ -15,6 +15,7 @@ const CopyButton = ({
return ( return (
<BaseButton <BaseButton
icon={isCopied ? <TickIcon /> : <CopyIcon />} icon={isCopied ? <TickIcon /> : <CopyIcon />}
buttonProps={{ 'aria-label': 'copy message' }}
onClick={(e) => { onClick={(e) => {
onClick(e); onClick(e);
setIsCopied(true); setIsCopied(true);

View file

@ -11,7 +11,11 @@ const DeleteButton = memo(
setIsDelete: React.Dispatch<React.SetStateAction<boolean>>; setIsDelete: React.Dispatch<React.SetStateAction<boolean>>;
}) => { }) => {
return ( return (
<BaseButton icon={<DeleteIcon />} onClick={() => setIsDelete(true)} /> <BaseButton
icon={<DeleteIcon />}
buttonProps={{ 'aria-label': 'delete message' }}
onClick={() => setIsDelete(true)}
/>
); );
} }
); );

View file

@ -9,7 +9,13 @@ const DownButton = ({
}: { }: {
onClick: React.MouseEventHandler<HTMLButtonElement>; onClick: React.MouseEventHandler<HTMLButtonElement>;
}) => { }) => {
return <BaseButton icon={<DownChevronArrow />} onClick={onClick} />; return (
<BaseButton
icon={<DownChevronArrow />}
buttonProps={{ 'aria-label': 'shift message down' }}
onClick={onClick}
/>
);
}; };
export default DownButton; export default DownButton;

View file

@ -10,7 +10,13 @@ const EditButton = memo(
}: { }: {
setIsEdit: React.Dispatch<React.SetStateAction<boolean>>; setIsEdit: React.Dispatch<React.SetStateAction<boolean>>;
}) => { }) => {
return <BaseButton icon={<EditIcon2 />} onClick={() => setIsEdit(true)} />; return (
<BaseButton
icon={<EditIcon2 />}
buttonProps={{ 'aria-label': 'edit message' }}
onClick={() => setIsEdit(true)}
/>
);
} }
); );

View file

@ -14,6 +14,7 @@ const MarkdownModeButton = () => {
return ( return (
<BaseButton <BaseButton
icon={markdownMode ? <MarkdownIcon /> : <FileTextIcon />} icon={markdownMode ? <MarkdownIcon /> : <FileTextIcon />}
buttonProps={{ 'aria-label': 'toggle markdown mode' }}
onClick={() => { onClick={() => {
setMarkdownMode(!markdownMode); setMarkdownMode(!markdownMode);
}} }}

View file

@ -9,7 +9,13 @@ const RefreshButton = ({
}: { }: {
onClick: React.MouseEventHandler<HTMLButtonElement>; onClick: React.MouseEventHandler<HTMLButtonElement>;
}) => { }) => {
return <BaseButton icon={<RefreshIcon />} onClick={onClick} />; return (
<BaseButton
icon={<RefreshIcon />}
buttonProps={{ 'aria-label': 'regenerate message' }}
onClick={onClick}
/>
);
}; };
export default RefreshButton; export default RefreshButton;

View file

@ -12,6 +12,7 @@ const UpButton = ({
return ( return (
<BaseButton <BaseButton
icon={<DownChevronArrow className='rotate-180' />} icon={<DownChevronArrow className='rotate-180' />}
buttonProps={{ 'aria-label': 'shift message up' }}
onClick={onClick} onClick={onClick}
/> />
); );

View file

@ -158,11 +158,16 @@ const ContentView = memo(
<> <>
<button <button
className='p-1 hover:text-white' className='p-1 hover:text-white'
aria-label='cancel'
onClick={() => setIsDelete(false)} onClick={() => setIsDelete(false)}
> >
<CrossIcon /> <CrossIcon />
</button> </button>
<button className='p-1 hover:text-white' onClick={handleDelete}> <button
className='p-1 hover:text-white'
aria-label='confirm'
onClick={handleDelete}
>
<TickIcon /> <TickIcon />
</button> </button>
</> </>

View file

@ -189,6 +189,7 @@ const EditViewButtons = memo(
generating ? 'cursor-not-allowed opacity-40' : '' generating ? 'cursor-not-allowed opacity-40' : ''
}`} }`}
onClick={handleSaveAndSubmit} onClick={handleSaveAndSubmit}
aria-label={t('saveAndSubmit') as string}
> >
<div className='flex items-center justify-center gap-2'> <div className='flex items-center justify-center gap-2'>
{t('saveAndSubmit')} {t('saveAndSubmit')}
@ -205,6 +206,7 @@ const EditViewButtons = memo(
: 'btn-primary' : 'btn-primary'
}`} }`}
onClick={handleSave} onClick={handleSave}
aria-label={t('save') as string}
> >
<div className='flex items-center justify-center gap-2'> <div className='flex items-center justify-center gap-2'>
{t('save')} {t('save')}
@ -217,6 +219,7 @@ const EditViewButtons = memo(
onClick={() => { onClick={() => {
!generating && setIsModalOpen(true); !generating && setIsModalOpen(true);
}} }}
aria-label={t('saveAndSubmit') as string}
> >
<div className='flex items-center justify-center gap-2'> <div className='flex items-center justify-center gap-2'>
{t('saveAndSubmit')} {t('saveAndSubmit')}
@ -228,6 +231,7 @@ const EditViewButtons = memo(
<button <button
className='btn relative btn-neutral' className='btn relative btn-neutral'
onClick={() => setIsEdit(false)} onClick={() => setIsEdit(false)}
aria-label={t('cancel') as string}
> >
<div className='flex items-center justify-center gap-2'> <div className='flex items-center justify-center gap-2'>
{t('cancel')} {t('cancel')}

View file

@ -12,6 +12,7 @@ const ScrollToBottomButton = React.memo(() => {
className={`cursor-pointer absolute right-6 bottom-[60px] md:bottom-[60px] z-10 rounded-full border border-gray-200 bg-gray-50 text-gray-600 dark:border-white/10 dark:bg-white/10 dark:text-gray-200 ${ className={`cursor-pointer absolute right-6 bottom-[60px] md:bottom-[60px] z-10 rounded-full border border-gray-200 bg-gray-50 text-gray-600 dark:border-white/10 dark:bg-white/10 dark:text-gray-200 ${
atBottom ? 'hidden' : '' atBottom ? 'hidden' : ''
}`} }`}
aria-label='scroll to bottom'
onClick={scrollToBottom} onClick={scrollToBottom}
> >
<DownArrow /> <DownArrow />

View file

@ -24,7 +24,10 @@ const TextField = () => {
className='m-0 w-full resize-none border-0 bg-transparent p-0 pl-2 pr-7 focus:ring-0 focus-visible:ring-0 dark:bg-transparent md:pl-0' className='m-0 w-full resize-none border-0 bg-transparent p-0 pl-2 pr-7 focus:ring-0 focus-visible:ring-0 dark:bg-transparent md:pl-0'
style={{ maxHeight: '200px', height: '24px', overflowY: 'hidden' }} style={{ maxHeight: '200px', height: '24px', overflowY: 'hidden' }}
></textarea> ></textarea>
<button className='absolute p-1 rounded-md text-gray-500 bottom-1.5 right-1 md:bottom-2.5 md:right-2 hover:bg-gray-100 dark:hover:text-gray-400 dark:hover:bg-gray-900 disabled:hover:bg-transparent dark:disabled:hover:bg-transparent'> <button
className='absolute p-1 rounded-md text-gray-500 bottom-1.5 right-1 md:bottom-2.5 md:right-2 hover:bg-gray-100 dark:hover:text-gray-400 dark:hover:bg-gray-900 disabled:hover:bg-transparent dark:disabled:hover:bg-transparent'
aria-label='submit'
>
<SendIcon /> <SendIcon />
</button> </button>
</div> </div>

View file

@ -20,7 +20,11 @@ const ChatConfigMenu = () => {
const [isModalOpen, setIsModalOpen] = useState<boolean>(false); const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
return ( return (
<div> <div>
<button className='btn btn-neutral' onClick={() => setIsModalOpen(true)}> <button
className='btn btn-neutral'
onClick={() => setIsModalOpen(true)}
aria-label={t('defaultChatConfig') as string}
>
{t('defaultChatConfig')} {t('defaultChatConfig')}
</button> </button>
{isModalOpen && <ChatConfigPopup setIsModalOpen={setIsModalOpen} />} {isModalOpen && <ChatConfigPopup setIsModalOpen={setIsModalOpen} />}

View file

@ -86,6 +86,7 @@ export const ModelSelector = ({
className='btn btn-neutral btn-small flex gap-1' className='btn btn-neutral btn-small flex gap-1'
type='button' type='button'
onClick={() => setDropDown((prev) => !prev)} onClick={() => setDropDown((prev) => !prev)}
aria-label='model'
> >
{_model} {_model}
<DownChevronArrow /> <DownChevronArrow />

View file

@ -52,11 +52,19 @@ const GoogleSyncButton = ({ loginHandler }: { loginHandler?: () => void }) => {
return ( return (
<div className='flex gap-4 flex-wrap justify-center'> <div className='flex gap-4 flex-wrap justify-center'>
<button className='btn btn-primary' onClick={() => login()}> <button
className='btn btn-primary'
onClick={() => login()}
aria-label={t('button.sync') as string}
>
{t('button.sync')} {t('button.sync')}
</button> </button>
{cloudSync && ( {cloudSync && (
<button className='btn btn-neutral' onClick={logout}> <button
className='btn btn-neutral'
onClick={logout}
aria-label={t('button.stop') as string}
>
{t('button.stop')} {t('button.stop')}
</button> </button>
)} )}

View file

@ -26,6 +26,7 @@ const ExportChat = () => {
}; };
downloadFile(fileData, getToday()); downloadFile(fileData, getToday());
}} }}
aria-label={t('export') as string}
> >
{t('export')} {t('export')}
</button> </button>

View file

@ -152,6 +152,7 @@ const ImportChat = () => {
<button <button
className='btn btn-small btn-primary mt-3' className='btn btn-small btn-primary mt-3'
onClick={handleFileUpload} onClick={handleFileUpload}
aria-label={t('import') as string}
> >
{t('import')} {t('import')}
</button> </button>

View file

@ -68,6 +68,7 @@ const ImportChatOpenAI = ({
<button <button
className='btn btn-small btn-primary mt-3' className='btn btn-small btn-primary mt-3'
onClick={handleFileUpload} onClick={handleFileUpload}
aria-label={t('import') as string}
> >
{t('import')} {t('import')}
</button> </button>

View file

@ -14,6 +14,7 @@ const LanguageSelector = () => {
className='btn btn-neutral btn-small w-36 flex justify-between' className='btn btn-neutral btn-small w-36 flex justify-between'
type='button' type='button'
onClick={() => setDropDown((prev) => !prev)} onClick={() => setDropDown((prev) => !prev)}
aria-label='language selector'
> >
{languageCodeToName[i18n.language as keyof typeof languageCodeToName] ?? {languageCodeToName[i18n.language as keyof typeof languageCodeToName] ??
i18n.language} i18n.language}

View file

@ -211,10 +211,18 @@ const ChatFolder = ({
> >
{isDelete || isEdit ? ( {isDelete || isEdit ? (
<> <>
<button className='p-1 hover:text-white' onClick={handleTick}> <button
className='p-1 hover:text-white'
onClick={handleTick}
aria-label='confirm'
>
<TickIcon /> <TickIcon />
</button> </button>
<button className='p-1 hover:text-white' onClick={handleCross}> <button
className='p-1 hover:text-white'
onClick={handleCross}
aria-label='cancel'
>
<CrossIcon /> <CrossIcon />
</button> </button>
</> </>
@ -229,6 +237,7 @@ const ChatFolder = ({
onClick={() => { onClick={() => {
setShowPalette((prev) => !prev); setShowPalette((prev) => !prev);
}} }}
aria-label='folder color'
> >
<ColorPaletteIcon /> <ColorPaletteIcon />
</button> </button>
@ -243,12 +252,14 @@ const ChatFolder = ({
onClick={() => { onClick={() => {
updateColor(c); updateColor(c);
}} }}
aria-label={c}
/> />
))} ))}
<button <button
onClick={() => { onClick={() => {
updateColor(); updateColor();
}} }}
aria-label='default color'
> >
<RefreshIcon /> <RefreshIcon />
</button> </button>
@ -260,16 +271,22 @@ const ChatFolder = ({
<button <button
className='p-1 hover:text-white md:hidden group-hover/folder:md:inline' className='p-1 hover:text-white md:hidden group-hover/folder:md:inline'
onClick={() => setIsEdit(true)} onClick={() => setIsEdit(true)}
aria-label='edit folder title'
> >
<EditIcon /> <EditIcon />
</button> </button>
<button <button
className='p-1 hover:text-white md:hidden group-hover/folder:md:inline' className='p-1 hover:text-white md:hidden group-hover/folder:md:inline'
onClick={() => setIsDelete(true)} onClick={() => setIsDelete(true)}
aria-label='delete folder'
> >
<DeleteIcon /> <DeleteIcon />
</button> </button>
<button className='p-1 hover:text-white' onClick={toggleExpanded}> <button
className='p-1 hover:text-white'
onClick={toggleExpanded}
aria-label='expand folder'
>
<DownChevronArrow <DownChevronArrow
className={`${ className={`${
isExpanded ? 'rotate-180' : '' isExpanded ? 'rotate-180' : ''

View file

@ -131,10 +131,18 @@ const ChatHistory = React.memo(
<div className='absolute flex right-1 z-10 text-gray-300 visible'> <div className='absolute flex right-1 z-10 text-gray-300 visible'>
{isDelete || isEdit ? ( {isDelete || isEdit ? (
<> <>
<button className='p-1 hover:text-white' onClick={handleTick}> <button
className='p-1 hover:text-white'
onClick={handleTick}
aria-label='confirm'
>
<TickIcon /> <TickIcon />
</button> </button>
<button className='p-1 hover:text-white' onClick={handleCross}> <button
className='p-1 hover:text-white'
onClick={handleCross}
aria-label='cancel'
>
<CrossIcon /> <CrossIcon />
</button> </button>
</> </>
@ -143,12 +151,14 @@ const ChatHistory = React.memo(
<button <button
className='p-1 hover:text-white' className='p-1 hover:text-white'
onClick={() => setIsEdit(true)} onClick={() => setIsEdit(true)}
aria-label='edit chat title'
> >
<EditIcon /> <EditIcon />
</button> </button>
<button <button
className='p-1 hover:text-white' className='p-1 hover:text-white'
onClick={() => setIsDelete(true)} onClick={() => setIsDelete(true)}
aria-label='delete chat'
> >
<DeleteIcon /> <DeleteIcon />
</button> </button>

View file

@ -21,10 +21,12 @@ const ClearConversation = () => {
return ( return (
<> <>
<button className='btn btn-neutral' <button
className='btn btn-neutral'
onClick={() => { onClick={() => {
setIsModalOpen(true); setIsModalOpen(true);
}} }}
aria-label={t('clearConversation') as string}
> >
<DeleteIcon /> <DeleteIcon />
{t('clearConversation')} {t('clearConversation')}

View file

@ -29,6 +29,7 @@ const ThemeSwitcher = () => {
<button <button
className='items-center gap-3 btn btn-neutral' className='items-center gap-3 btn btn-neutral'
onClick={switchTheme} onClick={switchTheme}
aria-label='toggle dark/light mode'
> >
{theme === 'dark' ? <SunIcon /> : <MoonIcon />} {theme === 'dark' ? <SunIcon /> : <MoonIcon />}
{t(getOppositeTheme(theme) + 'Mode')} {t(getOppositeTheme(theme) + 'Mode')}

View file

@ -27,6 +27,7 @@ const MobileBar = () => {
onClick={() => { onClick={() => {
setHideSideMenu(false); setHideSideMenu(false);
}} }}
aria-label='open sidebar'
> >
<span className='sr-only'>Open sidebar</span> <span className='sr-only'>Open sidebar</span>
<MenuIcon /> <MenuIcon />
@ -44,6 +45,7 @@ const MobileBar = () => {
onClick={() => { onClick={() => {
if (!generating) addChat(); if (!generating) addChat();
}} }}
aria-label='new chat'
> >
<PlusIcon className='h-6 w-6' /> <PlusIcon className='h-6 w-6' />
</button> </button>

View file

@ -49,6 +49,7 @@ const PopupModal = ({
type='button' type='button'
className='text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white' className='text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white'
onClick={_handleClose} onClick={_handleClose}
aria-label='close modal'
> >
<CrossIcon2 /> <CrossIcon2 />
</button> </button>
@ -70,6 +71,7 @@ const PopupModal = ({
type='button' type='button'
className='btn btn-primary' className='btn btn-primary'
onClick={handleConfirm} onClick={handleConfirm}
aria-label='confirm'
> >
{t('confirm')} {t('confirm')}
</button> </button>
@ -79,6 +81,7 @@ const PopupModal = ({
type='button' type='button'
className='btn btn-neutral' className='btn btn-neutral'
onClick={_handleClose} onClick={_handleClose}
aria-label='cancel'
> >
{t('cancel')} {t('cancel')}
</button> </button>

View file

@ -17,6 +17,7 @@ const ExportPrompt = () => {
onClick={() => { onClick={() => {
exportPrompts(prompts); exportPrompts(prompts);
}} }}
aria-label={t('export') as string}
> >
{t('export')} {t('export')}
</button> </button>

View file

@ -63,6 +63,7 @@ const ImportPrompt = () => {
<button <button
className='btn btn-small btn-primary mt-3' className='btn btn-small btn-primary mt-3'
onClick={handleFileUpload} onClick={handleFileUpload}
aria-label={t('import') as string}
> >
{t('import')} {t('import')}
</button> </button>

View file

@ -15,7 +15,11 @@ const PromptLibraryMenu = () => {
const [isModalOpen, setIsModalOpen] = useState<boolean>(false); const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
return ( return (
<div> <div>
<button className='btn btn-neutral' onClick={() => setIsModalOpen(true)}> <button
className='btn btn-neutral'
onClick={() => setIsModalOpen(true)}
aria-label={t('promptLibrary') as string}
>
{t('promptLibrary')} {t('promptLibrary')}
</button> </button>
{isModalOpen && ( {isModalOpen && (

View file

@ -45,6 +45,7 @@ const ShareGPT = React.memo(() => {
onClick={() => { onClick={() => {
setIsModalOpen(true); setIsModalOpen(true);
}} }}
aria-label={t('postOnShareGPT.title') as string}
> >
{t('postOnShareGPT.title')} {t('postOnShareGPT.title')}
</button> </button>

View file

@ -10,7 +10,10 @@ const StopGeneratingButton = () => {
className='absolute bottom-6 left-0 right-0 m-auto flex md:w-full md:m-auto gap-0 md:gap-2 justify-center' className='absolute bottom-6 left-0 right-0 m-auto flex md:w-full md:m-auto gap-0 md:gap-2 justify-center'
onClick={() => setGenerating(false)} onClick={() => setGenerating(false)}
> >
<button className='btn relative btn-neutral border-0 md:border'> <button
className='btn relative btn-neutral border-0 md:border'
aria-label='stop generating'
>
<div className='flex w-full items-center justify-center gap-2'> <div className='flex w-full items-center justify-center gap-2'>
<svg <svg
stroke='currentColor' stroke='currentColor'