| type | Feature |
|---|---|
| title | Persist wallet connection across reloads with a WalletProvider context |
| labels | type:feature, area:wallet, stack:nextjs, stack:react, stack:typescript, priority:high, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN |
| assignees |
components/WalletStatus.jsx keeps walletState/walletData in local component state, so the connection is lost on every navigation or page refresh and cannot be read by other components (e.g. a future "Fund this invoice" button or the Invoices page header). This issue introduces a WalletProvider React context that owns the wallet state, rehydrates it from storage on mount, and exposes a useWallet() hook.
- Repository scope: Liquifact/Liquifact-frontend only.
- Create
components/WalletProvider.jsxexportingWalletProviderand auseWallet()hook returning{ state, walletData, connect, disconnect }, reusing the existingWALLET_STATESenum fromcomponents/WalletStatus.jsx. - Persist a minimal, non-sensitive snapshot (connection intent + truncated address + network) via
localStorageand rehydrate on mount; never persist secrets or full balances. - Refactor
WalletStatusto consumeuseWallet()instead of its internaluseState, keeping all existing accessibility regions and toast calls intact. - Mount
WalletProvideronce inapp/layout.jsinside the existingToastProvider. - Guard against SSR by reading storage only after mount (no
windowaccess during render).
- Fork the repo and create a branch
git checkout -b feature/wallet-01-walletprovider-persistence- Implement changes
- Write code in: create
components/WalletProvider.jsx; updatecomponents/WalletStatus.jsxandapp/layout.js. - Write comprehensive tests in: create
components/WalletProvider.test.tsxβ assert rehydration, disconnect clears storage, anduseWalletthrows outside the provider. - Add documentation: document
WalletProvider/useWalletinREADME.mdand cross-referenceWALLET_INTEGRATION_CONTRACT.md. - Add JSDoc to the hook and provider props.
- Validate security: never persist private keys or full balances; sanitize values read back from storage.
- Write code in: create
- Test and commit
- Run
npm run lint,npm test, andnpm run build. - Cover edge cases: corrupt storage payload, SSR render, and rehydrate-then-disconnect.
- Include the full
npm testoutput and a note on what is persisted.
feat: persist wallet connection via a shared walletprovider context
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add a client-side search box to filter the Invest marketplace by issuer" labels: type:feature, area:invest, stack:nextjs, stack:react, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
The marketplace list in app/invest/page.js renders every loaded invoice with no way to find a specific issuer; as the list grows beyond the three mock entries this becomes unusable. This issue adds a debounced search input that filters the rendered invoices by issuer name and keeps the polite aria-live announcement accurate.
- Repository scope: Liquifact/Liquifact-frontend only.
- Add a labelled search
<input type="search">above the list that filtersinvoicesby case-insensitive substring match onissuer. - Debounce the input (e.g. 200ms) so filtering does not thrash on every keystroke.
- Update
getInvoiceLoadAnnouncementusage so the status region announces the filtered count (e.g. "2 of 3 invoices match"), and show a distinct "no matches" state separate from the empty-marketplace state. - Preserve the loading/error/empty branches and the slate/cyan styling; clearing the field restores the full list.
- Fork the repo and create a branch
git checkout -b feature/invest-02-issuer-search- Implement changes
- Write code in: update
app/invest/page.js; optionally extract acomponents/InvoiceSearch.jsx. - Write comprehensive tests in: extend
app/invest/page.test.jsxand/or createcomponents/InvoiceSearch.test.tsxβ assert filtering, debounce, no-match state, and clear. - Add documentation: note the search behaviour in
README.md. - Add JSDoc on any extracted search component.
- Validate a11y: the input has an associated label and the filtered count is announced politely.
- Write code in: update
- Test and commit
- Run
npm run lint,npm test, andnpm run build. - Cover edge cases: empty query, no matches, whitespace-only query, and rapid typing.
- Include the full
npm testoutput.
feat: add issuer search box to the invest marketplace
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add pagination or load-more to the Invest marketplace list" labels: type:feature, area:invest, stack:nextjs, stack:react, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
app/invest/page.js maps the entire invoice array into a single <ul> with no paging or windowing, so a real backend returning hundreds of invoices would render an unbounded DOM and a long, unscannable list. This issue adds page-size-bounded rendering with an accessible "Load more" (or numbered pages) control.
- Repository scope: Liquifact/Liquifact-frontend only.
- Render at most
PAGE_SIZE(e.g. 10) invoices at a time and expose a "Load more" button (or prev/next page controls) that reveals the next batch. - Keep the polite status region accurate ("Showing N of M invoices"); announce when more results are appended.
- Reset paging when filters/search change (coordinate with the existing list rendering so the change is non-breaking if those features land later).
- Preserve loading/empty/error branches and keyboard focus on the control after each load.
- Fork the repo and create a branch
git checkout -b feature/invest-03-marketplace-pagination- Implement changes
- Write code in: update
app/invest/page.js; optionally extract acomponents/Pagination.jsx. - Write comprehensive tests in: extend
app/invest/page.test.jsxβ assert initial page size, load-more appends, and the count announcement. - Add documentation: note paging behaviour in
README.md. - Add JSDoc on any pagination helper/component.
- Validate a11y: the control has a clear accessible name and focus is managed after load.
- Write code in: update
- Test and commit
- Run
npm run lint,npm test, andnpm run build. - Cover edge cases: fewer items than a page, exact page boundary, and last page.
- Include the full
npm testoutput.
feat: paginate the invest marketplace invoice list
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Show a determinate progress bar during invoice upload in UploadZone" labels: type:enhancement, area:upload, stack:nextjs, stack:react, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
components/UploadZone.jsx only shows an indeterminate spinner with "Uploading invoice..." during the uploading status; the user gets no sense of how far along a large (up to 10 MB) PDF upload is. This issue adds a determinate progress bar driven by upload progress events, with proper ARIA so screen-reader users hear progress too.
- Repository scope: Liquifact/Liquifact-frontend only.
- Render a
role="progressbar"element witharia-valuemin/aria-valuemax/aria-valuenowand a visible percentage during theuploadingstatus. - Accept an injectable
onProgress-style callback (or progress prop) so the bar can be driven by a realXMLHttpRequest/fetch-stream progress source later and tested deterministically now. - Fall back to the existing indeterminate spinner when progress is unknown; keep the
tokenizingandsuccesscopy unchanged so the e2e spec intests/toast.spec.jsxstill passes. - Respect
prefers-reduced-motionfor any bar transition.
- Fork the repo and create a branch
git checkout -b enhancement/upload-04-progress-bar- Implement changes
- Write code in: update
components/UploadZone.jsx; optionally extract acomponents/ProgressBar.jsx. - Write comprehensive tests in: extend
components/UploadZone.test.jsxβ assert progressbar ARIA values update and the indeterminate fallback. - Add documentation: note the progress UI in
README.md. - Add JSDoc on the progress prop/callback.
- Validate a11y: progressbar exposes value text and does not steal focus.
- Write code in: update
- Test and commit
- Run
npm run lint,npm test,npm run build, andnpm run test:e2e. - Cover edge cases: 0%, 100%, unknown-progress fallback, and reduced motion.
- Include the full
npm testoutput and confirmation the toast e2e still passes.
feat: add accessible upload progress bar to uploadzone
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Allow resetting UploadZone to upload another invoice after success" labels: type:enhancement, area:upload, stack:nextjs, stack:react, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
After a successful submission components/UploadZone.jsx stays in the success status with the previous file still selected, and the submit button relabels to "Upload & Tokenize Invoice" while still pointing at the already-submitted file β there is no clean way to start a fresh upload without reloading the page. This issue adds an explicit reset that clears the file, error, and status back to idle.
- Repository scope: Liquifact/Liquifact-frontend only.
- Add an "Upload another invoice" button shown in the
successstate that resetsfile,error, andstatusto their initial values and clears the file<input>. - Move focus to the dropzone after reset so keyboard users can immediately start again.
- Keep the existing status copy and
role="status"/role="alert"regions; the success message should clear on reset. - Do not change the disabled/
aria-disabledlogic on#invoice-upload-btnfor the idle/processing states.
- Fork the repo and create a branch
git checkout -b enhancement/upload-05-reset-flow- Implement changes
- Write code in: update
components/UploadZone.jsx. - Write comprehensive tests in: extend
components/UploadZone.test.jsxβ assert reset clears file/status and restores focus. - Add documentation: note the reset flow in
README.md. - Add comments explaining the focus-management choice.
- Validate a11y: focus lands on a sensible target after reset.
- Write code in: update
- Test and commit
- Run
npm run lint,npm test, andnpm run build. - Cover edge cases: reset after success, reset clears stale error, and re-upload after reset.
- Include the full
npm testoutput.
feat: add upload-another reset flow to uploadzone
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Cap the toast stack and de-duplicate repeated messages in ToastProvider" labels: type:enhancement, area:toast, stack:nextjs, stack:react, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
components/ToastProvider.jsx prepends every new toast to an unbounded array ([toast, ...current]) with no limit and no de-duplication, so a retry loop or rapid wallet errors (see the random-outcome flow in components/WalletStatus.jsx) can stack dozens of identical toasts and cover the viewport. This issue caps the visible stack and collapses duplicates.
- Repository scope: Liquifact/Liquifact-frontend only.
- Add a
MAX_TOASTSlimit (e.g. 3); when exceeded, drop the oldest toast and clear its timer intimers. - De-duplicate by
variant + title + message: if an identical toast is already visible, refresh its auto-dismiss timer instead of adding a new one. - Preserve the public
success/error/infoAPI, theAUTO_DISMISS_MSbehaviour, and pause/resume on hover. - Keep the
role="status"/aria-live="polite"live region and ensure timer cleanup remains leak-free.
- Fork the repo and create a branch
git checkout -b enhancement/toast-06-cap-and-dedupe- Implement changes
- Write code in: update
components/ToastProvider.jsx. - Write comprehensive tests in: create
components/ToastProvider.dedupe.test.tsxusing fake timers β assert cap eviction, duplicate collapse, and no timer leaks. - Add documentation: note the queue limits in
README.md. - Add JSDoc/comments on
MAX_TOASTSand the dedupe key. - Validate a11y: stacked toasts remain individually dismissible.
- Write code in: update
- Test and commit
- Run
npm run lint,npm test, andnpm run build. - Cover edge cases: burst of identical toasts, cap eviction during hover-pause, and unmount mid-timer.
- Include the full
npm testoutput.
feat: cap toast stack and de-duplicate repeated messages
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Make the marketplace error state retryable with a working ErrorBanner action" labels: type:enhancement, area:invest, stack:nextjs, stack:react, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
When loadInvoices rejects, app/invest/page.js renders an ErrorBanner with only title/description/previewLabel β but components/ErrorBanner.jsx already supports actionLabel/onAction, which is unused here. The user has no way to recover except a full page reload. This issue adds a "Try again" action that re-runs the load.
- Repository scope: Liquifact/Liquifact-frontend only.
- Refactor
InvestMarketplaceso the load logic is callable on demand (e.g. areload()that resets state to loading and re-invokesloadInvoices). - Pass
actionLabel="Try again"andonAction={reload}to theErrorBannerso the existing action button is used. - Reset
loadError, setinvoicesback tonull(loading), and re-announce via the polite status region on retry. - Continue to honour the
AbortController/isActivecancellation so a retry does not race a stale request.
- Fork the repo and create a branch
git checkout -b enhancement/invest-07-retryable-error- Implement changes
- Write code in: update
app/invest/page.js. - Write comprehensive tests in: extend
app/invest/page.test.jsxβ fail then succeed on retry, assert the loading skeleton reappears and the list renders. - Add documentation: note the retry behaviour in
README.md. - Add comments on the reload/abort interaction.
- Validate a11y: the retry button has a clear accessible name and focus is sensible after retry.
- Write code in: update
- Test and commit
- Run
npm run lint,npm test, andnpm run build. - Cover edge cases: retry-after-failure success, retry-then-failure, and rapid double retry.
- Include the full
npm testoutput.
feat: add retry action to the invest marketplace error state
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add a responsive mobile navigation menu to the app header" labels: type:feature, area:navigation, stack:nextjs, stack:react, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
The headers in app/page.js, app/invoices/page.js, and app/invest/page.js place the brand and a "Connect Wallet" button in a single flex row with no links to the Invoices/Invest sections and no mobile menu β on narrow viewports there is no navigation affordance at all. This issue adds an accessible responsive nav with a disclosure menu on mobile.
- Repository scope: Liquifact/Liquifact-frontend only.
- Create
components/NavMenu.jsxwith links to Home, Invoices (/invoices), and Invest (/invest), rendered inline on desktop and behind an accessible toggle button (aria-expanded,aria-controls) on mobile. - Mark the current route as
aria-current="page". - Close the menu on
Escapeand on navigation, and trap/return focus appropriately when open. - Keep the existing slate/cyan styling and the brand wordmark/back-link semantics; do not duplicate the static Connect Wallet button (reference the wallet UI rather than re-adding dead buttons).
- Fork the repo and create a branch
git checkout -b feature/navigation-08-responsive-nav- Implement changes
- Write code in: create
components/NavMenu.jsx; update the page headers inapp/page.js,app/invoices/page.js, andapp/invest/page.js. - Write comprehensive tests in: create
components/NavMenu.test.tsxβ assert toggle,aria-expanded, Escape-to-close, andaria-current. - Add documentation: document
NavMenuinREADME.md. - Add JSDoc on the component props.
- Validate a11y: menu is keyboard-operable with no
jest-axeviolations.
- Write code in: create
- Test and commit
- Run
npm run lint,npm test, andnpm run build. - Cover edge cases: open/close, Escape, current-route marking, and focus return.
- Include the full
npm testoutput.
feat: add responsive mobile navigation menu to the header
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Externalize copy into a typed i18n dictionary and remove inline strings" labels: type:refactor, area:i18n, stack:nextjs, stack:react, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
app/copy/en.js holds some strings, but many user-facing strings are still inlined: the marketplace yield disclaimer and filter labels in app/invest/page.js, the upload status copy in components/UploadZone.jsx, and the wallet helper texts in components/WalletStatus.jsx. This drift makes copy edits and future localization error-prone. This issue moves all visible strings into the dictionary and gives it a typed shape.
- Repository scope: Liquifact/Liquifact-frontend only.
- Extend
app/copy/en.jswith keys for the upload statuses, wallet states/helper texts, marketplace filter labels, and the yield disclaimer. - Replace the inline literals in the components/pages above with dictionary lookups.
- Add a JSDoc
@typedef(or acopy.types.js) describing the dictionary shape so missing keys are catchable. - No visible copy or behaviour change β strings must render identically.
- Fork the repo and create a branch
git checkout -b refactor/i18n-09-externalize-copy- Implement changes
- Write code in: update
app/copy/en.js,app/invest/page.js,components/UploadZone.jsx,components/WalletStatus.jsx. - Write comprehensive tests in: create
app/copy/en.test.tsxasserting key presence, and snapshot-check a couple of components for unchanged text. - Add documentation: document the copy/i18n convention in
README.md. - Add a JSDoc typedef for the dictionary.
- Validate: grep confirms no remaining inline user-facing strings in the touched files.
- Write code in: update
- Test and commit
- Run
npm run lint,npm test, andnpm run build. - Cover edge cases: every referenced key exists; rendered text is byte-identical.
- Include the full
npm testoutput and a before/after string inventory.
refactor: externalize ui copy into a typed dictionary
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Lazy-load WalletStatus to keep wallet code off the initial bundle" labels: type:refactor, area:performance, stack:nextjs, stack:react, stack:typescript, priority:low, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
components/WalletStatus.jsx is a client component that will soon pull in the Stellar/Freighter SDK, yet once mounted in the shared header it would ship in the first-load JS of every route β including the static home page that does not need wallet logic immediately. This issue lazy-loads the wallet UI so it is fetched on demand without blocking initial render.
- Repository scope: Liquifact/Liquifact-frontend only.
- Use
next/dynamicto loadWalletStatus(withssr: falsewhere appropriate) behind a small static placeholder that matches its dimensions to avoid layout shift. - Keep the exported
WALLET_STATESimport path stable for tests. - Ensure the accessible status region still mounts once the chunk loads and that no hydration warnings appear.
- Document the measured first-load JS impact (before/after) using
npm run buildoutput.
- Fork the repo and create a branch
git checkout -b refactor/performance-10-lazy-walletstatus- Implement changes
- Write code in: update the header/consumer that renders
WalletStatus(e.g.app/page.js) to use a dynamic import wrapper. - Write comprehensive tests in: create
components/WalletStatus.lazy.test.tsxβ assert the placeholder renders then the wallet UI appears. - Add documentation: note the code-splitting decision in
README.mdwith before/after bundle numbers. - Add comments explaining the
ssr/placeholder choice. - Validate: no CLS from the placeholder swap.
- Write code in: update the header/consumer that renders
- Test and commit
- Run
npm run lint,npm test, andnpm run build. - Cover edge cases: placeholder dimensions, hydration, and lazy mount.
- Include the
npm run buildfirst-load JS comparison.
perf: lazy-load walletstatus to reduce initial bundle
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Extract a shared Button component to unify variants and focus styles" labels: type:refactor, area:components, stack:nextjs, stack:react, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
Button styling is copy-pasted across the app with subtly different focus rings: the cyan pill in app/page.js, the disabled filter buttons in app/invest/page.js, the submit button and Spinner in components/UploadZone.jsx, the variant switch in components/WalletStatus.jsx, and the action button in components/ErrorBanner.jsx. They diverge (focus:ring vs focus-visible:outline), which is exactly the inconsistency a shared component prevents. This issue introduces a single Button.
- Repository scope: Liquifact/Liquifact-frontend only.
- Create
components/Button.jsxsupportingvariant(primary/secondary/warning/external/danger),loading(renders the shared spinner),disabled, and forwarded props/ref. - Apply one consistent
focus-visibleoutline across all variants. - Migrate the buttons in the files above to use
Buttonwithout visual regressions. - Export the shared
SpinnerfromButton(or acomponents/Spinner.jsx) and stop redefining the inline SVG inWalletStatus/UploadZone.
- Fork the repo and create a branch
git checkout -b refactor/components-11-shared-button- Implement changes
- Write code in: create
components/Button.jsx(and optionallycomponents/Spinner.jsx); update the consumers listed above. - Write comprehensive tests in: create
components/Button.test.tsxβ assert variants, disabled/loading, and focus styles. - Add documentation: document
ButtoninREADME.mdUI section. - Add JSDoc on the props.
- Validate a11y: focus-visible outline meets contrast; loading sets
aria-busy.
- Write code in: create
- Test and commit
- Run
npm run lint,npm test, andnpm run build. - Cover edge cases: each variant, loading state, and disabled state.
- Include the full
npm testoutput and a before/after note confirming no visual change.
refactor: extract shared button and spinner components
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add behavioral unit tests for the ErrorBanner component" labels: type:test, area:error-banner, stack:nextjs, stack:react, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
components/ErrorBanner.jsx has only an a11y smoke test in components/__tests__/ErrorBanner.a11y.test.jsx; its variant label switch (server vs validation), conditional details, and the actionLabel/onAction button are not behaviorally tested. This issue adds full coverage of its rendering contract.
- Repository scope: Liquifact/Liquifact-frontend only.
- Assert
variantLabelrenders "Server error" forserver/default and "Validation error" forvalidation. - Assert
title/description/previewLabelrender, thatdetailsonly renders when provided, and that the action button appears only whenactionLabelis set and callsonActionon click. - Assert the
role="alert"/aria-live="assertive"container is present. - Use
@testing-library/user-eventfor the action click.
- Fork the repo and create a branch
git checkout -b test/error-banner-12-behavioral-coverage- Implement changes
- Write code in: no source change unless a bug is found in
components/ErrorBanner.jsx. - Write comprehensive tests in: create
components/ErrorBanner.test.tsx. - Add documentation: none beyond test comments.
- Add comments documenting the variant matrix.
- Validate a11y: keep an axe check alongside the behavioral assertions.
- Write code in: no source change unless a bug is found in
- Test and commit
- Run
npm run lint,npm test, andnpm run build. - Cover edge cases: missing
details, missingactionLabel, and both variants. - Include the full
npm testoutput with ErrorBanner coverage.
test: cover errorbanner variants and action behaviour
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add unit tests for the getInvoiceLoadAnnouncement helper and InvestMarketplace states" labels: type:test, area:invest, stack:nextjs, stack:react, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
app/invest/page.js exports getInvoiceLoadAnnouncement and an injectable InvestMarketplace, but app/invest/page.test.jsx does not cover the helper's branches directly nor the abort/unmount path in the effect. This issue adds focused coverage for the pure helper and the loading/empty/error/non-array states.
- Repository scope: Liquifact/Liquifact-frontend only.
- Unit-test
getInvoiceLoadAnnouncementfor: non-array input, empty array, and N>0 (assert exact strings "No invoices available" / "N investable invoices loaded"). - Test
InvestMarketplacewith injected loaders that resolve to a list, an empty array, a non-array (coerced to[]), and a rejected promise (error banner + announcement). - Assert the skeleton renders while the loader is pending and that unmount during a pending load does not throw or set state (the
isActiveguard). - Reuse the injectable
loadInvoicesprop already used by the existing test.
- Fork the repo and create a branch
git checkout -b test/invest-13-announcement-and-states- Implement changes
- Write code in: no source change unless a bug is found in
app/invest/page.js. - Write comprehensive tests in: extend
app/invest/page.test.jsxor createapp/invest/announcement.test.tsx. - Add documentation: none beyond test comments.
- Add comments explaining the unmount-during-fetch assertion.
- Validate a11y: assert the polite status region content for each state.
- Write code in: no source change unless a bug is found in
- Test and commit
- Run
npm run lint,npm test, andnpm run build. - Cover edge cases: non-array, empty, populated, rejected, and unmount-mid-load.
- Include the full
npm testoutput with invest coverage.
test: cover getinvoiceloadannouncement and marketplace load states
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add unit tests for the InvoiceListSkeleton row count and loading semantics" labels: type:test, area:loading-states, stack:nextjs, stack:react, stack:typescript, priority:low, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
components/InvoiceListSkeleton.jsx renders rows placeholder list items with aria-busy="true" and an "Loading investable invoices" label, but components/InvoiceListSkeleton.test.jsx is thin. This issue ensures the row count, default, and loading semantics are locked in so the skeleton stays in sync with the real card layout.
- Repository scope: Liquifact/Liquifact-frontend only.
- Assert the component renders exactly
rows<li>items and falls back to the documented default whenrowsis omitted. - Assert the list carries
aria-busy="true"and thearia-label"Loading investable invoices". - Assert each row contains the issuer/status and amount/yield/maturity placeholder blocks so a future card refactor cannot silently drop columns.
- Cover
rows={0}(no items, list still present).
- Fork the repo and create a branch
git checkout -b test/loading-states-14-skeleton-coverage- Implement changes
- Write code in: no source change unless a bug is found in
components/InvoiceListSkeleton.jsx. - Write comprehensive tests in: extend
components/InvoiceListSkeleton.test.jsxor createcomponents/InvoiceListSkeleton.contract.test.tsx. - Add documentation: none beyond test comments.
- Add comments clarifying the placeholder-structure assertions.
- Validate a11y: assert
aria-busyand the loading label.
- Write code in: no source change unless a bug is found in
- Test and commit
- Run
npm run lint,npm test, andnpm run build. - Cover edge cases: default rows, custom rows, and
rows={0}. - Include the full
npm testoutput.
test: cover invoicelistskeleton row count and loading semantics
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add unit tests for the Invoices page upload integration and header" labels: type:test, area:invoices, stack:nextjs, stack:react, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
app/invoices/page.js renders the title, subtext, back link, a Connect Wallet button, and an <UploadZone />, but has no unit test, so a broken copy key, a missing UploadZone mount, or a regressed back-link focus-visible style would go unnoticed. This issue adds coverage for the page composition.
- Repository scope: Liquifact/Liquifact-frontend only.
- Assert the heading and subtext from
copy.invoicesrender and the "β LiquiFact" link points to/. - Assert the
UploadZoneform mounts (e.g. the#invoice-file-inputand#invoice-upload-btnare present). - Assert the back link carries the
focus-visibleoutline classes that distinguish this page from the others. - Keep the test resilient to
UploadZoneinternals by querying roles/ids rather than implementation details.
- Fork the repo and create a branch
git checkout -b test/invoices-15-page-coverage- Implement changes
- Write code in: no source change unless a bug is found in
app/invoices/page.js. - Write comprehensive tests in: create
app/invoices/page.test.tsx. - Add documentation: none beyond test comments.
- Add comments on how UploadZone is queried.
- Validate a11y: optional
jest-axesmoke check on the page.
- Write code in: no source change unless a bug is found in
- Test and commit
- Run
npm run lint,npm test, andnpm run build. - Cover edge cases: copy rendering, UploadZone mount, and back-link target.
- Include the full
npm testoutput.
test: cover the invoices page composition and uploadzone wiring
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Make WalletStatus open the wallet install page safely via rel=noopener" labels: type:security, area:wallet, stack:nextjs, stack:react, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
In the NO_WALLET state, components/WalletStatus.jsx opens the install page with window.open('https://www.stellar.org/wallets', '_blank') β without the noopener,noreferrer window features, the opened tab can access window.opener (reverse-tabnabbing), and an untrusted URL would flow straight through. This issue makes the external navigation safe and centralizes the allowed destination.
- Repository scope: Liquifact/Liquifact-frontend only.
- Pass
'noopener,noreferrer'as thewindow.openfeatures argument and null the returned reference'sopeneras a defensive fallback. - Move the install URL into a single trusted constant (or
app/copy/en.js) and validate it is anhttps:origin before opening. - Preserve the
WALLET_STATES.NO_WALLETbutton behaviour and accessibility. - Audit any other
window.open/external anchors introduced here for the same protection.
- Fork the repo and create a branch
git checkout -b security/wallet-16-safe-external-open- Implement changes
- Write code in: update
components/WalletStatus.jsx; add the URL constant where appropriate. - Write comprehensive tests in: create
components/WalletStatus.external.test.tsxβ mockwindow.openand assert the features string and URL. - Add documentation: note the external-link policy in
README.md. - Add comments explaining the tabnabbing mitigation.
- Validate security: confirm
noopeneris set and onlyhttps:URLs are opened.
- Write code in: update
- Test and commit
- Run
npm run lint,npm test, andnpm run build. - Cover edge cases: correct features argument and rejected non-https URL.
- Include the full
npm testoutput and a short threat note.
fix: open wallet install page with noopener to prevent tabnabbing
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Bound and safely render the backend health JSON on the home page" labels: type:security, area:home, stack:nextjs, stack:react, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
app/page.js takes whatever the backend returns from /health and dumps it via JSON.stringify(health, null, 2) into a <pre> with overflow-auto β an attacker-controlled or compromised backend could return an enormous payload (DoS via giant render) or deeply nested data that bloats the page. While React escapes text, the size and shape are unbounded. This issue bounds and sanitizes what is rendered.
- Repository scope: Liquifact/Liquifact-frontend only.
- Cap the rendered payload size (e.g. truncate the stringified output to a max length with an explicit "β¦(truncated)" marker) and limit object depth before display.
- Render a small set of recognized fields (e.g.
status,message,version) in a structured way and keep the raw payload behind a collapsible<details>rather than always-expanded. - Never render the response as HTML; keep it as text content only.
- Coordinate gracefully with the health-check helper if present, without duplicating fetch logic.
- Fork the repo and create a branch
git checkout -b security/home-17-bounded-health-render- Implement changes
- Write code in: update
app/page.js; optionally add alib/format/safeJson.jshelper. - Write comprehensive tests in: create
app/page.health-render.test.tsxand/orlib/format/safeJson.test.tsxβ assert truncation, depth limit, and recognized-field rendering. - Add documentation: note the bounded rendering in
README.md. - Add JSDoc on the formatting helper.
- Validate security: confirm large/nested payloads cannot blow up the DOM.
- Write code in: update
- Test and commit
- Run
npm run lint,npm test, andnpm run build. - Cover edge cases: huge string, deep nesting, and a normal healthy payload.
- Include the full
npm testoutput and a short note on the limits chosen.
fix: bound and safely render backend health json on the home page
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add a Dependabot config and lockfile-integrity check for the frontend" labels: type:security, area:dependencies, stack:nextjs, stack:react, stack:typescript, priority:low, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
The repo pins next, react, and a set of dev tooling in package.json with a committed package-lock.json, but there is no .github/dependabot.yml and CI (.github/workflows/ci.yml) runs npm ci without verifying the lockfile is in sync. Without automated updates, security patches for Next.js/React/Playwright land late. This issue adds Dependabot and a lockfile-drift guard.
- Repository scope: Liquifact/Liquifact-frontend only.
- Add
.github/dependabot.ymlconfiguring weeklynpmecosystem updates (andgithub-actionsecosystem) grouped sensibly to limit PR noise. - Add a CI step asserting
package-lock.jsonis in sync (e.g.npm cifails on drift, or an explicitnpm install --package-lock-onlydiff check) so PRs cannot merge with a stale lockfile. - Keep existing lint/test jobs intact.
- Document how to review and merge Dependabot PRs against the campaign workflow.
- Fork the repo and create a branch
git checkout -b security/dependencies-18-dependabot-lockfile- Implement changes
- Write code in: create
.github/dependabot.yml; update.github/workflows/ci.yml. - Write comprehensive tests in: validate via a CI run on the PR; add a documented local lockfile-drift command.
- Add documentation: add a "Dependency updates" subsection to
README.md. - Add comments in the workflow explaining the lockfile gate.
- Validate security: ensure the config does not auto-merge without review.
- Write code in: create
- Test and commit
- Run
npm run lint,npm test,npm run build, andnpm cilocally. - Cover edge cases: clean lockfile passes; an intentionally drifted lockfile fails CI.
- Include the CI run link/output.
ci: add dependabot config and lockfile-integrity check
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Fix the inaccurate README project structure, component props, and design tokens" labels: type:docs, area:readme, stack:nextjs, stack:react, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
README.md is out of sync with the code in several places: the "Project structure" tree omits components/, app/copy/, and the test/Playwright setup; the CI/CD section lists "Lint" and "Build" but the workflow actually runs lint and npm test (accessibility), not build; the WalletStatus props are documented as a status prop it does not accept; ErrorBanner props are wrong (it has variant/title/description/details/actionLabel/onAction/previewLabel, not message); and the Design Tokens claim Geist is imported via @fontsource/geist when app/layout.js uses next/font/google. This issue makes the README accurate.
- Repository scope: Liquifact/Liquifact-frontend only.
- Update the project-structure tree to reflect real folders/files (
components/,app/copy/en.js,tests/,jest.config.js,playwright.config.mjs,next.config.mjs). - Fix the CI/CD section to match
.github/workflows/ci.yml(lint + accessibility tests). - Correct the
WalletStatusandErrorBannerprop docs against their real signatures. - Fix the Design Tokens section: Geist via
next/font/google, and reconcile the listed color hexes with the actual slate/cyan palette.
- Fork the repo and create a branch
git checkout -b docs/readme-19-accuracy-fixes- Implement changes
- Write code in: docs-only.
- Write comprehensive tests in: not applicable; cross-check every claim against source.
- Add documentation: update
README.md. - Add a short "last verified against commit" note.
- Validate accuracy by diffing each statement against the corresponding file.
- Test and commit
- Run
npm run lintandnpm run buildto confirm nothing breaks. - Cover edge cases: every documented prop/path exists in the code.
- Include a rendered preview of the corrected sections.
docs: correct readme structure, component props, and design tokens
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Add an accessibility statement documenting the app's a11y commitments" labels: type:docs, area:accessibility, stack:nextjs, stack:react, stack:typescript, priority:low, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
The codebase invests heavily in accessibility β jest-axe wired in jest.setup.js, role="status"/aria-live regions in components/UploadZone.jsx, components/WalletStatus.jsx, and app/invest/page.js, and a CI step literally named "Test Accessibility" in .github/workflows/ci.yml β but there is no document explaining the standards targeted, the testing approach, or the known gaps (e.g. disabled filter buttons, motion handling). This issue writes an accessibility statement.
- Repository scope: Liquifact/Liquifact-frontend only.
- Create
docs/accessibility.mdstating the target standard (WCAG 2.1 AA), the keyboard/screen-reader patterns used, and howjest-axeis run in CI. - List known limitations and link to the relevant components/issues (disabled "Soon" filters, reduced-motion handling, focus styles).
- Provide a contributor checklist for keeping new UI accessible (labels, landmarks, focus, live regions, contrast).
- Link the statement from
README.md.
- Fork the repo and create a branch
git checkout -b docs/accessibility-20-a11y-statement- Implement changes
- Write code in: docs-only.
- Write comprehensive tests in: not applicable; ensure referenced patterns exist in code.
- Add documentation: create
docs/accessibility.mdand link fromREADME.md. - Add a maintenance note on updating the statement when a11y issues close.
- Validate accuracy against the current components and CI.
- Test and commit
- Run
npm run lintandnpm run build. - Cover edge cases: every referenced pattern and CI step is accurate.
- Include a rendered preview of the new doc.
docs: add accessibility statement and contributor checklist
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Document environment variables and add the missing test/test:e2e to the README scripts" labels: type:docs, area:configuration, stack:nextjs, stack:react, stack:typescript, priority:low, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
.env.local.example defines NEXT_PUBLIC_API_URL (and a commented NEXT_PUBLIC_STELLAR_NETWORK), but README.md only mentions NEXT_PUBLIC_API_URL in passing and its Development command table omits npm test even though package.json defines it (and CI runs it). This issue creates a clear environment-variable reference and aligns the documented scripts with the real ones.
- Repository scope: Liquifact/Liquifact-frontend only.
- Add an "Environment variables" table to
README.mdlisting each var, whether it is required, its default, and what reads it (link toapp/page.jsfor the API URL andWALLET_INTEGRATION_CONTRACT.mdfor the network). - Add
npm test(Jest/jsdom) to the Development command table alongsidetest:e2e. - Ensure
.env.local.exampleand the README stay consistent (same var names/defaults). - Note that
NEXT_PUBLIC_*values are exposed to the client and must not hold secrets.
- Fork the repo and create a branch
git checkout -b docs/configuration-21-env-and-scripts- Implement changes
- Write code in: docs-only; optionally clarify comments in
.env.local.example. - Write comprehensive tests in: not applicable; verify documented defaults match the code fallbacks.
- Add documentation: update
README.mdand.env.local.example. - Add a security note on
NEXT_PUBLIC_exposure. - Validate accuracy against
package.jsonscripts and the env reads in code.
- Write code in: docs-only; optionally clarify comments in
- Test and commit
- Run
npm run lint,npm test, andnpm run build. - Cover edge cases: documented defaults match
app/page.js; every script in the table exists. - Include a rendered preview of the env section.
docs: document environment variables and align readme scripts
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward. ++++++
type: Feature title: "Give the toast close button reliable Escape-to-dismiss and focus handling" labels: type:a11y, area:toast, stack:nextjs, stack:react, stack:typescript, priority:medium, MAYBE REWARDED, GRANTFOX OSS, OFFICIAL CAMPAIGN assignees: ''
Toasts in components/ToastProvider.jsx can only be dismissed by clicking "Close" or waiting for the timer; there is no keyboard shortcut to dismiss, and hovering pauses the timer but focusing (keyboard equivalent of hover) does not β so a keyboard user reading a toast can have it disappear mid-read. This issue adds keyboard parity: pause on focus, dismiss on Escape, and sensible focus return.
- Repository scope: Liquifact/Liquifact-frontend only.
- Pause/resume the auto-dismiss timer on
focus/blurof the toast (mirroring the existingmouseenter/mouseleavepause/resume) so focusing a toast does not let it vanish. - Allow
Escapeto dismiss the focused toast. - Ensure the "Close" button and toast are reachable in a sensible tab order and that focus moves to a safe element after dismissal (no focus loss to
body). - Keep the
role="status"/aria-live="polite"region and all existing variant styling.
- Fork the repo and create a branch
git checkout -b a11y/toast-22-keyboard-dismiss- Implement changes
- Write code in: update
components/ToastProvider.jsx. - Write comprehensive tests in: create
components/ToastProvider.keyboard.test.tsxusinguser-eventand fake timers β assert focus pauses the timer, Escape dismisses, and focus return. - Add documentation: note the keyboard behaviour in
README.md. - Add comments on the focus/blur pause parity.
- Validate a11y: no
jest-axeviolations and keyboard-operable dismissal.
- Write code in: update
- Test and commit
- Run
npm run lint,npm test, andnpm run build. - Cover edge cases: focus-pause, Escape dismiss, and focus return after close.
- Include the full
npm testoutput.
fix: add escape-to-dismiss and focus pause to toast notifications
- Minimum 95 percent test coverage for impacted modules.
- Clear, reviewer-focused documentation.
- Timeframe: 96 hours.
- π¬ Join the Liquifact community on Discord for questions, reviews, and faster merges: https://discord.gg/JrGPH4V3
- β This is a GrantFox OSS / Official Campaign task and may be rewarded. When your PR is merged you'll be prompted to rate the project β if this issue and the maintainers helped you ship, we'd be grateful for a 5-star rating. Clear questions in Discord and tidy, well-tested PRs are the fastest path to a merge and a reward.