diff --git a/includes/reader-activation/class-integrations.php b/includes/reader-activation/class-integrations.php index 17ffe4a9d4..bbcb006f77 100644 --- a/includes/reader-activation/class-integrations.php +++ b/includes/reader-activation/class-integrations.php @@ -424,13 +424,14 @@ public static function get_all_integration_settings() { continue; } $result[ $id ] = [ - 'id' => $id, - 'name' => $integration->get_name(), - 'description' => $integration->get_description(), - 'enabled' => self::is_enabled( $id ), - 'is_set_up' => $integration->is_set_up(), - 'setup_url' => $integration->get_setup_url(), - 'settings' => $integration->get_settings_config(), + 'id' => $id, + 'name' => $integration->get_name(), + 'description' => $integration->get_description(), + 'enabled' => self::is_enabled( $id ), + 'is_set_up' => $integration->is_set_up(), + 'setup_url' => $integration->get_setup_url(), + 'settings' => $integration->get_settings_config(), + 'required_plugins' => $integration->get_required_plugins(), ]; } return $result; diff --git a/includes/reader-activation/integrations/class-esp.php b/includes/reader-activation/integrations/class-esp.php index 13bcbd6685..a8fb417c1b 100644 --- a/includes/reader-activation/integrations/class-esp.php +++ b/includes/reader-activation/integrations/class-esp.php @@ -64,6 +64,23 @@ public function get_setup_url() { return $newsletters_configuration_manager->get_settings_url(); } + /** + * Get the plugins this integration depends on, with their install/active status. + * + * @return array List of associative arrays with keys `slug`, `name`, `is_active`, `is_installed`. + */ + public function get_required_plugins() { + $status = \Newspack\Plugin_Manager::get_managed_plugin_status( 'newspack-newsletters' ); + return [ + [ + 'slug' => 'newspack-newsletters', + 'name' => __( 'Newspack Newsletters', 'newspack-plugin' ), + 'is_active' => 'active' === $status, + 'is_installed' => 'uninstalled' !== $status, + ], + ]; + } + /** * Register the settings fields declared by this integration. * diff --git a/includes/reader-activation/integrations/class-integration.php b/includes/reader-activation/integrations/class-integration.php index c667727afa..a91671489a 100644 --- a/includes/reader-activation/integrations/class-integration.php +++ b/includes/reader-activation/integrations/class-integration.php @@ -151,6 +151,23 @@ public function get_setup_url() { return ''; } + /** + * Get the plugins this integration depends on, with their active status. + * + * Child classes should override this to declare any plugins that must be + * active for the integration to function. The integrations UI uses this + * to surface a "requirements" affordance on the integration card. + * + * Each entry must include all of `slug`, `name`, `is_active`, and `is_installed` — + * the integrations UI treats a missing `is_installed` as uninstalled and renders + * a disabled "Requires …" card instead of the Activate action. + * + * @return array List of associative arrays with keys `slug`, `name`, `is_active`, `is_installed`. + */ + public function get_required_plugins() { + return []; + } + /** * Whether this integration supports frontend reader registration. * diff --git a/packages/components/src/card-feature/README.md b/packages/components/src/card-feature/README.md index 6c7777ec35..d691cdcbb5 100644 --- a/packages/components/src/card-feature/README.md +++ b/packages/components/src/card-feature/README.md @@ -11,7 +11,7 @@ A card component for presenting a named feature or setting with a predictable, s | State | Condition | Button | Dropdown | Badge | |---|---|---|---|---| -| **Unmet requirements** | `requirements` is set | "Enable" — disabled | Hidden | Error badge with `requirements` text | +| **Unmet requirements** | `requirements` is set | "Enable" — disabled (clickable if `requirementsActionable`) | Hidden | Error badge with `requirements` text | | **Disabled** | `!enabled`, no requirements | "Enable" | Hidden | None | | **Enabled** | `enabled`, no requirements | "Configure" | Shown if `moreControls` provided | Success badge ("Enabled") | @@ -155,6 +155,7 @@ import { __ } from '@wordpress/i18n'; | `icon` | `CardFeatureIcon` | — | Icon displayed on the right. See `CardFeatureIcon` below. | | `enabled` | `boolean` | `false` | Whether the feature is currently enabled | | `requirements` | `string` | — | When set, enters the unmet-requirements state; value is used as the error badge text | +| `requirementsActionable` | `boolean` | `false` | When `requirements` is set, keep the primary button clickable so it can remediate the unmet requirement | | `enableLabel` | `string` | `"Enable"` | Primary button label when not enabled | | `configureLabel` | `string` | `"Configure"` | Primary button label when enabled | | `onEnable` | `() => void` | — | Called when the primary button is clicked and the feature is not enabled | diff --git a/packages/components/src/card-feature/index.tsx b/packages/components/src/card-feature/index.tsx index 079ddf040f..cef651d093 100644 --- a/packages/components/src/card-feature/index.tsx +++ b/packages/components/src/card-feature/index.tsx @@ -49,10 +49,17 @@ type CardFeatureProps = { /** Whether the feature is currently enabled. */ enabled?: boolean; /** - * When set, the card enters the "unmet requirements" state: the primary - * button is disabled and an error badge displays this string. + * When set, the card enters the "unmet requirements" state: an error + * badge displays this string and the title/description are muted. By + * default the primary button is disabled — set `requirementsActionable` + * if the primary button is the remediation for the unmet requirement. */ requirements?: string; + /** + * When `requirements` is set, keep the primary button clickable so the + * user can remediate the unmet requirement from this card. + */ + requirementsActionable?: boolean; /** Primary button label when not enabled. Default: "Enable". */ enableLabel?: string; /** Primary button label when enabled. Default: "Configure". */ @@ -83,6 +90,7 @@ const CardFeature = ( { icon, enabled = false, requirements, + requirementsActionable = false, enableLabel, configureLabel, onEnable, @@ -151,7 +159,7 @@ const CardFeature = ( {