diff --git a/assets/js/components/KeyMetrics/ConfirmSitePurposeChangeModal.js b/assets/js/components/KeyMetrics/ConfirmSitePurposeChangeModal.js index af7aa186dcb..113b5b54b3e 100644 --- a/assets/js/components/KeyMetrics/ConfirmSitePurposeChangeModal.js +++ b/assets/js/components/KeyMetrics/ConfirmSitePurposeChangeModal.js @@ -50,11 +50,14 @@ import { import { CORE_UI } from '../../googlesitekit/datastore/ui/constants'; import { MODULES_ANALYTICS_4 } from '../../modules/analytics-4/datastore/constants'; import { CORE_MODULES } from '../../googlesitekit/modules/datastore/constants'; +import { trackEvent } from '../../util'; +import useViewContext from '../../hooks/useViewContext'; function ConfirmSitePurposeChangeModal( { dialogActive = false, handleDialog = null, } ) { + const viewContext = useViewContext(); const [ isSaving, setIsSaving ] = useState( false ); const includeConversionTailoredMetrics = useSelect( ( select ) => { @@ -113,6 +116,14 @@ function ConfirmSitePurposeChangeModal( { setUIValues( { [ USER_INPUT_CURRENTLY_EDITING_KEY ]: undefined, } ); + + // Handle internal tracking. + trackEvent( + `${ viewContext }_kmw-settings-tailored-metrics-suggestions`, + 'cancel_update_metrics_selection', + 'conversion_reporting' + ); + handleDialog(); }, [ handleDialog, @@ -120,6 +131,7 @@ function ConfirmSitePurposeChangeModal( { resetUserInputSettings, setValues, setUIValues, + viewContext, ] ); const userInputPurposeConversionEvents = useSelect( ( select ) => { @@ -150,6 +162,14 @@ function ConfirmSitePurposeChangeModal( { await saveUserInputSettings(); setIsSaving( false ); + + // Handle internal tracking. + trackEvent( + `${ viewContext }_kmw-settings-tailored-metrics-suggestions`, + 'confirm_update_metrics_selection', + 'conversion_reporting' + ); + onClose(); }, [ saveUserInputSettings, @@ -157,6 +177,7 @@ function ConfirmSitePurposeChangeModal( { setIsSaving, setUserInputSetting, userInputPurposeConversionEvents, + viewContext, ] ); return ( diff --git a/assets/js/components/KeyMetrics/ConversionReportingNotificationCTAWidget.js b/assets/js/components/KeyMetrics/ConversionReportingNotificationCTAWidget.js index db4c9d3f431..424b848833b 100644 --- a/assets/js/components/KeyMetrics/ConversionReportingNotificationCTAWidget.js +++ b/assets/js/components/KeyMetrics/ConversionReportingNotificationCTAWidget.js @@ -17,15 +17,23 @@ */ /** - * WordPress dependencies + * External dependencies */ -import { __ } from '@wordpress/i18n'; -import { useCallback, useState, useEffect } from '@wordpress/element'; +import PropTypes from 'prop-types'; +import { useIntersection } from 'react-use'; /** - * External dependencies + * WordPress dependencies */ -import PropTypes from 'prop-types'; +import { __ } from '@wordpress/i18n'; +import { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from '@wordpress/element'; +import { usePrevious } from '@wordpress/compose'; /** * Internal dependencies @@ -35,15 +43,30 @@ import { CORE_USER } from '../../googlesitekit/datastore/user/constants'; import { CORE_UI } from '../../googlesitekit/datastore/ui/constants'; import { MODULES_ANALYTICS_4 } from '../../modules/analytics-4/datastore/constants'; import { KEY_METRICS_SELECTION_PANEL_OPENED_KEY } from './constants'; +import { conversionReportingDetectedEventsTracking } from './utils'; import ConversionReportingDashboardSubtleNotification from './ConversionReportingDashboardSubtleNotification'; import LostEventsSubtleNotification from './LostEventsSubtleNotification'; import whenActive from '../../util/when-active'; +import useViewContext from '../../hooks/useViewContext'; import useDisplayNewEventsCalloutForTailoredMetrics from './hooks/useDisplayNewEventsCalloutForTailoredMetrics'; import useDisplayNewEventsCalloutForUserPickedMetrics from './hooks/useDisplayNewEventsCalloutForUserPickedMetrics'; import useDisplayNewEventsCalloutAfterInitialDetection from './hooks/useDisplayNewEventsCalloutAfterInitialDetection'; +import { trackEvent } from '../../util'; function ConversionReportingNotificationCTAWidget( { Widget, WidgetNull } ) { + const viewContext = useViewContext(); + const [ isSaving, setIsSaving ] = useState( false ); + const [ isViewed, setIsViewed ] = useState( false ); + + const conversionReportingNotificationRef = useRef(); + const intersectionEntry = useIntersection( + conversionReportingNotificationRef, + { + threshold: 0.25, + } + ); + const inView = !! intersectionEntry?.intersectionRatio; const haveLostConversionEvents = useSelect( ( select ) => select( MODULES_ANALYTICS_4 ).haveLostEventsForCurrentMetrics() @@ -94,6 +117,32 @@ function ConversionReportingNotificationCTAWidget( { Widget, WidgetNull } ) { const shouldShowCalloutForLostEvents = haveLostConversionEvents && haveLostConversionEventsAfterDismiss; + const userPickedMetrics = useSelect( ( select ) => + select( CORE_USER ).getUserPickedMetrics() + ); + + // Build a common object to use as the first argument in conversionReportingDetectedEventsTracking(). + const conversionReportingDetectedEventsTrackingArgs = useMemo( () => { + return { + shouldShowInitialCalloutForTailoredMetrics, + shouldShowCalloutForUserPickedMetrics, + shouldShowCalloutForNewEvents, + userPickedMetrics, + }; + }, [ + shouldShowInitialCalloutForTailoredMetrics, + shouldShowCalloutForUserPickedMetrics, + shouldShowCalloutForNewEvents, + userPickedMetrics, + ] ); + + const isSavingConversionReportingSettings = useSelect( ( select ) => + select( CORE_USER ).isSavingConversionReportingSettings() + ); + const isSavingKeyMetricsSettings = useSelect( ( select ) => + select( CORE_USER ).isSavingKeyMetricsSettings() + ); + const { saveConversionReportingSettings } = useDispatch( CORE_USER ); const dismissCallout = useCallback( @@ -104,16 +153,35 @@ function ConversionReportingNotificationCTAWidget( { Widget, WidgetNull } ) { const conversionReportingSettings = { newEventsCalloutDismissedAt: timestamp, }; + if ( eventType === 'lostEvents' ) { conversionReportingSettings.lostEventsCalloutDismissedAt = timestamp; + + // Handle internal tracking for lost events banner dismissal. + trackEvent( + `${ viewContext }_kmw-lost-conversion-events-detected-notification`, + 'dismiss_notification', + 'conversion_reporting' + ); + } else { + // Handle internal tracking. + conversionReportingDetectedEventsTracking( + conversionReportingDetectedEventsTrackingArgs, + viewContext, + 'dismiss_notification' + ); } await saveConversionReportingSettings( conversionReportingSettings ); }, - [ saveConversionReportingSettings ] + [ + viewContext, + conversionReportingDetectedEventsTrackingArgs, + saveConversionReportingSettings, + ] ); const userInputPurposeConversionEvents = useSelect( ( select ) => @@ -134,6 +202,13 @@ function ConversionReportingNotificationCTAWidget( { Widget, WidgetNull } ) { setIsSaving( false ); } + // Handle internal tracking. + conversionReportingDetectedEventsTracking( + conversionReportingDetectedEventsTrackingArgs, + viewContext, + 'confirm_add_new_conversion_metrics' + ); + dismissCallout(); }, [ setUserInputSetting, @@ -141,55 +216,128 @@ function ConversionReportingNotificationCTAWidget( { Widget, WidgetNull } ) { dismissCallout, userInputPurposeConversionEvents, shouldShowInitialCalloutForTailoredMetrics, + viewContext, + conversionReportingDetectedEventsTrackingArgs, ] ); const { setValue } = useDispatch( CORE_UI ); - const handleSelectMetricsClick = useCallback( () => { - setValue( KEY_METRICS_SELECTION_PANEL_OPENED_KEY, true ); - }, [ setValue ] ); + const handleSelectMetricsClick = useCallback( + ( clickContext = '' ) => { + setValue( KEY_METRICS_SELECTION_PANEL_OPENED_KEY, true ); + + // Handle internal tracking of lost events variant. + if ( 'lostEvents' === clickContext ) { + if ( shouldShowCalloutForLostEvents ) { + trackEvent( + `${ viewContext }_kmw-lost-conversion-events-detected-notification`, + 'confirm_get_select_metrics', + 'conversion_reporting' + ); + } + + return; + } + + // Handle internal tracking if not lost events variant. + conversionReportingDetectedEventsTracking( + conversionReportingDetectedEventsTrackingArgs, + viewContext, + 'confirm_select_new_conversion_metrics' + ); + }, + [ + viewContext, + conversionReportingDetectedEventsTrackingArgs, + setValue, + shouldShowCalloutForLostEvents, + ] + ); const isSelectionPanelOpen = useSelect( ( select ) => select( CORE_UI ).getValue( KEY_METRICS_SELECTION_PANEL_OPENED_KEY ) ); - const isSavingConversionReportingSettings = useSelect( ( select ) => - select( CORE_USER ).isSavingConversionReportingSettings() - ); + const prevIsSelectionPanelOpen = usePrevious( isSelectionPanelOpen ); + // Handle dismiss of new events callout on opening of the selection panel. useEffect( () => { - if ( isSelectionPanelOpen ) { - if ( - // Dismiss the new events callout if shouldShowCalloutForNewEvents is true - // and settings are not being saved. This prevents duplicate requests, as after - // the first call, the settings enter the saving state. Once saving is complete, - // shouldShowCalloutForNewEvents will no longer be true. - ( ! isSavingConversionReportingSettings && - ( shouldShowCalloutForNewEvents || - shouldShowCalloutForUserPickedMetrics ) ) || + if ( + ! prevIsSelectionPanelOpen && + isSelectionPanelOpen && + // Dismiss the new events callout if shouldShowCalloutForNewEvents is true + // and settings are not being saved. This prevents duplicate requests, as after + // the first call, the settings enter the saving state. Once saving is complete, + // shouldShowCalloutForNewEvents will no longer be true. + ( ( ! isSavingConversionReportingSettings && + ( shouldShowCalloutForNewEvents || + shouldShowCalloutForUserPickedMetrics ) ) || // shouldShowInitialCalloutForTailoredMetrics is more specific because the "Add metrics" // CTA does not open the panel; it directly adds metrics. We want to dismiss this callout // only when the user opens the selection panel and saves their metrics selection. This marks // the transition to manual selection, after which this callout should no longer be shown. ( shouldShowInitialCalloutForTailoredMetrics && - isSavingConversionReportingSettings ) - ) { - dismissCallout(); - } - - if ( shouldShowCalloutForLostEvents ) { - dismissCallout( 'lostEvents' ); - } + isSavingKeyMetricsSettings ) ) + ) { + dismissCallout(); } }, [ isSelectionPanelOpen, + prevIsSelectionPanelOpen, shouldShowCalloutForNewEvents, shouldShowCalloutForUserPickedMetrics, shouldShowInitialCalloutForTailoredMetrics, isSavingConversionReportingSettings, + isSavingKeyMetricsSettings, + dismissCallout, + ] ); + + // Handle dismiss of lost events callout on opening of the selection panel. + useEffect( () => { + if ( + ! prevIsSelectionPanelOpen && + isSelectionPanelOpen && + shouldShowCalloutForLostEvents && + ! isSavingConversionReportingSettings + ) { + dismissCallout( 'lostEvents' ); + } + }, [ + isSelectionPanelOpen, + prevIsSelectionPanelOpen, + isSavingConversionReportingSettings, shouldShowCalloutForLostEvents, dismissCallout, ] ); + // Track when the notification is viewed. + useEffect( () => { + if ( ! isViewed && inView ) { + // Handle internal tracking. + conversionReportingDetectedEventsTracking( + conversionReportingDetectedEventsTrackingArgs, + viewContext, + 'view_notification' + ); + + // Handle internal tracking for lost events banner. + if ( shouldShowCalloutForLostEvents ) { + trackEvent( + `${ viewContext }_kmw-lost-conversion-events-detected-notification`, + 'view_notification', + 'conversion_reporting' + ); + } + + setIsViewed( true ); + } + }, [ + isViewed, + inView, + viewContext, + conversionReportingDetectedEventsTrackingArgs, + shouldShowCalloutForLostEvents, + ] ); + if ( ! shouldShowInitialCalloutForTailoredMetrics && ! shouldShowCalloutForLostEvents && @@ -209,10 +357,12 @@ function ConversionReportingNotificationCTAWidget( { Widget, WidgetNull } ) { } return ( - + { shouldShowCalloutForLostEvents && ( { + handleSelectMetricsClick( 'lostEvents' ); + } } onDismissCallback={ () => dismissCallout( 'lostEvents' ) } /> ) } diff --git a/assets/js/components/KeyMetrics/ConversionReportingSettingsSubtleNotification.js b/assets/js/components/KeyMetrics/ConversionReportingSettingsSubtleNotification.js index 0fbf3df0cb5..7f6f8614cfa 100644 --- a/assets/js/components/KeyMetrics/ConversionReportingSettingsSubtleNotification.js +++ b/assets/js/components/KeyMetrics/ConversionReportingSettingsSubtleNotification.js @@ -16,11 +16,16 @@ * limitations under the License. */ +/** + * External dependencies + */ +import { useIntersection } from 'react-use'; + /** * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { useState, useCallback } from '@wordpress/element'; +import { useState, useCallback, useRef, useEffect } from '@wordpress/element'; /** * Internal dependencies @@ -30,9 +35,33 @@ import { SpinnerButton } from 'googlesitekit-components'; import StarFill from '../../../svg/icons/star-fill.svg'; import SubtleNotification from '../../googlesitekit/notifications/components/layout/SubtleNotification'; import { CORE_SITE } from '../../googlesitekit/datastore/site/constants'; +import { trackEvent } from '../../util'; +import useViewContext from '../../hooks/useViewContext'; export default function ConversionReportingSettingsSubtleNotification() { + const viewContext = useViewContext(); const [ isNavigating, setIsNavigating ] = useState( false ); + const [ isViewed, setIsViewed ] = useState( false ); + + const notificationRef = useRef(); + const intersectionEntry = useIntersection( notificationRef, { + threshold: 0.25, + } ); + const inView = !! intersectionEntry?.intersectionRatio; + + // Track when the notification is viewed. + useEffect( () => { + if ( ! isViewed && inView ) { + // Handle internal tracking. + trackEvent( + `${ viewContext }_kmw-settings-change-from-manual-to-tailored`, + 'view_notification', + 'conversion_reporting' + ); + + setIsViewed( true ); + } + }, [ isViewed, inView, viewContext ] ); const userInputURL = useSelect( ( select ) => select( CORE_SITE ).getAdminURL( 'googlesitekit-user-input' ) @@ -40,10 +69,18 @@ export default function ConversionReportingSettingsSubtleNotification() { const handleCTAClick = useCallback( () => { setIsNavigating( true ); - }, [ setIsNavigating ] ); + + // Handle internal tracking. + trackEvent( + `${ viewContext }_kmw-settings-change-from-manual-to-tailored`, + 'confirm_get_tailored_metrics', + 'conversion_reporting' + ); + }, [ setIsNavigating, viewContext ] ); return ( + select( MODULES_ANALYTICS_4 ).getKeyMetricsConversionEventWidgets() + ); const saveSettings = useCallback( async ( widgetSlugs ) => { @@ -141,49 +145,68 @@ export default function Footer( { [ saveKeyMetricsSettings ] ); - const onSaveSuccess = useCallback( () => { - trackEvent( trackingCategory, 'metrics_sidebar_save' ); - - if ( isGA4Connected && hasMissingCustomDimensions ) { - setValues( FORM_CUSTOM_DIMENSIONS_CREATE, { - autoSubmit: true, - } ); + const onSaveSuccess = useCallback( + ( selectedItemSlugs ) => { + const userSavedConversionReportingKeyMetricsList = Object.values( + conversionReportingSpecificKeyMetricsWidgets + ) + .flat() + .some( ( slug ) => selectedItemSlugs.includes( slug ) ); + + // Include the conversion_reporting tracking label if necessary. + if ( userSavedConversionReportingKeyMetricsList ) { + trackEvent( + trackingCategory, + 'metrics_sidebar_save', + 'conversion_reporting' + ); + } else { + trackEvent( trackingCategory, 'metrics_sidebar_save' ); + } - if ( ! hasAnalytics4EditScope ) { - // Let parent component know that the user is navigating to OAuth URL - // so that the panel is kept open. - onNavigationToOAuthURL(); - - // Ensure the panel is closed, just in case the user navigates to - // the OAuth URL before the function is fully executed. - closePanel(); - - setPermissionScopeError( { - code: ERROR_CODE_MISSING_REQUIRED_SCOPE, - message: __( - 'Additional permissions are required to create new Analytics custom dimensions', - 'google-site-kit' - ), - data: { - status: 403, - scopes: [ EDIT_SCOPE ], - skipModal: true, - redirectURL, - }, + if ( isGA4Connected && hasMissingCustomDimensions ) { + setValues( FORM_CUSTOM_DIMENSIONS_CREATE, { + autoSubmit: true, } ); + + if ( ! hasAnalytics4EditScope ) { + // Let parent component know that the user is navigating to OAuth URL + // so that the panel is kept open. + onNavigationToOAuthURL(); + + // Ensure the panel is closed, just in case the user navigates to + // the OAuth URL before the function is fully executed. + closePanel(); + + setPermissionScopeError( { + code: ERROR_CODE_MISSING_REQUIRED_SCOPE, + message: __( + 'Additional permissions are required to create new Analytics custom dimensions', + 'google-site-kit' + ), + data: { + status: 403, + scopes: [ EDIT_SCOPE ], + skipModal: true, + redirectURL, + }, + } ); + } } - } - }, [ - trackingCategory, - isGA4Connected, - hasMissingCustomDimensions, - setValues, - hasAnalytics4EditScope, - onNavigationToOAuthURL, - closePanel, - setPermissionScopeError, - redirectURL, - ] ); + }, + [ + trackingCategory, + isGA4Connected, + hasMissingCustomDimensions, + setValues, + hasAnalytics4EditScope, + onNavigationToOAuthURL, + closePanel, + setPermissionScopeError, + redirectURL, + conversionReportingSpecificKeyMetricsWidgets, + ] + ); const onCancel = useCallback( () => { trackEvent( trackingCategory, 'metrics_sidebar_cancel' ); @@ -201,7 +224,9 @@ export default function Footer( { minSelectedItemCount={ MIN_SELECTED_METRICS_COUNT } maxSelectedItemCount={ maxSelectedMetricsLimit } isBusy={ isSavingSettings || isNavigatingToOAuthURL } - onSaveSuccess={ onSaveSuccess } + onSaveSuccess={ () => { + onSaveSuccess( selectedMetrics ); + } } onCancel={ onCancel } isOpen={ isOpen } closePanel={ closePanel } diff --git a/assets/js/components/KeyMetrics/utils.js b/assets/js/components/KeyMetrics/utils.js new file mode 100644 index 00000000000..839a26e1e50 --- /dev/null +++ b/assets/js/components/KeyMetrics/utils.js @@ -0,0 +1,73 @@ +/** + * Key Metrics utility functions. + * + * Site Kit by Google, Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * External dependencies. + */ +import propTypes from 'prop-types'; + +/** + * Internal dependencies + */ +import { trackEvent } from '../../util'; + +export function conversionReportingDetectedEventsTracking( + { + shouldShowInitialCalloutForTailoredMetrics, + shouldShowCalloutForUserPickedMetrics, + shouldShowCalloutForNewEvents, + userPickedMetrics, + }, + viewContext, + eventName +) { + let category = ''; + + // Handle internal tracking or the initial detection of events with tailored KMW selection. + if ( shouldShowInitialCalloutForTailoredMetrics ) { + category = `${ viewContext }_kmw-tailored-conversion-events-detected-notification`; + } + + // Handle internal tracking or the initial detection of events with manual KMW selection. + if ( shouldShowCalloutForUserPickedMetrics ) { + category = `${ viewContext }_kmw-manual-conversion-events-detected-notification`; + } + + // Handle internal tracking for new events with manual KMW selection. + if ( shouldShowCalloutForNewEvents && userPickedMetrics?.length ) { + category = `${ viewContext }_kmw-manual-new-conversion-events-detected-notification`; + } + + // Handle internal tracking for new events with tailored KMW selection. + if ( shouldShowCalloutForNewEvents && ! userPickedMetrics?.length ) { + category = `${ viewContext }_kmw-tailored-new-conversion-events-detected-notification`; + } + + if ( category ) { + trackEvent( category, eventName, 'conversion_reporting' ); + } +} + +conversionReportingDetectedEventsTracking.propTypes = { + shouldShowInitialCalloutForTailoredMetrics: propTypes.bool.isRequired, + shouldShowCalloutForUserPickedMetrics: propTypes.bool.isRequired, + shouldShowCalloutForNewEvents: propTypes.bool.isRequired, + userPickedMetrics: propTypes.object.isRequired, + viewContext: propTypes.string.isRequired, + eventName: propTypes.string.isRequired, +}; diff --git a/assets/js/components/user-input/KeyMetricsSettingsSellProductsSubtleNotification.js b/assets/js/components/user-input/KeyMetricsSettingsSellProductsSubtleNotification.js index ce3280235ee..aed45313c22 100644 --- a/assets/js/components/user-input/KeyMetricsSettingsSellProductsSubtleNotification.js +++ b/assets/js/components/user-input/KeyMetricsSettingsSellProductsSubtleNotification.js @@ -16,24 +16,53 @@ * limitations under the License. */ +/** + * External dependencies + */ +import { useIntersection } from 'react-use'; + /** * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { useCallback } from '@wordpress/element'; +import { useCallback, useEffect, useRef, useState } from '@wordpress/element'; /** * Internal dependencies */ +import useViewContext from '../../hooks/useViewContext'; import { useSelect, useDispatch } from 'googlesitekit-data'; import { Button } from 'googlesitekit-components'; import SubtleNotification from '../../googlesitekit/notifications/components/layout/SubtleNotification'; import { CORE_USER } from '../../googlesitekit/datastore/user/constants'; import { USER_INPUT_LEGACY_SITE_PURPOSE_DISMISSED_ITEM_KEY } from './util/constants'; +import { trackEvent } from '../../util'; import WarningSVG from '../../../svg/icons/warning.svg'; export default function KeyMetricsSettingsSellProductsSubtleNotification() { + const viewContext = useViewContext(); const { dismissItem } = useDispatch( CORE_USER ); + const [ isViewed, setIsViewed ] = useState( false ); + + const notificationRef = useRef(); + const intersectionEntry = useIntersection( notificationRef, { + threshold: 0.25, + } ); + const inView = !! intersectionEntry?.intersectionRatio; + + // Track when the notification is viewed. + useEffect( () => { + if ( ! isViewed && inView ) { + // Handle internal tracking. + trackEvent( + `${ viewContext }_kmw-settings-suggested-site-purpose-edit-notification`, + 'view_notification', + 'conversion_reporting' + ); + + setIsViewed( true ); + } + }, [ isViewed, inView, viewContext ] ); const isDismissed = useSelect( ( select ) => select( CORE_USER ).isItemDismissed( @@ -43,7 +72,13 @@ export default function KeyMetricsSettingsSellProductsSubtleNotification() { const onDismiss = useCallback( async () => { await dismissItem( USER_INPUT_LEGACY_SITE_PURPOSE_DISMISSED_ITEM_KEY ); - }, [ dismissItem ] ); + // Handle internal tracking. + trackEvent( + `${ viewContext }_kmw-settings-suggested-site-purpose-edit-notification`, + 'confirm_notification', + 'conversion_reporting' + ); + }, [ dismissItem, viewContext ] ); if ( isDismissed ) { return null; @@ -51,6 +86,7 @@ export default function KeyMetricsSettingsSellProductsSubtleNotification() { return ( - - -
- { icon } - { type === 'success' && ! icon && ( - +const SubtleNotification = forwardRef( + ( + { + className, + title, + description, + dismissCTA, + additionalCTA, + type = 'success', + icon, + }, + ref + ) => { + return ( + + + - ) } -
+ > +
+ { icon } + { type === 'success' && ! icon && ( + + ) } + { type === 'warning' && ! icon && ( + + ) } +
-
-

{ title }

-

- { description } -

-
-
- { dismissCTA } +
+

{ title }

+

+ { description } +

+
+
+ { dismissCTA } - { additionalCTA } -
- - - - ); -} + { additionalCTA } +
+
+
+ + ); + } +); SubtleNotification.propTypes = { className: PropTypes.string, @@ -91,3 +101,5 @@ SubtleNotification.propTypes = { type: PropTypes.string, icon: PropTypes.object, }; + +export default SubtleNotification;