Skip to content

Commit

Permalink
Merge pull request #227 from wwWallet/refactor/buttons-focus
Browse files Browse the repository at this point in the history
Enhancing UI Consistency and Accessibility in Button Components with Dark Mode Integration
  • Loading branch information
gkatrakazas authored Apr 25, 2024
2 parents 5ad3ee9 + 1762c04 commit a4daf28
Show file tree
Hide file tree
Showing 27 changed files with 1,154 additions and 840 deletions.
20 changes: 12 additions & 8 deletions src/components/BottomNav.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const BottomNav = ({ isOpen, toggle }) => {
const { t } = useTranslation();

const navItems = [
{ icon: <FaWallet size={26} />, path: '/', label: `${t("common.navItemCredentials")}` },
{ icon: <FaWallet size={26} />, path: '/', alias: '/cb', label: `${t("common.navItemCredentials")}` },
{ icon: <IoIosTime size={26} />, path: '/history', label: `${t("common.navItemHistory")}` },
{ icon: <IoIosAddCircle size={26} />, path: '/add', label: `${t("common.navItemAddCredentialsSimple")}` },
{ icon: <IoIosSend size={26} />, path: '/send', label: `${t("common.navItemSendCredentialsSimple")}` },
Expand All @@ -26,28 +26,32 @@ const BottomNav = ({ isOpen, toggle }) => {
}
};

const isActive = (item) => {
return location.pathname === item.path || location.pathname === item.alias;
};

return (
<div className={`fixed bottom-0 left-0 right-0 bg-white flex justify-around p-4 z-40 max480:flex hidden shadow-2xl rounded-t-lg`}>
<div className={`fixed bottom-0 left-0 right-0 bg-white dark:bg-gray-800 flex justify-around p-4 z-40 max480:flex hidden shadow-2xl rounded-t-lg`}>
{navItems.map(item => (
<div
<button
key={item.path}
className={`cursor-pointer flex flex-col items-center w-[20%] ${(location.pathname === item.path && !isOpen) ? 'text-custom-blue' : 'text-gray-400'} transition-colors duration-200`}
className={`cursor-pointer flex flex-col items-center w-[20%] ${isActive(item) && !isOpen ? 'text-primary dark:text-white' : 'text-gray-400 dark:text-gray-400'} transition-colors duration-200`}
onClick={() => handleNavigate(item.path)}
title={item.label}
>
{item.icon}
<span className="text-xs">{item.label}</span>
</div>
</button>
))}
<div
<button
key={t("common.navItemProfile")}
className={`cursor-pointer flex flex-col items-center w-[20%] ${(isOpen) ? 'text-custom-blue' : 'text-gray-400'} transition-colors duration-200`}
className={`cursor-pointer flex flex-col items-center w-[20%] ${isOpen ? 'text-primary dark:text-white' : 'text-gray-400 dark:text-gray-400'} transition-colors duration-200`}
onClick={toggle}
title={t("common.navItemProfile")}
>
<FaUserCircle size={26} />
<span className="text-xs">{t("common.navItemProfile")}</span>
</div>
</button>
</div>
);
};
Expand Down
36 changes: 18 additions & 18 deletions src/components/BrowserSupport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useSessionStorage } from './useStorage';
import { Trans, useTranslation } from 'react-i18next';

import { FaCheckCircle, FaExclamationTriangle } from 'react-icons/fa';
import GetButton from './Buttons/GetButton';

const noWarnPlatforms = [
{ browser: /chrome/ui, not: { os: /ios/ui } },
Expand All @@ -22,7 +23,7 @@ type ContextValue = {
const BrowserSupportedContext = createContext<ContextValue>({
browserSupported: true,
bypassWarning: true,
setBypass: () => {},
setBypass: () => { },
showWarningPortal: false,
});

Expand Down Expand Up @@ -80,75 +81,74 @@ export function WarningPortal({
{t('browserSupportWarningPortal.heading')}
</h2>

<p className="text-sm">{t('browserSupportWarningPortal.intro')}</p>
<p className="text-sm dark:text-gray-300">{t('browserSupportWarningPortal.intro')}</p>

<p className="text-sm">{t('browserSupportWarningPortal.supportedList.intro')}</p>
<p className="text-sm dark:text-gray-300">{t('browserSupportWarningPortal.supportedList.intro')}</p>
<ul className="ml-4 list-none text-sm">
<li className="flex justify-start items-center" style={{ textAlign: 'left' }}>
<div className="w-1/12">
<FaCheckCircle className="text-md text-green-500" />
</div>
<div className="w-11/12 pl-1">
<div className="w-11/12 pl-1 dark:text-gray-300">
{t('browserSupportWarningPortal.supportedList.windows')}
</div>
</li>
<li className="flex justify-start items-center">
<div className="w-1/12">
<FaCheckCircle className="text-md text-green-500" />
</div>
<div className="w-11/12 pl-1">
<div className="w-11/12 pl-1 dark:text-gray-300">
{t('browserSupportWarningPortal.supportedList.macos')}
</div>
</li>
<li className="flex justify-start items-center">
<div className="w-1/12">
<FaCheckCircle className="text-md text-green-500" />
</div>
<div className="w-11/12 pl-1">
<div className="w-11/12 pl-1 dark:text-gray-300">
{t('browserSupportWarningPortal.supportedList.android')}
</div>
</li>
<li className="flex justify-start items-center">
<div className="w-1/12">
<FaCheckCircle className="text-md text-green-500" />
</div>
<div className="w-11/12 pl-1">
<div className="w-11/12 pl-1 dark:text-gray-300">
{t('browserSupportWarningPortal.supportedList.linux')}
</div>
</li>
</ul>

<p className="text-sm">
<p className="text-sm dark:text-gray-300">
<Trans
i18nKey="browserSupportWarningPortal.moreDetails"
components={{
docLink: <a
href="https://github.com/wwWallet/wallet-frontend#prf-compatibility" target='blank_'
className="font-medium text-custom-blue hover:underline dark:text-blue-500"
className="font-medium text-primary hover:underline dark:text-blue-500"
/>
}}
/>
</p>

<p className="text-sm">{t('browserSupportWarningPortal.outro')}</p>
<p className="text-sm dark:text-gray-300">{t('browserSupportWarningPortal.outro')}</p>

<button
className={`w-full text-white bg-gray-300 hover:bg-gray-400 focus:ring-4 focus:outline-none focus:ring-gray-400 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-gray-300 dark:hover:bg-gray-400 dark:focus:ring-gray-400 ${classes || ""}`}
<GetButton
content={t('browserSupportWarningPortal.continueAnyway')}
onClick={() => setBypass(true)}
type="button"
>
{t('browserSupportWarningPortal.continueAnyway')}
</button>
variant="custom"
additionalClassName='text-white bg-gray-300 hover:bg-gray-400 w-full'
/>
</>
);
}

return (
<Ctx>
<If test={(ctx) => !ctx.showWarningPortal }>
<If test={(ctx) => !ctx.showWarningPortal}>
{children}
</If>
<If test={(ctx) => ctx.showWarningPortal }>
<If test={(ctx) => ctx.showWarningPortal}>
<Content classes={classes} />
</If>
</Ctx>
Expand Down
40 changes: 40 additions & 0 deletions src/components/Buttons/GetButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';

const GetButton = ({ type = 'button', content, onClick, variant = 'custom', additionalClassName = '', disabled = false, ariaLabel, title }) => {

const getVariantClassName = () => {
const commonClasses = 'rounded-lg shadow-sm text-sm px-4 py-2 text-center flex flex-row flex-nowrap items-center justify-center';
switch (variant) {
case 'primary':
return `${commonClasses} text-white ${!disabled ? "bg-primary hover:bg-primary-hover dark:text-white dark:hover:bg-primary-light-hover dark:bg-primary-light" : "bg-gray-300 cursor-not-allowed hover:bg-gray-300"}`;
case 'secondary':
return `${commonClasses} text-white ${!disabled ? "bg-primary-light hover:bg-primary-light-hover dark:bg-extra-light dark:hover:bg-primary-light" : "bg-gray-300 cursor-not-allowed hover:bg-gray-300"}`;
case 'tertiary':
return `${commonClasses} text-gray-700 bg-gray-100 hover:bg-gray-200`;
case 'cancel':
return `${commonClasses} ${!disabled ? "text-gray-900 bg-gray-300 hover:bg-gray-400" : "text-white bg-gray-300 cursor-not-allowed hover:bg-gray-300"}`;
case 'delete':
return `${commonClasses} ${!disabled ? "text-white bg-red-600 hover:bg-red-700" : "text-red-400 bg-gray-300 hover:bg-gray-300 cursor-not-allowed"}`;
case 'custom':
return `${commonClasses}`;
}
};

const focusVisibleClasses = 'focus-visible:outline-2 focus-visible:outline-offset-2';
const className = `${getVariantClassName()} ${focusVisibleClasses} ${additionalClassName}`;

return (
<button
type={type}
{...(onClick && { onClick: onClick })}
{...(disabled && { disabled })}
className={className}
{...(ariaLabel && { 'aria-label': ariaLabel })}
{...(title && { title })}
>
{content}
</button>
);
};

export default GetButton;
34 changes: 13 additions & 21 deletions src/components/Buttons/QRButton.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,23 @@
import React from 'react';
import { BsQrCodeScan } from 'react-icons/bs';
import GetButton from './GetButton';

const QRButton = ({ openQRScanner, isSmallScreen }) => {
const isMobile = window.innerWidth <= 480;
const buttonClass = "px-2 py-2 mb-2 text-white bg-custom-blue hover:bg-custom-blue-hover focus:ring-4 focus:outline-none focus:ring-custom-blue font-medium rounded-lg text-sm px-4 py-2 text-center dark:bg-custom-blue-hover dark:hover:bg-custom-blue-hover dark:focus:ring-custom-blue-hover shadow-md";

if (isMobile) {
if (isSmallScreen) {
return (
<button
className={`fixed z-20 bottom-20 right-5 ${buttonClass}`}
onClick={openQRScanner}
>
<div className="flex items-center">
<BsQrCodeScan size={20} className="text-white" />
</div>
</button>
);
} else if (isSmallScreen) {
return (
<button
className={buttonClass}
onClick={openQRScanner}
>
<div className="flex items-center">
<BsQrCodeScan size={20} className="text-white" />
</div>
</button>
<div className="mb-2">
<GetButton
content={
<BsQrCodeScan size={20} className="text-white" />
}
onClick={openQRScanner}
variant="primary"
additionalClassName={`${isMobile ? "fixed z-20 bottom-[85px] right-5" : ""}`}
/>
</div>

);
}

Expand Down
16 changes: 10 additions & 6 deletions src/components/Credentials/CredentialDeleteButton.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { MdDelete } from 'react-icons/md';
import { useTranslation } from 'react-i18next';
import GetButton from '../../components/Buttons/GetButton';

const CredentialDeleteButton = ({ onDelete }) => {
const { t } = useTranslation();
Expand All @@ -10,13 +11,16 @@ const CredentialDeleteButton = ({ onDelete }) => {
};

return (
<div className=" lg:p-0 p-2 w-full">
<button
className="lg:mt-5 mt-2 text-white cursor-pointer flex items-center bg-red-600 hover:bg-red-800 font-medium rounded-lg text-sm px-4 py-2 text-center"
<div className="lg:p-0 p-2 w-full lg:mt-5 mt-2">
<GetButton
content={
<>
<MdDelete size={20} /> {t('common.delete')}
</>
}
onClick={handleClick}
>
<MdDelete size={20} /> {t('common.delete')}
</button>
variant="delete"
/>
</div>
);
};
Expand Down
26 changes: 13 additions & 13 deletions src/components/Credentials/CredentialInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ import { parseCredential } from '../../functions/parseCredential';
const getFieldIcon = (fieldName) => {
switch (fieldName) {
case 'type':
return <BiSolidCategoryAlt size={25} className="inline mr-1 mb-1" />;
return <BiSolidCategoryAlt size={25} className="inline mr-1" />;
case 'expdate':
return <RiPassExpiredFill size={25} className="inline mr-1 mb-1" />;
return <RiPassExpiredFill size={25} className="inline mr-1" />;
case 'dateOfBirth':
return <AiFillCalendar size={25} className="inline mr-1 mb-1" />;
return <AiFillCalendar size={25} className="inline mr-1" />;
case 'id':
return <MdOutlineNumbers size={25} className="inline mr-1 mb-1" />
return <MdOutlineNumbers size={25} className="inline mr-1" />
case 'familyName':
case 'firstName':
return <BiSolidUserCircle size={25} className="inline mr-1 mb-1" />;
return <BiSolidUserCircle size={25} className="inline mr-1" />;
case 'diplomaTitle':
return <MdTitle size={25} className="inline mr-1 mb-1" />;
return <MdTitle size={25} className="inline mr-1" />;
case 'eqfLevel':
return <GiLevelEndFlag size={25} className="inline mr-1 mb-1" />;
return <GiLevelEndFlag size={25} className="inline mr-1" />;
case 'grade':
return <MdGrade size={25} className="inline mr-1 mb-1" />;
return <MdGrade size={25} className="inline mr-1" />;
default:
return null;
}
Expand All @@ -34,22 +34,22 @@ const getFieldIcon = (fieldName) => {
const renderRow = (fieldName, label, fieldValue) => {
if (fieldValue) {
return (
<tr className="text-left text-xs sm:text-sm md:text-base">
<td className="font-bold text-custom-blue py-2 px-2 rounded-l-xl">
<tr className="text-left">
<td className="font-bold text-primary dark:text-primary-light py-2 px-2 rounded-l-xl">
<div className="flex md:flex-row flex-col items-left">
{getFieldIcon(fieldName)}
<span className="md:ml-1">{label}:</span>
<span className="md:ml-1 flex items-center">{label}:</span>
</div>
</td>
<td className="py-2 px-2 rounded-r-xl">{fieldValue}</td>
<td className="text-gray-700 dark:text-white py-2 px-2 rounded-r-xl">{fieldValue}</td>
</tr>
);
} else {
return null;
}
};

const CredentialInfo = ({ credential, mainClassName = "pt-5 pr-2 w-full" }) => {
const CredentialInfo = ({ credential, mainClassName = "text-xs sm:text-sm md:text-base pt-5 pr-2 w-full" }) => {

const [parsedCredential, setParsedCredential] = useState(null);

Expand Down
30 changes: 17 additions & 13 deletions src/components/Credentials/CredentialJson.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React, { useEffect, useState } from 'react';

import { AiOutlineDown, AiOutlineUp } from 'react-icons/ai';
import { parseCredential } from '../../functions/parseCredential';
import GetButton from '../Buttons/GetButton';

const CredentialJson = ({ credential }) => {
const [showJsonCredentials, setShowJsonCredentials] = useState(false);
Expand All @@ -18,28 +19,31 @@ const CredentialJson = ({ credential }) => {

return (
<div className=" lg:p-0 p-2 w-full">
<div className="mb-2 flex items-center">
<button
<div className="mb-4 flex items-center">
<GetButton
content={
<>
{showJsonCredentials ? 'Hide Credentials Details' : 'Show Credentials Details'}
{showJsonCredentials ? (
<AiOutlineUp className="ml-1" />
) : (
<AiOutlineDown className="ml-1" />
)}
</>
}
onClick={() => setShowJsonCredentials(!showJsonCredentials)}
className="px-2 py-2 mb-2 text-white cursor-pointer flex items-center bg-custom-blue hover:bg-custom-blue-hover focus:ring-custom-blue font-medium rounded-lg text-sm px-4 py-2 text-center dark:bg-custom-blue-hover dark:hover:bg-custom-blue-hover dark:focus:ring-custom-blue-hover"
>
{showJsonCredentials ? 'Hide Credentials Details' : 'Show Credentials Details'}
{showJsonCredentials ? (
<AiOutlineUp className="ml-1" />
) : (
<AiOutlineDown className="ml-1" />
)}
</button>
variant="primary"
/>
</div>

<hr className="my-2 border-t border-gray-500 py-2" />
<hr className="my-2 border-t border-primary dark:border-primary-light py-2" />

{showJsonCredentials && parsedCredential ? (
<div>
<textarea
rows="10"
readOnly
className="w-full border rounded p-2 rounded-xl"
className="w-full dark:bg-gray-900 dark:text-white border rounded p-2 rounded-xl"
value={JSON.stringify(parsedCredential, null, 2)}
/>
</div>
Expand Down
Loading

0 comments on commit a4daf28

Please sign in to comment.