Implement the shared setup-failure notice
- Dominant language
- JavaScript
- Stars
- 1.4k
- Forks
- 383
- Avg merge
- 4d 14h
- Merged PRs (30d)
- 77
Description
## Feature Description
Some of the setups the hub starts can fail while the user is still looking at the hub: a service that can't be activated before they're handed off to its setup (#13338), or a feature that's switched on in the background and whose save doesn't land. However different those features are, from the user's point of view the same thing has happened — the thing they clicked didn't work — so it should be reported the same way each time, in the place they clicked.
This issue adds that one notice. It takes the failing feature's place in the list, names the feature so it stands on its own where the card was, says what went wrong, and offers a retry. Dismissing it puts the card back, because the feature still isn't set up and the user may well want another go later.
Two cases sit outside it. Once the user has been handed off to a service's setup flow they're no longer on the hub, and errors there are that flow's own business. And a feature that brings its own setup UI reports its own failures — Visitor groups is the only one, covered by #13344.
For reference, see the [Setup failures](https://docs.google.com/document/d/1sLWcimi6eZqbK4YXVCZtO0ZrfrDV7Ub218Hu_vpwmyI/edit?tab=t.0#heading=h.cls9bhpcupwo) section in the design doc.
---------------
_Do not alter or remove anything below. The following sections will be managed by moderators only._
## Acceptance criteria
- When a setup the hub is running itself fails while the user is still on the hub, the feature's card is replaced by an error notice, in the place that card occupied in the list, on both tabs. The failure it reports at this point is a feature's Google service failing to activate before the user is handed off to its setup (#13338).
- The notice's title names the feature that failed — *"Analytics setup failed"*, *"Enhanced measurement setup failed"*, and so on for any feature.
- Its body says why the setup failed, as Site Kit reports setup errors elsewhere: the message from the service, followed by a *Learn more* link to the troubleshooting page for that error, and with the *redo the plugin setup* link where the error is one that calls for reconnecting Site Kit. Unlike Site Kit's other error notices, it doesn't add the *"Error:"* prefix or the *"(Please try again.)"* suffix — the title and the retry make them redundant.
- Where the setup failed for want of permissions, the body says that instead: that the settings couldn't be configured due to insufficient permissions, and to contact your administrator or *get help*.
- **Retry** runs the same setup again. It isn't offered for failures where retrying can't help.
- **Got it** puts the feature's card back, in its normal pre-setup state.
- Nothing about the failure is remembered: reloading the hub shows the feature's card as normal. The feature isn't dismissed, and it isn't marked in any way.
- Starting a setup from the detail panel (#13330) closes the panel, so a failure from there shows in the feature's place in the list behind it, just as it does for a setup started from the card.
- The notice is built to serve every setup the hub runs itself, so the features enabled in place report their failures through it when they're built (#13341) rather than each bringing a notice of its own.
## Implementation Brief
- [ ] In `assets/js/util/errors.ts`:
- Extract the message-formatting logic currently embedded in `ErrorNotice.tsx` (the "Error: " prefix, the "(Please try again.)" suffix, and the `reconnectURL` "redo the plugin setup" sentence) into an exported `getErrorNoticeMessage( message, error, { noPrefix, skipRetryMessage } )` helper that returns the final (possibly HTML) string; update `ErrorNotice.tsx` to call it, with no change in its existing behaviour/tests.
- Extract the non-retryable-error checks out of `isErrorRetryable()` (`isInsufficientPermissionsError` / `isPermissionScopeError` / `isAuthError`) into an exported `isRetryableErrorType( error )` helper; have `isErrorRetryable()` call it internally, so its existing resolver-based contract and tests are unaffected.
- `FeatureSetupErrorNotice` (below) uses `isRetryableErrorType()` directly rather than `isErrorRetryable()`, since its retry re-dispatches `setupFeature()` rather than invalidating a selector resolution.
- [ ] Create `assets/js/components/feature-discovery/FeatureSetupErrorNotice.tsx`:
- Props: `slug: string`, `error: object`, `onRetry(): void`, `onDismiss(): void`, `isRetrying?: boolean`.
- Read the failed feature via `useSelect( CORE_FEATURE_DISCOVERY ).getFeature( slug )` for its `title`.
- Title: the feature name plus "setup failed" (e.g. "Analytics setup failed", "Enhanced measurement setup failed") for any feature — don't hardcode per-feature copy.
- Description:
- Where `isInsufficientPermissionsError( error )`: fixed copy stating the settings couldn't be configured due to insufficient permissions, with a "contact your administrator or get help" sentence, "get help" linking to `select( CORE_SITE ).getErrorTroubleshootingLinkURL( error )` — mirrors the wording pattern used by `PermissionsErrorNotice` and `AudienceSegmentationSetupErrorWidget`.
- Otherwise: `getErrorNoticeMessage( error.message, error, { noPrefix: true, skipRetryMessage: true } )` (no "Error:" prefix, no "(Please try again.)" suffix — the title and Retry already make them redundant, per AC), followed by a "Learn more" link to the same troubleshooting URL.
- Render via the shared `Notice` (`NOTICE_TYPES.ERROR`):
- `ctaButton`: `{ label: __( 'Retry' ), onClick: onRetry, inProgress: isRetrying }`, included only when `isRetryableErrorType( error )` is true.
- `dismissButton`: `{ label: __( 'Got it' ), onClick: onDismiss }`.
- [ ] In `assets/js/components/feature-discovery/FeatureListItem.tsx` (built by #13320):
- Add `const [ setupError, setSetupError ] = useState()` and an `isRetrying` flag.
- Add a `handleRetry` that sets `isRetrying`, dispatches `CORE_FEATURE_DISCOVERY.setupFeature( slug )` again, and on a result carrying `.error` calls `setSetupError( error )`, otherwise clears it (`setSetupError( undefined )`) — the same call `FeatureCTA` itself makes.
- Where `setupError` is set, render ` setSetupError( undefined ) } isRetrying={ isRetrying } />` in the card's slot — on both tabs — in place of ``.
- Pass an `onSetupError={ setSetupError }` callback down to `FeatureCard` (→ `FeatureCTA`).
- `setupError` is local component state, so a hub reload always remounts to no error.
- Starting setup from the detail panel closes the panel and runs through this same `FeatureCTA`/`onSetupError` path, so a failure from there surfaces in this same list-item slot with no extra handling.
- [ ] In `assets/js/components/feature-discovery/FeatureCTA.tsx` (added in #13322):
- Accept an `onSetupError?: ( error ) => void` prop.
- After `await dispatch( CORE_FEATURE_DISCOVERY ).setupFeature( slug )` resolves, check the result for `error`; where present, call `onSetupError?.( result.error )`.
- [ ] In `assets/js/components/feature-discovery/FeatureCard.tsx` (Added in #13320):
- Forward the `onSetupError` prop through to `FeatureCTA`.
### Test Coverage
- Add unit tests for the two new `util/errors.ts` helpers (`getErrorNoticeMessage`, `isRetryableErrorType`); confirm `ErrorNotice`'s and `isErrorRetryable`'s existing tests still pass unchanged.
- Add `FeatureSetupErrorNotice` unit tests and Storybook stories/VRT — a generic retryable error, an insufficient-permissions error (no Retry button), and the reconnect-URL variant — modelled on `AudienceSegmentationSetupErrorWidget`'s tests.
- Extend `FeatureListItem`/`FeatureCTA` test coverage to cover: a failed `setupFeature()` call swapping the card for the notice on both tabs, Retry re-running setup, "Got it" restoring the card to its normal pre-setup state, and a failure started from the detail panel surfacing in the list behind it.
## QA Brief
-
## Changelog entry
-
Contributor guide
Research direction
Start with the existing message and retryability logic in assets/js/util/errors.ts and ErrorNotice.tsx, then read FeatureListItem.tsx, FeatureCTA.tsx, and FeatureCard.tsx. Model the new FeatureSetupErrorNotice.tsx tests and stories on AudienceSegmentationSetupErrorWidget. Done means setup failures replace the card on both tabs, Retry and Got it behave as specified, detail-panel failures surface in the list, and the listed unit tests and VRT coverage pass.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100