Skip to content

Backup hides why Run Now is disabled from touch and keyboard users #5481

Description

@atomantic

Problem

Settings > Backup correctly disables Run Backup Now when no destination has been saved, a save is in flight, or the form is dirty (client/src/components/settings/BackupTab.jsx:177 and client/src/components/settings/BackupTab.jsx:182). However, the actual reason is exposed only through the button's title attribute at client/src/components/settings/BackupTab.jsx:183 and client/src/components/settings/BackupTab.jsx:448.

The existing tests encode that hover-only contract at client/src/components/settings/BackupTab.test.jsx:190, client/src/components/settings/BackupTab.test.jsx:200, and client/src/components/settings/BackupTab.test.jsx:232. The adjacent live region at client/src/components/settings/BackupTab.jsx:457 says only Unsaved changes or Saving…; it is blank when no destination is configured and never tells the user that Run uses saved settings.

Trigger

  1. Open /settings/backup on a touch device with no saved destination, or edit any backup setting without saving it.
  2. Run Backup Now is greyed out.
  3. Touch users cannot hover the title, keyboard users receive no reliable disabled-control tooltip, and the no-destination case has no adjacent explanation at all.

The gate is necessary: POST /api/backup/run re-reads the persisted destination and excludes rather than receiving the live form (server/routes/backup.js:23). The missing piece is visible explanation, not removal of the gate.

Impact

Users see an inert primary action without a dependable remedy. In the dirty case they may know something is unsaved but not that saving is specifically required before Run; in the empty-destination case they have to infer the prerequisite from the rest of the form.

Fix

  • In client/src/components/settings/BackupTab.jsx, derive a concise visible disabled reason from the existing savedDestPath / saving / dirty state and render it beside the action bar whenever the prerequisite gate is active.
  • Give the hint a stable id and connect the disabled button with aria-describedby. The title may remain as supplementary desktop affordance, but it must not be the only explanation.
  • Keep Running… as the action's own progress feedback; do not add redundant reason text for the running state.
  • Update client/src/components/settings/BackupTab.test.jsx to assert the visible reason and accessible description for no destination, dirty state, and in-flight save, plus disappearance after a successful save.

Rejected alternative: toast when the disabled button is clicked. Native disabled buttons do not dispatch the click reliably and that still leaves touch, keyboard, and first-glance users without discoverable guidance.

Dispatch rationale: model:light and effort:low fit a localized presentation/test update over state that is already correct and well covered.

Acceptance criteria

  • With no saved destination, the page visibly says that a destination must be configured and saved before Run is available.
  • With dirty settings, the page visibly says to save because Run uses saved settings.
  • During a settings save, the page visibly says Run is waiting for the save to finish.
  • The disabled Run button references the visible explanation with aria-describedby.
  • The explanation clears when Run becomes available and does not duplicate the Running… progress state.
  • BackupTab tests verify rendered guidance rather than relying only on title.

Metadata

Metadata

Assignees

Labels

area:uiUI components and stylingeffort:lowEffort: lowmodel:lightModel size: lightplanTracked by /do:replanuxUser experience

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions