Skip to content

Commit 48940ed

Browse files
release: v1.3.1
- Improve Starter Sites Onboarding Layout and Discovery - Add a Four-Column Starter Sites Grid on Larger Screens - Refine Onboarding Banners and Upgrade Messaging - Improve Starter Site Color Variant Preview and Filtering - Use Site Title to Improve Starter Site Ranking and Search
2 parents fcdccdb + f6ef1eb commit 48940ed

15 files changed

Lines changed: 257 additions & 356 deletions

e2e-tests/specs/onboarding.spec.js

Lines changed: 5 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,12 @@ test.describe('Onboarding', () => {
4848
expect(await page.locator('.ss-card .ss-badge').count()).toBeGreaterThan(0);
4949

5050
// 'All' and 'Free' should show after you select a category.
51+
// Match exactly: card page-shot buttons (e.g. "Gallery", "Ballet Blog",
52+
// "All Courses") also contain "all" as a substring, so a non-exact name
53+
// match resolves to multiple elements once the grid renders a full page.
5154
await page.locator('.ob-cat-wrap').first().click();
52-
await expect(page.getByRole('button', { name: 'All' })).toBeVisible();
53-
await expect(page.getByRole('button', { name: 'Free' })).toBeVisible();
55+
await expect(page.getByRole('button', { name: 'All', exact: true })).toBeVisible();
56+
await expect(page.getByRole('button', { name: 'Free', exact: true })).toBeVisible();
5457

5558
// Check card structure.
5659
const firstListedSiteCard = page.locator('.ss-card-wrap').first();
@@ -62,41 +65,6 @@ test.describe('Onboarding', () => {
6265
await expect(firstListedSiteCard.locator('.ss-title')).not.toBeEmpty();
6366
});
6467

65-
test('Onboarding promo notice can be dismissed and stays hidden after reload', async ({ page, admin }) => {
66-
await admin.visitAdminPage(ONBOARDING_URL);
67-
68-
const promoNotice = page.locator('.ob-onboarding-promo');
69-
await expect(promoNotice).toBeVisible();
70-
71-
// The client sends the dismiss action via FormData, which fetch encodes as
72-
// multipart/form-data. Inspect the raw body bytes so the predicate works
73-
// regardless of whether the encoding is multipart or url-encoded.
74-
const isDismissCall = (request) =>
75-
request.url().includes('admin-ajax.php') &&
76-
request.method() === 'POST' &&
77-
(request.postDataBuffer()?.toString('utf8') ?? '').includes(
78-
'dismiss_onboarding_promo_notice'
79-
);
80-
81-
const dismissRequest = page.waitForRequest(isDismissCall);
82-
const dismissResponse = page.waitForResponse((response) =>
83-
isDismissCall(response.request())
84-
);
85-
86-
await promoNotice.getByRole('button', { name: 'Dismiss notice' }).click();
87-
88-
const request = await dismissRequest;
89-
const response = await dismissResponse;
90-
expect(request.postDataBuffer()?.toString('utf8')).toContain(
91-
'dismiss_onboarding_promo_notice'
92-
);
93-
expect(response.ok()).toBeTruthy();
94-
95-
await expect(promoNotice).toBeHidden();
96-
await page.reload();
97-
await expect(promoNotice).toBeHidden();
98-
});
99-
10068
test('Site Import Customization Rendering', async ({ page, admin }) => {
10169
await admin.visitAdminPage(ONBOARDING_URL);
10270
await openFirstSiteAndWaitForData( page );

includes/Admin.php

Lines changed: 3 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,9 @@ class Admin {
2424
const IMPORTED_TEMPLATES_COUNT_OPT = 'tiob_premade_imported';
2525
const FEEDBACK_DISMISSED_OPT = 'tiob_feedback_dismiss';
2626

27-
const TC_REMOVED_KEY = 'tiob_tc_removed';
28-
const TC_NEW_NOTICE_DISMISSED = 'tiob_new_tc_notice_dismissed';
29-
const ONBOARDING_PROMO_NOTICE_DISMISSED = 'tiob_onboarding_promo_notice_dismissed';
30-
const VISITED_LIBRARY_OPT = 'tiob_library_visited';
27+
const TC_REMOVED_KEY = 'tiob_tc_removed';
28+
const TC_NEW_NOTICE_DISMISSED = 'tiob_new_tc_notice_dismissed';
29+
const VISITED_LIBRARY_OPT = 'tiob_library_visited';
3130

3231
/**
3332
* Admin page slug
@@ -89,7 +88,6 @@ public function init() {
8988
add_action( 'wp_ajax_tpc_get_logs', array( $this, 'external_get_logs' ) );
9089

9190
add_action( 'wp_ajax_dismiss_new_tc_notice', array( $this, 'dismiss_new_tc_notice' ) );
92-
add_action( 'wp_ajax_dismiss_onboarding_promo_notice', array( $this, 'dismiss_onboarding_promo_notice' ) );
9391

9492
$this->register_feedback_settings();
9593

@@ -163,52 +161,6 @@ public function dismiss_new_tc_notice() {
163161
$this->ensure_ajax_response( $response );
164162
}
165163

166-
/**
167-
* Dismiss onboarding promo notice.
168-
*
169-
* @return void
170-
*/
171-
public function dismiss_onboarding_promo_notice() {
172-
$response = array(
173-
'success' => false,
174-
'code' => 'ti__ob_not_allowed',
175-
'message' => 'Not allowed!',
176-
);
177-
178-
if ( ! isset( $_REQUEST['nonce'] ) ) {
179-
$this->ensure_ajax_response( $response );
180-
return;
181-
}
182-
183-
$nonce = sanitize_text_field( wp_unslash( $_REQUEST['nonce'] ) );
184-
185-
if ( ! wp_verify_nonce( $nonce, 'dismiss_onboarding_promo_notice' ) ) {
186-
$this->ensure_ajax_response( $response );
187-
return;
188-
}
189-
190-
if ( ! current_user_can( 'install_plugins' ) ) {
191-
$this->ensure_ajax_response( $response );
192-
return;
193-
}
194-
195-
$response['success'] = true;
196-
unset( $response['code'] );
197-
unset( $response['message'] );
198-
199-
update_option( self::ONBOARDING_PROMO_NOTICE_DISMISSED, 'yes' );
200-
$this->ensure_ajax_response( $response );
201-
}
202-
203-
/**
204-
* Decide if the onboarding promo notice should be shown.
205-
*
206-
* @return bool
207-
*/
208-
private function should_show_onboarding_promo_notice() {
209-
return get_option( self::ONBOARDING_PROMO_NOTICE_DISMISSED, 'no' ) !== 'yes';
210-
}
211-
212164
/**
213165
* Decide if the business/agency variant of the onboarding promo text should be shown.
214166
*
@@ -937,11 +889,6 @@ private function get_localization() {
937889
'ajaxURL' => esc_url( admin_url( 'admin-ajax.php' ) ),
938890
'nonce' => wp_create_nonce( 'dismiss_new_tc_notice' ),
939891
),
940-
'onboardingPromoNotice' => array(
941-
'show' => $this->should_show_onboarding_promo_notice(),
942-
'ajaxURL' => esc_url( admin_url( 'admin-ajax.php' ) ),
943-
'nonce' => wp_create_nonce( 'dismiss_onboarding_promo_notice' ),
944-
),
945892
'onboardingPluginCompatibility' => array(
946893
'hyve-lite' => is_php_version_compatible( '8.1' ),
947894
),

includes/Starter_Ranking.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,9 @@ private static function fetch_order( $builder, $query = null, $budget = null ) {
8989
$deadline = time() + ( null !== $budget ? (int) $budget : self::REQUEST_BUDGET );
9090
$start = self::base_url() . '/api/workflows/' . self::SLUG . '/start';
9191
$body = array(
92-
'site_url' => home_url(),
93-
'builder' => $builder,
92+
'site_url' => home_url(),
93+
'site_title' => sanitize_text_field( get_bloginfo( 'name' ) ),
94+
'builder' => $builder,
9495
);
9596

9697
if ( null !== $query && '' !== $query ) {

onboarding/src/Components/OnboardingPromoNotice.js

Lines changed: 0 additions & 96 deletions
This file was deleted.

onboarding/src/Components/Sites.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ const Sites = ( {
2727
sortBy,
2828
selectedColors,
2929
} ) => {
30-
const [ maxShown, setMaxShown ] = useState( 9 );
30+
const [ maxShown, setMaxShown ] = useState( 12 );
3131
const { sites = {} } = getSites;
3232

3333
useEffect( () => {
34-
setMaxShown( 9 );
34+
setMaxShown( 12 );
3535
}, [ editor, category, searchQuery, sortBy, selectedColors ] );
3636

3737
const getBuilders = () => Object.keys( sites );
@@ -218,9 +218,9 @@ const Sites = ( {
218218
const rest = filterByColors( filterByCategory( ranked, category ) ).filter(
219219
( site ) => site && site.slug && ! inMatches[ site.slug ]
220220
);
221-
const remainder = matches.length % 3;
221+
const remainder = matches.length % 4;
222222
const pad =
223-
remainder === 0 ? 0 : Math.min( 3 - remainder, rest.length );
223+
remainder === 0 ? 0 : Math.min( 4 - remainder, rest.length );
224224

225225
return {
226226
list: [ ...matches, ...rest ],
@@ -280,7 +280,7 @@ const Sites = ( {
280280
return false;
281281
}
282282

283-
setMaxShown( ( shown ) => shown + 9 );
283+
setMaxShown( ( shown ) => shown + 12 );
284284
} }
285285
>
286286
<span

onboarding/src/Components/StarterSiteCard.js

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ const StarterSiteCard = ( {
4747
setSite,
4848
handleNextStep,
4949
trackingId,
50-
editor
50+
editor,
51+
selectedColors = []
5152
} ) => {
5253
const {
5354
upsell,
@@ -93,6 +94,32 @@ const StarterSiteCard = ( {
9394
screenshot: screenshotMap[ color ] || screenshot,
9495
} ) );
9596
}, [ data?.colors, data?.screenshots_by_color, screenshot ] );
97+
// First globally-selected color this card actually has, returned as the
98+
// canonical RAW colorOptions slug so option.slug === activeColor still hits.
99+
// selectedColors are normalized lowercase (Filters.js) while colorOptions
100+
// slugs / screenshots_by_color keys are raw, so match case-insensitively.
101+
const filterColor = useMemo( () => {
102+
if (
103+
! Array.isArray( selectedColors ) ||
104+
! selectedColors.length ||
105+
! colorOptions.length
106+
) {
107+
return '';
108+
}
109+
110+
const normalizedToSlug = new Map(
111+
colorOptions.map( ( option ) => [
112+
String( option.slug || '' ).trim().toLowerCase(),
113+
option.slug,
114+
] )
115+
);
116+
117+
const matched = selectedColors.find( ( color ) =>
118+
normalizedToSlug.has( color )
119+
);
120+
121+
return matched ? normalizedToSlug.get( matched ) : '';
122+
}, [ selectedColors, colorOptions ] );
96123
const previewColors = colorOptions.slice( 0, 2 );
97124
const [ activePage, setActivePage ] = useState( pageShots[0]?.key || 'home' );
98125
const [ activeColor, setActiveColor ] = useState( colorOptions[0]?.slug || '' );
@@ -102,9 +129,12 @@ const StarterSiteCard = ( {
102129
setActivePage( pageShots[0]?.key || 'home' );
103130
}, [ pageShots ] );
104131

132+
// Seed the displayed color from the global filter when this card matches a
133+
// selected color; otherwise fall back to the first palette. Re-runs only on
134+
// filter/colorOptions changes, so manual swatch clicks survive until then.
105135
useEffect( () => {
106-
setActiveColor( colorOptions[0]?.slug || '' );
107-
}, [ colorOptions ] );
136+
setActiveColor( filterColor || colorOptions[0]?.slug || '' );
137+
}, [ colorOptions, filterColor ] );
108138

109139
const activePageShot =
110140
pageShots.find( ( shot ) => shot.key === activePage ) || pageShots[0] || null;
@@ -194,7 +224,7 @@ const StarterSiteCard = ( {
194224
) ) }
195225
</div>
196226
) }
197-
{ colorOptions.length > 0 && (
227+
{ colorOptions.length > 1 && (
198228
<div
199229
className={
200230
isColorExpanded
@@ -290,12 +320,14 @@ export default compose(
290320
getCurrentEditor,
291321
getCurrentCategory,
292322
getSearchQuery,
323+
getSelectedColors,
293324
} = select( 'ti-onboarding' );
294325
return {
295326
trackingId: getTrackingId(),
296327
editor: getCurrentEditor(),
297328
category: getCurrentCategory(),
298329
query: getSearchQuery(),
330+
selectedColors: getSelectedColors() || [],
299331
};
300332
} ),
301333
withDispatch( ( dispatch, { data } ) => {

0 commit comments

Comments
 (0)