Use themes REST API - CYS intro page (#40552)

* Use themes REST API

* Add changefile(s) from automation for the following project(s): woocommerce

* Use browse_all.href and set default value to /:admin-dir/themes.php

* Fix lint

* Fix tests

* Fix tests

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com>
This commit is contained in:
Moon 2023-10-05 06:33:50 -07:00 committed by GitHub
parent fbb294f543
commit a99a52947e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 104 additions and 97 deletions

View File

@ -11,7 +11,7 @@ import {
} from '@woocommerce/navigation';
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
import { dispatch } from '@wordpress/data';
import { addQueryArgs } from '@wordpress/url';
import { getAdminLink } from '@woocommerce/settings';
/**
* Internal dependencies
@ -64,8 +64,10 @@ const redirectToWooHome = () => {
window.location.href = getNewPath( {}, '/', {} );
};
const redirectToThemes = () => {
window.location.href = addQueryArgs( 'themes.php' );
const redirectToThemes = ( _context: customizeStoreStateMachineContext ) => {
window.location.href =
_context?.intro?.themeData?._links?.browse_all?.href ??
getAdminLink( 'themes.php' );
};
const markTaskComplete = async () => {
@ -116,7 +118,14 @@ export const customizeStoreStateMachineDefinition = createMachine( {
context: {
intro: {
hasErrors: false,
themeCards: [] as ThemeCard[],
themeData: {
themes: [] as ThemeCard[],
_links: {
browse_all: {
href: getAdminLink( 'themes.php' ),
},
},
},
activeTheme: '',
activeThemeHasMods: false,
customizeStoreTaskCompleted: false,
@ -184,7 +193,7 @@ export const customizeStoreStateMachineDefinition = createMachine( {
onDone: {
target: 'intro',
actions: [
'assignThemeCards',
'assignThemeData',
'assignActiveThemeHasMods',
'assignCustomizeStoreCompleted',
],

View File

@ -8,20 +8,28 @@ import { recordEvent } from '@woocommerce/tracks';
* Internal dependencies
*/
import { customizeStoreStateMachineEvents } from '..';
import { customizeStoreStateMachineContext } from '../types';
import { ThemeCard } from './types';
import {
customizeStoreStateMachineContext,
RecommendThemesAPIResponse,
} from '../types';
import { events } from './';
export const assignThemeCards = assign<
export const assignThemeData = assign<
customizeStoreStateMachineContext,
customizeStoreStateMachineEvents // this is actually the wrong type for the event but I still don't know how to type this properly
>( {
intro: ( context, event ) => {
const themeCards = (
event as DoneInvokeEvent< { themeCards: ThemeCard[] } >
).data.themeCards;
const apiResponse = (
event as DoneInvokeEvent< {
themeData: RecommendThemesAPIResponse;
} >
).data.themeData;
// type coercion workaround for now
return { ...context.intro, themeCards };
return {
...context.intro,
themeData: apiResponse,
};
},
} );

View File

@ -58,7 +58,7 @@ type ModalStatus = keyof typeof MODAL_COMPONENTS;
export const Intro: CustomizeStoreComponent = ( { sendEvent, context } ) => {
const {
intro: { themeCards, activeThemeHasMods, customizeStoreTaskCompleted },
intro: { themeData, activeThemeHasMods, customizeStoreTaskCompleted },
} = context;
const isJetpackOffline = false;
@ -157,16 +157,16 @@ export const Intro: CustomizeStoreComponent = ( { sendEvent, context } ) => {
</p>
<div className="woocommerce-customize-store-theme-cards">
{ themeCards?.map( ( themeCard ) => (
{ themeData.themes?.map( ( theme ) => (
<ThemeCard
key={ themeCard.slug }
slug={ themeCard.slug }
description={ themeCard.description }
image={ themeCard.image }
name={ themeCard.name }
colorPalettes={ themeCard.colorPalettes }
link={ themeCard?.link }
isActive={ themeCard.isActive }
key={ theme.slug }
slug={ theme.slug }
description={ theme.description }
thumbnail_url={ theme.thumbnail_url }
name={ theme.name }
color_palettes={ theme.color_palettes }
link_url={ theme?.link_url }
is_active={ theme.is_active }
/>
) ) }
</div>

View File

@ -7,63 +7,16 @@ import { resolveSelect } from '@wordpress/data';
import { ONBOARDING_STORE_NAME } from '@woocommerce/data';
// @ts-ignore No types for this exist yet.
import { store as coreStore } from '@wordpress/core-data';
import apiFetch from '@wordpress/api-fetch';
// placeholder xstate async service that returns a set of theme cards
export const fetchThemeCards = async () => {
return [
{
slug: 'twentytwentyone',
name: 'Twenty Twenty One',
description: 'The default theme for WordPress.',
isActive: true,
image: 'https://i0.wp.com/s2.wp.com/wp-content/themes/pub/twentytwentyone/screenshot.png',
colorPalettes: [],
},
{
slug: 'twentytwenty',
name: 'Twenty Twenty',
description: 'The previous default theme for WordPress.',
image: 'https://i0.wp.com/s2.wp.com/wp-content/themes/pub/twentytwenty/screenshot.png',
colorPalettes: [],
},
{
slug: 'tsubaki',
name: 'Tsubaki',
description:
'Tsubaki puts the spotlight on your products and your customers. This theme leverages WooCommerce to provide you with intuitive product navigation and the patterns you need to master digital merchandising.',
image: 'https://i0.wp.com/s2.wp.com/wp-content/themes/premium/tsubaki/screenshot.png',
colorPalettes: [],
},
{
slug: 'winkel',
name: 'Winkel',
description:
'Winkel is a minimal, product-focused theme featuring Payments block. Its clean, cool look combined with a simple layout makes it perfect for showcasing fashion items clothes, shoes, and accessories.',
image: 'https://i0.wp.com/s2.wp.com/wp-content/themes/pub/winkel/screenshot.png',
colorPalettes: [
{
title: 'Default',
primary: '#ffffff',
secondary: '#676767',
},
{
title: 'Charcoal',
primary: '#1f2527',
secondary: '#9fd3e8',
},
{
title: 'Rainforest',
primary: '#eef4f7',
secondary: '#35845d',
},
{
title: 'Ruby Wine',
primary: '#ffffff',
secondary: '#c8133e',
},
],
},
];
const themes = await apiFetch( {
path: '/wc-admin/onboarding/themes/recommended',
method: 'GET',
} );
return themes;
};
export const fetchIntroData = async () => {
@ -98,11 +51,11 @@ export const fetchIntroData = async () => {
hasModifiedPages;
const customizeStoreTaskCompleted = ( await getTask( 'customize-store' ) )
?.isComplete;
const themeCards = await fetchThemeCards();
const themeData = await fetchThemeCards();
return {
activeThemeHasMods,
customizeStoreTaskCompleted,
themeCards,
themeData,
};
};

View File

@ -28,13 +28,21 @@ describe( 'Intro Banners', () => {
intro: {
hasErrors: false,
activeTheme: '',
themeCards: [],
themeData: {
themes: [],
_links: {
browse_all: {
href: '',
},
},
},
activeThemeHasMods: false,
customizeStoreTaskCompleted: false,
currentThemeIsAiGenerated: false,
},
themeConfiguration: {},
} }
currentState={ 'intro' }
parentMachine={ null as unknown as AnyInterpreter }
/>
);
@ -56,13 +64,21 @@ describe( 'Intro Banners', () => {
intro: {
hasErrors: false,
activeTheme: '',
themeCards: [],
themeData: {
themes: [],
_links: {
browse_all: {
href: '',
},
},
},
activeThemeHasMods: false,
customizeStoreTaskCompleted: false,
currentThemeIsAiGenerated: false,
},
themeConfiguration: {},
} }
currentState={ 'intro' }
parentMachine={ null as unknown as AnyInterpreter }
/>
);

View File

@ -26,13 +26,21 @@ describe( 'Intro Modals', () => {
intro: {
hasErrors: false,
activeTheme: '',
themeCards: [],
themeData: {
themes: [],
_links: {
browse_all: {
href: '',
},
},
},
activeThemeHasMods: true,
customizeStoreTaskCompleted: false,
currentThemeIsAiGenerated: false,
},
themeConfiguration: {},
} }
currentState={ 'intro' }
parentMachine={ null as unknown as AnyInterpreter }
/>
);

View File

@ -13,31 +13,31 @@ import { ColorPalettes } from './color-palettes';
export const ThemeCard = ( {
slug,
description,
image,
thumbnail_url,
name,
colorPalettes = [],
link = '',
isActive = false,
color_palettes = [],
link_url = '',
is_active = false,
}: TypeThemeCard ) => {
return (
<div className="theme-card" key={ slug }>
<div>
{ link ? (
<Link href={ link }>
<img src={ image } alt={ description } />
{ link_url ? (
<Link href={ link_url }>
<img src={ thumbnail_url } alt={ description } />
</Link>
) : (
<img src={ image } alt={ description } />
<img src={ thumbnail_url } alt={ description } />
) }
</div>
<div className="theme-card__info">
<h2 className="theme-card__title">{ name }</h2>
{ colorPalettes && (
<ColorPalettes colorPalettes={ colorPalettes } />
{ color_palettes && (
<ColorPalettes colorPalettes={ color_palettes } />
) }
</div>
<div>
{ isActive && (
{ is_active && (
<span className="theme-card__active">
{ __( 'Active theme', 'woocommerce' ) }
</span>

View File

@ -10,8 +10,8 @@ export type ThemeCard = {
slug: string;
name: string;
description: string;
image: string;
isActive: boolean;
link?: string;
colorPalettes: ColorPalette[];
thumbnail_url: string;
is_active: boolean;
link_url?: string;
color_palettes: ColorPalette[];
};

View File

@ -20,11 +20,20 @@ export type CustomizeStoreComponentMeta = {
component: CustomizeStoreComponent;
};
export type RecommendThemesAPIResponse = {
themes: ThemeCard[];
_links: {
browse_all?: {
href: string;
};
};
};
export type customizeStoreStateMachineContext = {
themeConfiguration: Record< string, unknown >; // placeholder for theme configuration until we know what it looks like
intro: {
hasErrors: boolean;
themeCards: ThemeCard[];
themeData: RecommendThemesAPIResponse;
activeTheme: string;
activeThemeHasMods: boolean;
customizeStoreTaskCompleted: boolean;

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Use the newly added themes REST API on the CYS intro page