mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-11-29 16:43:16 +00:00
(fix:pa11y) aria-labels, alt text and contrast^C
This commit is contained in:
@@ -37,12 +37,14 @@ export default function Hero({
|
||||
<Fragment key={key}>
|
||||
<button
|
||||
onClick={() => handleQuestion({ question: demo.query })}
|
||||
className="w-full rounded-full border border-silver px-6 py-4 text-left hover:border-gray-4000 dark:hover:border-gray-3000 xl:min-w-[24vw]"
|
||||
className="w-full rounded-full border border-silver px-6 py-4 text-left hover:border-gray-4000 dark:hover:border-gray-3000 xl:min-w-[24vw] bg-white dark:bg-raisin-black focus:outline-none focus:ring-2 focus:ring-purple-taupe"
|
||||
>
|
||||
<p className="mb-1 font-semibold text-black dark:text-silver">
|
||||
<p className="mb-1 font-semibold text-black-1000 dark:text-bright-gray">
|
||||
{demo.header}
|
||||
</p>
|
||||
<span className="text-gray-400">{demo.query}</span>
|
||||
<span className="text-gray-700 dark:text-gray-300">
|
||||
{demo.query}
|
||||
</span>
|
||||
</button>
|
||||
</Fragment>
|
||||
),
|
||||
|
||||
@@ -21,11 +21,10 @@ import {
|
||||
handleAbort,
|
||||
} from './conversation/conversationSlice';
|
||||
import ConversationTile from './conversation/ConversationTile';
|
||||
import { useDarkTheme, useMediaQuery, useOutsideAlerter } from './hooks';
|
||||
import { useDarkTheme, useMediaQuery } from './hooks';
|
||||
import useDefaultDocument from './hooks/useDefaultDocument';
|
||||
import DeleteConvModal from './modals/DeleteConvModal';
|
||||
import { ActiveState, Doc } from './models/misc';
|
||||
import APIKeyModal from './preferences/APIKeyModal';
|
||||
import { getConversations, getDocs } from './preferences/preferenceApi';
|
||||
import {
|
||||
selectApiKeyStatus,
|
||||
@@ -68,8 +67,6 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
const [isDocsListOpen, setIsDocsListOpen] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const isApiKeySet = useSelector(selectApiKeyStatus);
|
||||
const [apiKeyModalState, setApiKeyModalState] =
|
||||
useState<ActiveState>('INACTIVE');
|
||||
|
||||
const [uploadModalState, setUploadModalState] =
|
||||
useState<ActiveState>('INACTIVE');
|
||||
@@ -192,12 +189,6 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
useOutsideAlerter(navRef, () => {
|
||||
if (isMobile && navOpen && apiKeyModalState === 'INACTIVE') {
|
||||
setNavOpen(false);
|
||||
setIsDocsListOpen(false);
|
||||
}
|
||||
}, [navOpen, isDocsListOpen, apiKeyModalState]);
|
||||
|
||||
/*
|
||||
Needed to fix bug where if mobile nav was closed and then window was resized to desktop, nav would still be closed but the button to open would be gone, as per #1 on issue #146
|
||||
@@ -220,7 +211,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
>
|
||||
<img
|
||||
src={Expand}
|
||||
alt="menu toggle"
|
||||
alt="Toggle navigation menu"
|
||||
className={`${
|
||||
!navOpen ? 'rotate-180' : 'rotate-0'
|
||||
} m-auto transition-all duration-200`}
|
||||
@@ -234,7 +225,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
>
|
||||
<img
|
||||
src={openNewChat}
|
||||
alt="open new chat icon"
|
||||
alt="Start new chat"
|
||||
className="cursor-pointer"
|
||||
/>
|
||||
</button>
|
||||
@@ -263,7 +254,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
}}
|
||||
>
|
||||
<a href="/" className="flex gap-1.5">
|
||||
<img className="mb-2 h-10" src={DocsGPT3} alt="" />
|
||||
<img className="mb-2 h-10" src={DocsGPT3} alt="DocsGPT Logo" />
|
||||
<p className="my-auto text-2xl font-semibold">DocsGPT</p>
|
||||
</a>
|
||||
</div>
|
||||
@@ -275,7 +266,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
>
|
||||
<img
|
||||
src={Expand}
|
||||
alt="menu toggle"
|
||||
alt="Toggle navigation menu"
|
||||
className={`${
|
||||
!navOpen ? 'rotate-180' : 'rotate-0'
|
||||
} m-auto transition-all duration-200`}
|
||||
@@ -298,7 +289,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
>
|
||||
<img
|
||||
src={Add}
|
||||
alt="new"
|
||||
alt="Create new chat"
|
||||
className="opacity-80 group-hover:opacity-100"
|
||||
/>
|
||||
<p className=" text-sm text-dove-gray group-hover:text-neutral-600 dark:text-chinese-silver dark:group-hover:text-bright-gray">
|
||||
@@ -314,7 +305,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
<img
|
||||
src={isDarkTheme ? SpinnerDark : Spinner}
|
||||
className="animate-spin cursor-pointer bg-transparent"
|
||||
alt="Loading..."
|
||||
alt="Loading conversations"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -365,6 +356,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
<img
|
||||
className="mt-2 h-9 w-9 hover:cursor-pointer"
|
||||
src={UploadIcon}
|
||||
alt="Upload document"
|
||||
onClick={() => {
|
||||
setUploadModalState('ACTIVE');
|
||||
if (isMobile) {
|
||||
@@ -392,7 +384,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
>
|
||||
<img
|
||||
src={SettingGear}
|
||||
alt="icon"
|
||||
alt="Settings"
|
||||
className="ml-2 w-5 filter dark:invert"
|
||||
/>
|
||||
<p className="my-auto text-sm text-eerie-black dark:text-white">
|
||||
@@ -414,7 +406,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
>
|
||||
<img
|
||||
src={Discord}
|
||||
alt="discord"
|
||||
alt="Join Discord community"
|
||||
className="m-2 w-6 self-center filter dark:invert"
|
||||
/>
|
||||
</NavLink>
|
||||
@@ -427,7 +419,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
>
|
||||
<img
|
||||
src={Twitter}
|
||||
alt="x"
|
||||
alt="Follow us on Twitter"
|
||||
className="m-2 w-5 self-center filter dark:invert"
|
||||
/>
|
||||
</NavLink>
|
||||
@@ -440,7 +432,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
>
|
||||
<img
|
||||
src={Github}
|
||||
alt="github"
|
||||
alt="View on GitHub"
|
||||
className="m-2 w-6 self-center filter dark:invert"
|
||||
/>
|
||||
</NavLink>
|
||||
@@ -457,18 +449,13 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
>
|
||||
<img
|
||||
src={Hamburger}
|
||||
alt="menu toggle"
|
||||
alt="Toggle mobile menu"
|
||||
className="w-7 filter dark:invert"
|
||||
/>
|
||||
</button>
|
||||
<div className="text-[#949494] font-medium text-[20px]">DocsGPT</div>
|
||||
</div>
|
||||
</div>
|
||||
<APIKeyModal
|
||||
modalState={apiKeyModalState}
|
||||
setModalState={setApiKeyModalState}
|
||||
isCancellable={isApiKeySet}
|
||||
/>
|
||||
<DeleteConvModal
|
||||
modalState={modalStateDeleteConv}
|
||||
setModalState={setModalStateDeleteConv}
|
||||
|
||||
@@ -59,7 +59,8 @@ const SettingsBar = ({ setActiveTab, activeTab }: SettingsBarProps) => {
|
||||
<div className="md:hidden z-10">
|
||||
<button
|
||||
onClick={() => scrollTabs(-1)}
|
||||
className="flex h-6 w-6 items-center rounded-full justify-center transition-all hover:bg-gray-100"
|
||||
className="flex h-6 w-6 items-center rounded-full justify-center transition-all hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
aria-label="Scroll tabs left"
|
||||
>
|
||||
<img src={ArrowLeft} alt="left-arrow" className="h-3" />
|
||||
</button>
|
||||
@@ -67,16 +68,22 @@ const SettingsBar = ({ setActiveTab, activeTab }: SettingsBarProps) => {
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="flex flex-nowrap overflow-x-auto no-scrollbar md:space-x-4 scroll-smooth snap-x"
|
||||
role="tablist"
|
||||
aria-label="Settings tabs"
|
||||
>
|
||||
{tabs.map((tab, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => setActiveTab(tab)}
|
||||
className={`snap-start h-9 rounded-3xl px-4 font-bold hover:text-neutral-600 dark:hover:text-white/60 ${
|
||||
className={`snap-start h-9 rounded-3xl px-4 font-bold transition-colors ${
|
||||
activeTab === tab
|
||||
? 'bg-neutral-100 text-neutral-600 dark:bg-dark-charcoal dark:text-white/60'
|
||||
: 'text-gray-6000'
|
||||
? 'bg-neutral-200 text-neutral-900 dark:bg-dark-charcoal dark:text-white'
|
||||
: 'text-neutral-700 hover:text-neutral-900 dark:text-neutral-400 dark:hover:text-white'
|
||||
}`}
|
||||
role="tab"
|
||||
aria-selected={activeTab === tab}
|
||||
aria-controls={`${tab.toLowerCase()}-panel`}
|
||||
id={`${tab.toLowerCase()}-tab`}
|
||||
>
|
||||
{tab}
|
||||
</button>
|
||||
@@ -85,7 +92,8 @@ const SettingsBar = ({ setActiveTab, activeTab }: SettingsBarProps) => {
|
||||
<div className="md:hidden z-10">
|
||||
<button
|
||||
onClick={() => scrollTabs(1)}
|
||||
className="flex h-6 w-6 rounded-full items-center justify-center hover:bg-gray-100"
|
||||
className="flex h-6 w-6 rounded-full items-center justify-center hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
aria-label="Scroll tabs right"
|
||||
>
|
||||
<img src={ArrowRight} alt="right-arrow" className="h-3" />
|
||||
</button>
|
||||
|
||||
@@ -386,9 +386,15 @@ export default function Conversation() {
|
||||
{...getRootProps()}
|
||||
className="flex w-full items-center rounded-[40px] border border-silver bg-white dark:bg-raisin-black"
|
||||
>
|
||||
<input {...getInputProps()}></input>
|
||||
<label htmlFor="file-upload" className="sr-only">
|
||||
{t('modals.uploadDoc.upload')}
|
||||
</label>
|
||||
<input {...getInputProps()} id="file-upload" />
|
||||
<label htmlFor="message-input" className="sr-only">
|
||||
{t('inputPlaceholder')}
|
||||
</label>
|
||||
<textarea
|
||||
id="inputbox"
|
||||
id="message-input"
|
||||
ref={inputRef}
|
||||
tabIndex={1}
|
||||
placeholder={t('inputPlaceholder')}
|
||||
@@ -400,19 +406,27 @@ export default function Conversation() {
|
||||
handleQuestionSubmission();
|
||||
}
|
||||
}}
|
||||
aria-label={t('inputPlaceholder')}
|
||||
></textarea>
|
||||
{status === 'loading' ? (
|
||||
<img
|
||||
src={isDarkTheme ? SpinnerDark : Spinner}
|
||||
className="relative right-[38px] bottom-[24px] -mr-[30px] animate-spin cursor-pointer self-end bg-transparent"
|
||||
></img>
|
||||
alt={t('loading')}
|
||||
/>
|
||||
) : (
|
||||
<div className="mx-1 cursor-pointer rounded-full p-3 text-center hover:bg-gray-3000 dark:hover:bg-dark-charcoal">
|
||||
<img
|
||||
className="ml-[4px] h-6 w-6 text-white "
|
||||
<button
|
||||
onClick={() => handleQuestionSubmission()}
|
||||
src={isDarkTheme ? SendDark : Send}
|
||||
></img>
|
||||
aria-label={t('send')}
|
||||
className="flex items-center justify-center"
|
||||
>
|
||||
<img
|
||||
className="ml-[4px] h-6 w-6 text-white"
|
||||
src={isDarkTheme ? SendDark : Send}
|
||||
alt={t('send')}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -166,6 +166,8 @@
|
||||
"copied": "Copied",
|
||||
"speak": "Speak",
|
||||
"answer": "Answer",
|
||||
"send": "Send message",
|
||||
"loading": "Loading response",
|
||||
"edit": {
|
||||
"placeholder": "Type the updated query..."
|
||||
},
|
||||
|
||||
@@ -29,8 +29,9 @@ function AddPrompt({
|
||||
setNewPromptName('');
|
||||
setNewPromptContent('');
|
||||
}}
|
||||
aria-label="Close add prompt modal"
|
||||
>
|
||||
<img className="filter dark:invert" src={Exit} />
|
||||
<img className="filter dark:invert" src={Exit} alt="Close modal" />
|
||||
</button>
|
||||
<div className="p-8">
|
||||
<p className="mb-1 text-xl text-jet dark:text-bright-gray">
|
||||
@@ -40,7 +41,11 @@ function AddPrompt({
|
||||
Add your custom prompt and save it to DocsGPT
|
||||
</p>
|
||||
<div>
|
||||
<label htmlFor="new-prompt-name" className="sr-only">
|
||||
Prompt Name
|
||||
</label>
|
||||
<Input
|
||||
id="new-prompt-name"
|
||||
placeholder="Prompt Name"
|
||||
type="text"
|
||||
className="h-10 rounded-lg"
|
||||
@@ -57,10 +62,15 @@ function AddPrompt({
|
||||
Prompt Text
|
||||
</span>
|
||||
</div>
|
||||
<label htmlFor="new-prompt-content" className="sr-only">
|
||||
Prompt Text
|
||||
</label>
|
||||
<textarea
|
||||
id="new-prompt-content"
|
||||
className="h-56 w-full rounded-lg border-2 border-silver px-3 py-2 outline-none dark:border-silver/40 dark:bg-transparent dark:text-white"
|
||||
value={newPromptContent}
|
||||
onChange={(e) => setNewPromptContent(e.target.value)}
|
||||
aria-label="Prompt Text"
|
||||
></textarea>
|
||||
</div>
|
||||
<div className="mt-6 flex flex-row-reverse">
|
||||
@@ -104,8 +114,9 @@ function EditPrompt({
|
||||
onClick={() => {
|
||||
setModalState('INACTIVE');
|
||||
}}
|
||||
aria-label="Close edit prompt modal"
|
||||
>
|
||||
<img className="filter dark:invert" src={Exit} />
|
||||
<img className="filter dark:invert" src={Exit} alt="Close modal" />
|
||||
</button>
|
||||
<div className="p-8">
|
||||
<p className="mb-1 text-xl text-jet dark:text-bright-gray">
|
||||
@@ -115,13 +126,17 @@ function EditPrompt({
|
||||
Edit your custom prompt and save it to DocsGPT
|
||||
</p>
|
||||
<div>
|
||||
<label htmlFor="edit-prompt-name" className="sr-only">
|
||||
Prompt Name
|
||||
</label>
|
||||
<Input
|
||||
id="edit-prompt-name"
|
||||
placeholder="Prompt Name"
|
||||
type="text"
|
||||
className="h-10 rounded-lg"
|
||||
value={editPromptName}
|
||||
onChange={(e) => setEditPromptName(e.target.value)}
|
||||
></Input>
|
||||
/>
|
||||
<div className="relative bottom-12 left-3 mt-[-3.00px]">
|
||||
<span className="bg-white px-1 text-xs text-silver dark:bg-outer-space dark:text-silver">
|
||||
Prompt Name
|
||||
@@ -132,10 +147,15 @@ function EditPrompt({
|
||||
Prompt Text
|
||||
</span>
|
||||
</div>
|
||||
<label htmlFor="edit-prompt-content" className="sr-only">
|
||||
Prompt Text
|
||||
</label>
|
||||
<textarea
|
||||
id="edit-prompt-content"
|
||||
className="h-56 w-full rounded-lg border-2 border-silver px-3 py-2 outline-none dark:border-silver/40 dark:bg-transparent dark:text-white"
|
||||
value={editPromptContent}
|
||||
onChange={(e) => setEditPromptContent(e.target.value)}
|
||||
aria-label="Prompt Text"
|
||||
></textarea>
|
||||
</div>
|
||||
<div className="mt-6 flex flex-row-reverse gap-4">
|
||||
|
||||
@@ -115,12 +115,20 @@ export default function APIKeys() {
|
||||
<table className="min-w-full divide-y divide-silver dark:divide-silver/40 ">
|
||||
<thead>
|
||||
<tr className="text-start text-sm font-medium text-gray-700 dark:text-gray-50 uppercase">
|
||||
<th className="p-2">{t('settings.apiKeys.name')}</th>
|
||||
<th className="p-2">
|
||||
<th scope="col" className="p-2">
|
||||
{t('settings.apiKeys.name')}
|
||||
</th>
|
||||
<th scope="col" className="p-2">
|
||||
{t('settings.apiKeys.sourceDoc')}
|
||||
</th>
|
||||
<th className="p-2">{t('settings.apiKeys.key')}</th>
|
||||
<th></th>
|
||||
<th scope="col" className="p-2">
|
||||
{t('settings.apiKeys.key')}
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
className="p-2"
|
||||
aria-label="Actions"
|
||||
></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200 dark:divide-neutral-700">
|
||||
@@ -146,7 +154,7 @@ export default function APIKeys() {
|
||||
<td>
|
||||
<img
|
||||
src={Trash}
|
||||
alt="Delete"
|
||||
alt={`Delete ${element.name}`}
|
||||
className="h-4 w-4 cursor-pointer hover:opacity-50"
|
||||
id={`img-${index}`}
|
||||
onClick={() => handleDeleteKey(element.id)}
|
||||
|
||||
@@ -164,7 +164,7 @@ export default function Analytics() {
|
||||
<div className="mt-12">
|
||||
<div className="flex flex-col items-start">
|
||||
<div className="flex flex-col gap-3">
|
||||
<p className="font-bold text-jet dark:text-bright-gray">
|
||||
<p className="font-bold text-gray-800 dark:text-bright-gray">
|
||||
Filter by chatbot
|
||||
</p>
|
||||
<Dropdown
|
||||
@@ -191,6 +191,7 @@ export default function Analytics() {
|
||||
}
|
||||
rounded="3xl"
|
||||
border="border"
|
||||
borderColor="gray-700"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -81,9 +81,9 @@ export default function General() {
|
||||
return (
|
||||
<div className="mt-12">
|
||||
<div className="mb-5">
|
||||
<p className="font-bold text-jet dark:text-bright-gray">
|
||||
<label className="block font-bold text-jet dark:text-bright-gray">
|
||||
{t('settings.general.selectTheme')}
|
||||
</p>
|
||||
</label>
|
||||
<Dropdown
|
||||
options={themes}
|
||||
selectedValue={selectedTheme}
|
||||
@@ -97,9 +97,9 @@ export default function General() {
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-5">
|
||||
<p className="mb-2 font-bold text-jet dark:text-bright-gray">
|
||||
<label className="block mb-2 font-bold text-jet dark:text-bright-gray">
|
||||
{t('settings.general.selectLanguage')}
|
||||
</p>
|
||||
</label>
|
||||
<Dropdown
|
||||
options={languageOptions.filter(
|
||||
(languageOption) =>
|
||||
@@ -115,9 +115,9 @@ export default function General() {
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-5">
|
||||
<p className="font-bold text-jet dark:text-bright-gray">
|
||||
<label className="block font-bold text-jet dark:text-bright-gray">
|
||||
{t('settings.general.chunks')}
|
||||
</p>
|
||||
</label>
|
||||
<Dropdown
|
||||
options={chunks}
|
||||
selectedValue={selectedChunks}
|
||||
@@ -128,9 +128,9 @@ export default function General() {
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-5">
|
||||
<p className="mb-2 font-bold text-jet dark:text-bright-gray">
|
||||
<label className="mb-2 block font-bold text-jet dark:text-bright-gray">
|
||||
{t('settings.general.convHistory')}
|
||||
</p>
|
||||
</label>
|
||||
<Dropdown
|
||||
options={Array.from(token_limits, ([value, desc]) => ({
|
||||
value: value,
|
||||
@@ -163,16 +163,14 @@ export default function General() {
|
||||
/>
|
||||
</div>
|
||||
<div className="w-56">
|
||||
<p className="font-bold text-jet dark:text-bright-gray">
|
||||
<label className="block font-bold text-jet dark:text-bright-gray">
|
||||
{t('settings.general.deleteAllLabel')}
|
||||
</p>
|
||||
</label>
|
||||
<button
|
||||
className="mt-2 flex w-full cursor-pointer items-center justify-between rounded-3xl border border-solid border-red-500 px-5 py-3 text-red-500 hover:bg-red-500 hover:text-white"
|
||||
className="mt-2 flex w-full cursor-pointer items-center justify-between rounded-3xl border border-solid border-red-700 px-5 py-3 text-red-700 transition-colors hover:bg-red-700 hover:text-white dark:border-red-600 dark:text-red-600 dark:hover:bg-red-600 dark:hover:text-white"
|
||||
onClick={() => dispatch(setModalStateDeleteConv('ACTIVE'))}
|
||||
>
|
||||
<span className="overflow-hidden text-ellipsis ">
|
||||
{t('settings.general.deleteAllBtn')}
|
||||
</span>
|
||||
{t('settings.general.deleteAllBtn')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -168,7 +168,7 @@ export default function Prompts({
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
className="mt-[24px] rounded-3xl border border-solid border-purple-30 px-5 py-3 text-purple-30 hover:bg-purple-30 hover:text-white"
|
||||
className="mt-[24px] rounded-3xl border border-solid border-purple-700 px-5 py-3 text-purple-700 transition-colors hover:bg-purple-700 hover:text-white dark:border-purple-400 dark:text-purple-400 dark:hover:bg-purple-400 dark:hover:text-white"
|
||||
onClick={() => {
|
||||
setModalType('ADD');
|
||||
setModalState('ACTIVE');
|
||||
|
||||
Reference in New Issue
Block a user