Skip to content

Commit

Permalink
Merge pull request #8737 from google/enhancement/8169-widget-area-new…
Browse files Browse the repository at this point in the history
…-badges

Add reusable Widget area, New badge feature.
  • Loading branch information
eugene-manuilov committed May 28, 2024
2 parents 9ee2d7a + e99a2ae commit b820762
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 5 deletions.
54 changes: 52 additions & 2 deletions assets/js/googlesitekit/widgets/components/WidgetAreaRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ import WidgetCellWrapper from './WidgetCellWrapper';
import useViewOnly from '../../../hooks/useViewOnly';
import { CORE_USER } from '../../datastore/user/constants';
import useLatestIntersection from '../../../hooks/useLatestIntersection';
import NewBadge from '../../../components/NewBadge';
import { WEEK_IN_SECONDS } from '../../../util';
import { useDispatch } from '@wordpress/data';
const { useSelect } = Data;

/**
Expand Down Expand Up @@ -95,6 +98,10 @@ export default function WidgetAreaRenderer( { slug, contextID } ) {
const widgetArea = useSelect( ( select ) =>
select( CORE_WIDGETS ).getWidgetArea( slug )
);

const { Icon, title, style, subtitle, hasNewBadge, CTA, Footer } =
widgetArea;

const widgets = useSelect( ( select ) =>
select( CORE_WIDGETS ).getWidgets( slug, {
modules: viewableModules ? viewableModules : undefined,
Expand Down Expand Up @@ -129,6 +136,47 @@ export default function WidgetAreaRenderer( { slug, contextID } ) {
} );
}, [ intersectionEntry, slug, activeContextID, contextID ] );

// NewBadge Expirable Item
const expirableBadgeSlug = `widget-area-expirable-new-badge-${ slug }`;

const hasBadgeBeenSeen = useSelect( ( select ) =>
select( CORE_USER ).hasExpirableItem( expirableBadgeSlug )
);
const isExpiredBadgeActive = useSelect( ( select ) =>
select( CORE_USER ).isExpirableItemActive( expirableBadgeSlug )
);

// Show the new badge if this widget area allows new badges, it's new badge
// has not been seen yet, or the badge has been seen and is still active.
const showNewBadge =
hasNewBadge && ( hasBadgeBeenSeen === false || isExpiredBadgeActive );

const { setExpirableItemTimers } = useDispatch( CORE_USER );

useEffect( () => {
// Wait until the selectors have resolved.
if (
hasBadgeBeenSeen !== undefined &&
isExpiredBadgeActive !== undefined
) {
// Only set the expirable item if the badge is new and the user is viewing it for the first time.
if ( hasNewBadge && ! hasBadgeBeenSeen ) {
setExpirableItemTimers( [
{
slug: expirableBadgeSlug,
expiresInSeconds: WEEK_IN_SECONDS * 4,
},
] );
}
}
}, [
hasNewBadge,
expirableBadgeSlug,
hasBadgeBeenSeen,
isExpiredBadgeActive,
setExpirableItemTimers,
] );

if ( viewableModules === undefined ) {
return null;
}
Expand Down Expand Up @@ -175,8 +223,6 @@ export default function WidgetAreaRenderer( { slug, contextID } ) {
</WidgetCellWrapper>
) );

const { Icon, title, style, subtitle, CTA, Footer } = widgetArea;

return (
<InViewProvider value={ inViewState }>
{ !! isActive && (
Expand All @@ -198,6 +244,7 @@ export default function WidgetAreaRenderer( { slug, contextID } ) {
{ title && (
<h3 className="googlesitekit-widget-area-header__title googlesitekit-heading-3">
{ title }
{ showNewBadge && <NewBadge /> }
</h3>
) }

Expand All @@ -206,6 +253,9 @@ export default function WidgetAreaRenderer( { slug, contextID } ) {
{ subtitle && (
<h4 className="googlesitekit-widget-area-header__subtitle">
{ subtitle }
{ showNewBadge && ! title && (
<NewBadge />
) }
</h4>
) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
provideModules,
provideUserCapabilities,
unsubscribeFromAll,
muteFetch,
} from '../../../../../tests/js/test-utils';
import {
VIEW_CONTEXT_MAIN_DASHBOARD,
Expand Down Expand Up @@ -100,6 +101,11 @@ describe( 'WidgetAreaRenderer', () => {

const connection = { connected: true };
await registry.dispatch( CORE_SITE ).receiveGetConnection( connection );

const fetchGetExpiredItems = new RegExp(
'^/google-site-kit/v1/core/user/data/expirable-items'
);
muteFetch( fetchGetExpiredItems );
} );

afterEach( () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import WidgetContextRenderer from './WidgetContextRenderer';
import { CORE_WIDGETS } from '../datastore/constants';
import {
createTestRegistry,
muteFetch,
provideModules,
render,
waitFor,
Expand Down Expand Up @@ -54,6 +55,11 @@ describe( 'WidgetContextRenderer', () => {
registry
.dispatch( CORE_WIDGETS )
.assignWidgetArea( 'TestArea1', 'TestContext' );

const fetchGetExpiredItems = new RegExp(
'^/google-site-kit/v1/core/user/data/expirable-items'
);
muteFetch( fetchGetExpiredItems );
} );

it( 'should render the registered widget areas', async () => {
Expand Down
4 changes: 4 additions & 0 deletions assets/js/googlesitekit/widgets/datastore/areas.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export const actions = {
* @since 1.107.0 Extended to support an optional CTA component.
* @since 1.110.0 Extended to support an optional filterActiveWidgets function.
* @since n.e.x.t Extended to make title optional and support an optional Footer component.
* @since n.e.x.t Extended to support an optional hasNewBadge parameter.
*
* @param {string} slug Widget Area's slug.
* @param {Object} settings Widget Area's settings.
Expand All @@ -91,6 +92,7 @@ export const actions = {
* @param {WPComponent} [settings.Icon] Optional. React component to render icon for this widget area.
* @param {string} [settings.style] Optional. Widget area style (one of "boxes", "composite"). Default: "boxes".
* @param {number} [settings.priority] Optional. Priority for this widget area. Default: 10.
* @param {boolean} [settings.hasNewBadge] Optional. Whether this widget area should display a new badge.
* @param {WPComponent} [settings.CTA] Optional. React component used as CTA appearing beside the subtitle.
* @param {WPComponent} [settings.Footer] Optional. React component used as footer for the widget area.
* @param {Function} [settings.filterActiveWidgets] Optional. Function used to filter active widgets.
Expand All @@ -104,6 +106,7 @@ export const actions = {
title,
subtitle,
Icon,
hasNewBadge = false,
CTA,
Footer,
filterActiveWidgets,
Expand All @@ -124,6 +127,7 @@ export const actions = {
title,
subtitle,
Icon,
hasNewBadge,
CTA,
Footer,
filterActiveWidgets,
Expand Down
10 changes: 7 additions & 3 deletions assets/js/googlesitekit/widgets/datastore/areas.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -492,12 +492,16 @@ describe( 'core/widgets Widget areas', () => {
expect(
registry.select( CORE_WIDGETS ).getWidgetArea( 'TestArea' )
).toEqual( {
Icon: undefined,
priority: 10,
slug: 'TestArea',
title: 'Test Header',
subtitle: 'Cool stuff for yoursite.com',
Icon: undefined,
style: 'composite',
slug: 'TestArea',
priority: 10,
hasNewBadge: false,
CTA: undefined,
Footer: undefined,
filterActiveWidgets: undefined,
} );
} );

Expand Down
1 change: 1 addition & 0 deletions assets/js/googlesitekit/widgets/register-defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export function registerDefaults( widgetsAPI ) {
'Understand how different visitor groups interact with your site',
'google-site-kit'
),
hasNewBadge: true,
style: WIDGET_AREA_STYLES.BOXES,
priority: 2,
Footer: AudienceAreaFooter,
Expand Down

0 comments on commit b820762

Please sign in to comment.