summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2023-12-13 11:34:41 -0300
committerSebastian <sebasjm@gmail.com>2024-01-14 17:38:29 -0300
commit6e1176b1b3f57fabd750f3a68d942b0e5cb57bd7 (patch)
tree60142e9cd14c6680bea7b4622302021c32ad00c6
parentad22420fbb0ea02469b54975e430c65dd19868e7 (diff)
downloadwallet-core-dev/sebasjm/a11y-test.tar.gz
wallet-core-dev/sebasjm/a11y-test.tar.bz2
wallet-core-dev/sebasjm/a11y-test.zip
-rw-r--r--packages/demobank-ui/src/components/Transactions/views.tsx60
-rw-r--r--packages/demobank-ui/src/pages/OperationState/views.tsx2
-rw-r--r--packages/demobank-ui/src/pages/PaymentOptions.tsx25
-rw-r--r--packages/web-util/src/components/Attention.tsx4
-rw-r--r--packages/web-util/src/components/CopyButton.tsx5
-rw-r--r--packages/web-util/src/components/LangSelector.tsx4
-rw-r--r--packages/web-util/src/forms/InputArray.tsx4
-rw-r--r--packages/web-util/src/forms/InputSelectMultiple.tsx6
-rw-r--r--packages/web-util/src/forms/InputSelectOne.tsx6
9 files changed, 80 insertions, 36 deletions
diff --git a/packages/demobank-ui/src/components/Transactions/views.tsx b/packages/demobank-ui/src/components/Transactions/views.tsx
index f3ffcd157..5f1b9b688 100644
--- a/packages/demobank-ui/src/components/Transactions/views.tsx
+++ b/packages/demobank-ui/src/components/Transactions/views.tsx
@@ -36,6 +36,11 @@ export function ReadyView({ transactions, onNext, onPrev }: State.Ready): VNode
prev[d].push(cur)
return prev
}, {} as Record<string, typeof transactions>)
+ /**
+ * FIXME: create an abstraction of a table with accessible feature
+ * multi column multi header and subheaders
+ * Also responsiveness
+ */
return (
<div class="px-4 mt-4">
<div class="sm:flex sm:items-center">
@@ -44,28 +49,41 @@ export function ReadyView({ transactions, onNext, onPrev }: State.Ready): VNode
</div>
</div>
<div class="-mx-4 mt-5 ring-1 ring-gray-300 sm:mx-0 rounded-lg min-w-fit bg-white">
- <table class="min-w-full divide-y divide-gray-300">
+ <table class="min-w-full divide-y divide-gray-300" summary={i18n.str`List of all transfer received and sent related to this account sorted by date with the latest on top.`}>
<thead>
<tr>
- <th scope="col" class="pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">{i18n.str`Date`}</th>
- <th scope="col" class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">{i18n.str`Amount`}</th>
- <th scope="col" class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">{i18n.str`Counterpart`}</th>
- <th scope="col" class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">{i18n.str`Subject`}</th>
+ <th id="transfer-date" scope="col" class="pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">
+ {i18n.str`Date`}
+ </th>
+ <th id="transfer-counterpart" scope="col" class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">
+ {i18n.str`Counterpart`}
+ </th>
+ <th id="transfer-subject" scope="col" class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">
+ {i18n.str`Subject`}
+ </th>
+ <th id="transfer-amount" scope="col" class="hidden sm:table-cell px-2 py-3.5 text-left text-sm font-semibold text-gray-900 ">
+ {i18n.str`Amount`}
+ </th>
</tr>
</thead>
<tbody>
- {Object.entries(txByDate).map(([date, txs], idx) => {
+ {Object.entries(txByDate).map(([date, txs], idxGr) => {
return <Fragment>
<tr class="border-t border-gray-200">
- <th colSpan={4} scope="colgroup" class="bg-gray-50 py-2 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-3">
+ <th colSpan={4} scope="colgroup"
+ class="bg-gray-50 py-2 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-3"
+ id={`transfer-date-${idxGr}`}
+ headers={`transfer-date`}
+ >
{date}
</th>
</tr>
- {txs.map(item => {
+ {txs.map((item, idxTx) => {
const time = item.when.t_ms === "never" ? "" : format(item.when.t_ms, "HH:mm:ss", { locale: dateLocale })
- return (<tr key={idx} class="border-b border-gray-200 last:border-none">
- <td class="relative py-2 pl-2 pr-2 text-sm ">
- <div class="font-medium text-gray-900">{time}</div>
+ return (<tr key={idxTx} class="border-b border-gray-200 last:border-none">
+ <td class="relative py-2 pl-2 pr-2 text-sm sm:bg-gray-50 w-28"
+ id={`transfer-time-${idxGr}-${idxTx}`}
+ headers={`transfer-date-${idxGr} transfer-date`}>
<dl class="font-normal sm:hidden">
<dt class="sr-only sm:hidden"><i18n.Translate>Amount</i18n.Translate></dt>
<dd class="mt-1 truncate text-gray-700">
@@ -90,19 +108,25 @@ export function ReadyView({ transactions, onNext, onPrev }: State.Ready): VNode
</dd>
</dl>
</td>
+ <td class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500"
+ headers={`transfer-date-${idxGr} transfer-time-${idxGr}-${idxTx} transfer-counterpart`}
+ >
+ <a href={`#/wire-transfer/${item.counterpart}`} class="text-indigo-600 hover:text-indigo-900">
+ {item.counterpart}
+ </a>
+ </td>
+ <td class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500 break-all "
+ headers={`transfer-date-${idxGr} transfer-time-${idxGr}-${idxTx} transfer-subject`}
+ >{item.subject}</td>
<td data-negative={item.negative ? "true" : "false"}
- class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500 ">
+ class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500 min-w-md"
+ headers={`transfer-date-${idxGr} transfer-time-${idxGr}-${idxTx} transfer-amount`}
+ >
{item.amount ? (<RenderAmount value={item.amount} negative={item.negative} withColor spec={config.currency_specification} />
) : (
<span style={{ color: "grey" }}>&lt;{i18n.str`invalid value`}&gt;</span>
)}
</td>
- <td class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500">
- <a href={`#/wire-transfer/${item.counterpart}`} class="text-indigo-600 hover:text-indigo-900">
- {item.counterpart}
- </a>
- </td>
- <td class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500 break-all min-w-md">{item.subject}</td>
</tr>)
})}
</Fragment>
diff --git a/packages/demobank-ui/src/pages/OperationState/views.tsx b/packages/demobank-ui/src/pages/OperationState/views.tsx
index 05d53bb05..c43efb933 100644
--- a/packages/demobank-ui/src/pages/OperationState/views.tsx
+++ b/packages/demobank-ui/src/pages/OperationState/views.tsx
@@ -317,7 +317,7 @@ export function ReadyView({ uri, onClose: doClose }: State.Ready): VNode<{}> {
onClose()
}}
>
- Cancel
+ <i18n.Translate>Cancel</i18n.Translate>
</button>
</div>
diff --git a/packages/demobank-ui/src/pages/PaymentOptions.tsx b/packages/demobank-ui/src/pages/PaymentOptions.tsx
index 2a7374cab..80a7a620f 100644
--- a/packages/demobank-ui/src/pages/PaymentOptions.tsx
+++ b/packages/demobank-ui/src/pages/PaymentOptions.tsx
@@ -35,6 +35,7 @@ export function PaymentOptions({ limit, goToConfirmOperation, onAuthorizationReq
const [bankState] = useBankState();
const [tab, setTab] = useState<"charge-wallet" | "wire-transfer" | undefined>();
+ const [changeUsingMouse, setChangeUsingMouse] = useState(false)
return (
<div class="mt-4">
@@ -45,11 +46,12 @@ export function PaymentOptions({ limit, goToConfirmOperation, onAuthorizationReq
</legend>
<div class="px-4 mt-4 grid grid-cols-1 gap-y-6 sm:grid-cols-2 sm:gap-x-4">
- {/* <!-- Active: "border-indigo-600 ring-2 ring-indigo-600", Not Active: "border-gray-300" --> */}
- <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (tab === "charge-wallet" ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
- <input type="radio" name="project-type" value="Newsletter" class="sr-only" aria-labelledby="project-type-0-label" aria-describedby="project-type-0-description-0 project-type-0-description-1" onClick={() => {
- setTab("charge-wallet")
- }}
+ <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus-within:ring focus-within:outline focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-gray-600"}>
+ <input type="radio" name="transfer-type" value="char-wallet" class="sr-only"
+ onClick={(e) => {
+ setChangeUsingMouse(e.clientX > 0 && e.clientY > 0)
+ setTab("charge-wallet")
+ }}
/>
<div class="flex flex-col">
<span class="flex">
@@ -76,13 +78,16 @@ export function PaymentOptions({ limit, goToConfirmOperation, onAuthorizationReq
</label>
- <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none" + (tab === "wire-transfer" ? "border-indigo-600 ring-2 ring-indigo-600" : "border-gray-300")}>
- <input type="radio" name="project-type" value="Existing Customers" class="sr-only" aria-labelledby="project-type-1-label" aria-describedby="project-type-1-description-0 project-type-1-description-1" onClick={() => {
+ <label class={"relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus-visible:ring-red-400" + (tab === "wire-transfer" ? " ring-2 ring-indigo-600" : "border-gray-300")}>
+ <input type="radio" name="transfer-type" value="wire-transfer" class="sr-only" onClick={(e) => {
+ setChangeUsingMouse(e.clientX > 0 && e.clientY > 0)
setTab("wire-transfer")
}} />
<div class="flex flex-col">
<span class="flex">
- <div class="text-4xl mr-4 my-auto">&#x2194;</div>
+ <div class="text-4xl mr-4 my-auto">
+ <span aria-hidden="true">&#x2194;</span>
+ </div>
<span class="grow self-center text-lg font-medium text-gray-900 align-middle text-center">
<i18n.Translate>to another bank account</i18n.Translate>
</span>
@@ -98,7 +103,7 @@ export function PaymentOptions({ limit, goToConfirmOperation, onAuthorizationReq
</div>
{tab === "charge-wallet" && (
<WalletWithdrawForm
- focus
+ focus={changeUsingMouse}
limit={limit}
onAuthorizationRequired={onAuthorizationRequired}
goToConfirmOperation={goToConfirmOperation}
@@ -109,7 +114,7 @@ export function PaymentOptions({ limit, goToConfirmOperation, onAuthorizationReq
)}
{tab === "wire-transfer" && (
<PaytoWireTransferForm
- focus
+ focus={changeUsingMouse}
title={i18n.str`Transfer details`}
limit={limit}
onAuthorizationRequired={onAuthorizationRequired}
diff --git a/packages/web-util/src/components/Attention.tsx b/packages/web-util/src/components/Attention.tsx
index b85230a1b..bb87d5415 100644
--- a/packages/web-util/src/components/Attention.tsx
+++ b/packages/web-util/src/components/Attention.tsx
@@ -1,5 +1,6 @@
import { TranslatedString, assertUnreachable } from "@gnu-taler/taler-util";
import { ComponentChildren, Fragment, VNode, h } from "preact";
+import { useTranslationContext } from "../index.browser.js";
interface Props {
type?: "info" | "success" | "warning" | "danger",
@@ -8,6 +9,7 @@ interface Props {
children?: ComponentChildren,
}
export function Attention({ type = "info", title, children, onClose }: Props): VNode {
+ const { i18n } = useTranslationContext();
return <div class={`group attention-${type} mt-2 shadow-lg`}>
<div class="rounded-md group-[.attention-info]:bg-blue-50 group-[.attention-warning]:bg-yellow-50 group-[.attention-danger]:bg-red-50 group-[.attention-success]:bg-green-50 p-4 shadow">
<div class="flex">
@@ -39,7 +41,7 @@ export function Attention({ type = "info", title, children, onClose }: Props): V
</div>
{onClose &&
<div>
- <button type="button" class="font-semibold items-center rounded bg-transparent px-2 py-1 text-xs text-gray-900 hover:bg-gray-50"
+ <button type="button" aria-label={i18n.str`Close banner`} class="font-semibold items-center rounded bg-transparent px-2 py-1 text-xs text-gray-900 hover:bg-gray-50"
onClick={(e) => {
e.preventDefault();
onClose();
diff --git a/packages/web-util/src/components/CopyButton.tsx b/packages/web-util/src/components/CopyButton.tsx
index e76447291..bb08b99ef 100644
--- a/packages/web-util/src/components/CopyButton.tsx
+++ b/packages/web-util/src/components/CopyButton.tsx
@@ -1,5 +1,7 @@
import { h, VNode } from "preact";
+import { useTransition } from "preact/compat";
import { useEffect, useState } from "preact/hooks";
+import { useTranslationContext } from "../index.browser.js";
export function CopyIcon(): VNode {
return (
@@ -19,6 +21,7 @@ export function CopiedIcon(): VNode {
export function CopyButton({ class: clazz, getContent }: { class: string, getContent: () => string }): VNode {
const [copied, setCopied] = useState(false);
+ const {i18n} = useTranslationContext()
function copyText(): void {
if (!navigator.clipboard && !window.isSecureContext) {
alert('clipboard is not available on insecure context (http)')
@@ -38,7 +41,7 @@ export function CopyButton({ class: clazz, getContent }: { class: string, getCon
if (!copied) {
return (
- <button class={clazz} onClick={copyText} >
+ <button class={clazz} onClick={copyText} aria-label={i18n.str`Copy`} >
<CopyIcon />
</button>
);
diff --git a/packages/web-util/src/components/LangSelector.tsx b/packages/web-util/src/components/LangSelector.tsx
index a8d910129..ebcd1c696 100644
--- a/packages/web-util/src/components/LangSelector.tsx
+++ b/packages/web-util/src/components/LangSelector.tsx
@@ -65,7 +65,7 @@ export function LangSelector({ supportedLangs }: { supportedLangs: string[] }):
return (
<div>
<div class="relative mt-2">
- <button type="button" class="relative w-full cursor-default rounded-md bg-white py-1.5 pl-3 pr-10 text-left text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-600 sm:text-sm sm:leading-6" aria-haspopup="listbox" aria-expanded="true" aria-labelledby="listbox-label"
+ <button type="button" class="relative w-full cursor-default rounded-md bg-white py-1.5 pl-3 pr-10 text-left text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-600 sm:text-sm sm:leading-6" aria-haspopup="listbox" aria-expanded="true"
onClick={() => {
setHidden((h) => !h);
}}>
@@ -81,7 +81,7 @@ export function LangSelector({ supportedLangs }: { supportedLangs: string[] }):
</button>
{!hidden &&
- <ul class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm" tabIndex={-1} role="listbox" aria-labelledby="listbox-label" aria-activedescendant="listbox-option-3">
+ <ul class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm" tabIndex={-1} role="listbox" aria-activedescendant="listbox-option-3">
{supportedLangs
.filter((l) => l !== lang)
.map((lang) => (
diff --git a/packages/web-util/src/forms/InputArray.tsx b/packages/web-util/src/forms/InputArray.tsx
index 7d9a1b378..a8a30b32a 100644
--- a/packages/web-util/src/forms/InputArray.tsx
+++ b/packages/web-util/src/forms/InputArray.tsx
@@ -5,6 +5,7 @@ import { FormProvider, UIFormProps } from "./FormProvider.js";
import { LabelWithTooltipMaybeRequired } from "./InputLine.js";
import { RenderAllFieldsByUiConfig, UIFormField } from "./forms.js";
import { useField } from "./useField.js";
+import { useTranslationContext } from "../index.browser.js";
function Option({
label,
@@ -77,6 +78,7 @@ export function InputArray<T extends object, K extends keyof T>(
labelField: string;
} & UIFormProps<T, K>,
): VNode {
+ const { i18n } = useTranslationContext();
const { fields, labelField, name, label, required, tooltip } = props;
const { value, onChange, state } = useField<T, K>(name);
const list = (value ?? []) as Array<Record<string, string | undefined>>;
@@ -175,7 +177,7 @@ export function InputArray<T extends object, K extends keyof T>(
}}
class="block rounded-md bg-red-600 px-3 py-2 text-center text-sm text-white shadow-sm hover:bg-red-500 "
>
- Remove
+ <i18n.Translate>Remove</i18n.Translate>
</button>
)}
</div>
diff --git a/packages/web-util/src/forms/InputSelectMultiple.tsx b/packages/web-util/src/forms/InputSelectMultiple.tsx
index a67eb23b7..d1e31c15c 100644
--- a/packages/web-util/src/forms/InputSelectMultiple.tsx
+++ b/packages/web-util/src/forms/InputSelectMultiple.tsx
@@ -4,6 +4,7 @@ import { UIFormProps } from "./FormProvider.js";
import { ChoiceS } from "./InputChoiceStacked.js";
import { LabelWithTooltipMaybeRequired } from "./InputLine.js";
import { useField } from "./useField.js";
+import { useTranslationContext } from "../index.browser.js";
export function InputSelectMultiple<T extends object, K extends keyof T>(
props: {
@@ -22,6 +23,8 @@ export function InputSelectMultiple<T extends object, K extends keyof T>(
return { ...prev, [curr.value as string]: curr.label };
}, {} as Record<string, string>);
+ const { i18n } = useTranslationContext();
+
const list = (value ?? []) as string[];
const filteredChoices =
filter === undefined
@@ -51,7 +54,7 @@ export function InputSelectMultiple<T extends object, K extends keyof T>(
}}
class="group relative h-5 w-5 rounded-sm hover:bg-gray-500/20"
>
- <span class="sr-only">Remove</span>
+ <span class="sr-only"><i18n.Translate>Remove</i18n.Translate></span>
<svg
viewBox="0 0 14 14"
class="h-5 w-5 stroke-gray-700/50 group-hover:stroke-gray-700/75"
@@ -81,6 +84,7 @@ export function InputSelectMultiple<T extends object, K extends keyof T>(
<button
type="button"
disabled={state.disabled}
+ aria-label={i18n.str`Clear filter`}
onClick={() => {
setFilter(filter === undefined ? "" : undefined);
}}
diff --git a/packages/web-util/src/forms/InputSelectOne.tsx b/packages/web-util/src/forms/InputSelectOne.tsx
index d100b079d..93cb2c2f2 100644
--- a/packages/web-util/src/forms/InputSelectOne.tsx
+++ b/packages/web-util/src/forms/InputSelectOne.tsx
@@ -4,12 +4,14 @@ import { UIFormProps } from "./FormProvider.js";
import { ChoiceS } from "./InputChoiceStacked.js";
import { LabelWithTooltipMaybeRequired } from "./InputLine.js";
import { useField } from "./useField.js";
+import { useTranslationContext } from "../index.browser.js";
export function InputSelectOne<T extends object, K extends keyof T>(
props: {
choices: ChoiceS<T[K]>[];
} & UIFormProps<T, K>,
): VNode {
+ const { i18n } = useTranslationContext();
const { name, label, choices, placeholder, tooltip, required } = props;
const { value, onChange } = useField<T, K>(name);
@@ -25,6 +27,7 @@ export function InputSelectOne<T extends object, K extends keyof T>(
: choices.filter((v) => {
return regex.test(v.label);
});
+
return (
<div class="sm:col-span-6">
<LabelWithTooltipMaybeRequired
@@ -42,7 +45,7 @@ export function InputSelectOne<T extends object, K extends keyof T>(
}}
class="group relative h-5 w-5 rounded-sm hover:bg-gray-500/20"
>
- <span class="sr-only">Remove</span>
+ <span class="sr-only"><i18n.Translate>Remove</i18n.Translate></span>
<svg
viewBox="0 0 14 14"
class="h-5 w-5 stroke-gray-700/50 group-hover:stroke-gray-700/75"
@@ -69,6 +72,7 @@ export function InputSelectOne<T extends object, K extends keyof T>(
/>
<button
type="button"
+ aria-label={i18n.str`Clear filter`}
onClick={() => {
setFilter(filter === undefined ? "" : undefined);
}}