Skip to content

Commit

Permalink
Stepper framework: Add importers steps (#63525)
Browse files Browse the repository at this point in the history
* Fix styling issues inside stepper framework

* Adjust importer routes

* Set current user in the redux store

* Add block margin to the importer-step

* Update step navigator to support path with query params

* Add util functions

* Update getWpComOnboardingUrl method to support switch between frameworks

* Add importer hooks

* Create common importer wrapper component

* Create Blogger importer step

* Create Medium importer step

* Create Squarespace importer step

* Create Wix importer step

* Create Wordpress importer step

* Update navigation typing to support arg: StepPath + QueryParams

* Register importer steps

* Configure importer navigation

* Configure route to consume importer from the stepper framework

* Fix typing error

* Optimize calling analyzeUrl: prevent calling for an undefined value

* Replace providing siteId reference

* Disable query fetching on each window focus
Fixes issue with multiple rerenders when user tries to choose a file for upload. Before this commit, a user wasn't able to uplad a file.

* Optimize initial fetching
Fetch only selected site instaed of fetching all

* Improve generateStepPath helper method

* Update tests setup to support  and  frameworks

* Add stepper import flows e2e tests
  • Loading branch information
bogiii authored May 18, 2022
1 parent d647722 commit 4b2a762
Show file tree
Hide file tree
Showing 35 changed files with 733 additions and 34 deletions.
2 changes: 1 addition & 1 deletion client/landing/stepper/declarative-flow/anchor-fm-flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const anchorFmFlow: Flow = {
}
};

const goToStep = ( step: StepPath ) => {
const goToStep = ( step: StepPath | `${ StepPath }?${ string }` ) => {
navigate( step );
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ button {
}
}

.import-step {
.import-step,
.importer-step {
@include onboarding-block-margin;
}

Expand Down
12 changes: 7 additions & 5 deletions client/landing/stepper/declarative-flow/internals/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@ export const FlowRenderer: React.FC< { flow: Flow } > = ( { flow } ) => {
const currentRoute = location.pathname.substring( 1 ) as StepPath;
const history = useHistory();
const { search } = useLocation();
const stepNavigation = flow.useStepNavigation( currentRoute, ( path: StepPath ) =>
history.push( generatePath( '/' + path + search ), stepPaths )
);
const stepNavigation = flow.useStepNavigation( currentRoute, ( path ) => {
const _path = path.includes( '?' ) // does path contain search params
? generatePath( '/' + path )
: generatePath( '/' + path + search );

history.push( _path, stepPaths );
} );
const pathToClass = ( path: string ) =>
path.replace( /([a-z0-9])([A-Z])/g, '$1-$2' ).toLowerCase();

Expand All @@ -41,8 +45,6 @@ export const FlowRenderer: React.FC< { flow: Flow } > = ( { flow } ) => {
return (
<Switch>
{ stepPaths.map( ( path ) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - steps with slash char cause type issue ( example: import/list )
const StepComponent = Steps[ path ];
return (
<Route key={ path } path={ `/${ path }` }>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ const ImportReadyPreview: Step = function ImportStep( props ) {
siteSlug as string,
urlData.url,
urlData.platform,
isAtomicSite
isAtomicSite,
'stepper'
);

navigation.submit?.( { url } );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ const ImportReady: Step = function ImportStep( props ) {
siteSlug as string,
urlData.url,
urlData.platform,
isAtomicSite
isAtomicSite,
'stepper'
);

navigation.submit?.( { url } );
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const BASE_ROUTE = 'import';
export const BASE_STEPPER_ROUTE = 'setup';
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ export function getFinalImporterUrl(
targetSlug: string,
fromSite: string,
platform: ImporterPlatform,
isAtomicSite: boolean | null
isAtomicSite: boolean | null,
framework: 'signup' | 'stepper' = 'signup'
) {
let importerUrl;

Expand All @@ -26,19 +27,27 @@ export function getFinalImporterUrl(
isEnabled( `onboarding/import-from-${ platform }` )
)
) {
importerUrl = getWpComOnboardingUrl( targetSlug, platform, fromSite );
importerUrl = getWpComOnboardingUrl( targetSlug, platform, fromSite, framework );
} else {
importerUrl = getImporterUrl( targetSlug, platform, fromSite );
}

return importerUrl;
}

export function generateStepPath( stepName: string, stepSectionName?: string ): StepPath {
// In the stepper framework, the capture screen is on `import` route (instead of `importCapture`)
const excludeStepName = 'capture';
const routes = [ BASE_ROUTE, stepName, stepSectionName ].filter( ( x ) => x !== 'capture' );
const path = routes.join( '_' ).replace( excludeStepName, '' );
/**
* Stepper's redirection handlers
* generateStepPath share the same interface/params between 'signup' & 'stepper' frameworks
*/
export function generateStepPath(
stepName: string | StepPath,
stepSectionName?: string
): StepPath {
if ( stepName === 'intent' ) return 'intent';
else if ( stepName === 'capture' ) return BASE_ROUTE;

const routes = [ BASE_ROUTE, stepName, stepSectionName ];
const path = routes.join( '_' );

return camelCase( path ) as StepPath;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ export function redirect( to: string ) {
window.location.href = to;
}

// Update query params without refresh/rerender
export function updateQueryParams( queryParams: string ) {
const path = `${ window.location.pathname }?${ queryParams }`;
window.history.pushState( { path }, '', path );
}

export function removeTrailingSlash( str: string ) {
return str.replace( /\/+$/, '' );
}

export function removeLeadingSlash( str: string ) {
return str.replace( /^\/+/, '' );
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Step } from 'calypso/landing/stepper/declarative-flow/internals/types';
import BloggerImporter from 'calypso/signup/steps/import-from/blogger';
import { withImporterWrapper } from '../importer';
import './style.scss';

const ImporterBlogger: Step = function ( props ) {
const Importer = withImporterWrapper( BloggerImporter );

return <Importer importer={ 'blogger' } { ...props } />;
};

export default ImporterBlogger;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@import 'signup/steps/import/style/base';
@import '../importer/style';
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Step } from 'calypso/landing/stepper/declarative-flow/internals/types';
import MediumImporter from 'calypso/signup/steps/import-from/medium';
import { withImporterWrapper } from '../importer';
import './style.scss';

const ImporterMedium: Step = function ( props ) {
const Importer = withImporterWrapper( MediumImporter );

return <Importer importer={ 'medium' } { ...props } />;
};

export default ImporterMedium;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@import 'signup/steps/import/style/base';
@import '../importer/style';
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Step } from 'calypso/landing/stepper/declarative-flow/internals/types';
import SquarespaceImporter from 'calypso/signup/steps/import-from/squarespace';
import { withImporterWrapper } from '../importer';
import './style.scss';

const ImporterSquarespace: Step = function ( props ) {
const Importer = withImporterWrapper( SquarespaceImporter );

return <Importer importer={ 'squarespace' } { ...props } />;
};

export default ImporterSquarespace;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@import 'signup/steps/import/style/base';
@import '../importer/style';
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Step } from 'calypso/landing/stepper/declarative-flow/internals/types';
import WixImporter from 'calypso/signup/steps/import-from/wix';
import { withImporterWrapper } from '../importer';
import './style.scss';

const ImporterWix: Step = function ( props ) {
const Importer = withImporterWrapper( WixImporter );

return <Importer importer={ 'wix' } { ...props } />;
};

export default ImporterWix;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@import 'signup/steps/import/style/base';
@import '../importer/style';
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Step } from 'calypso/landing/stepper/declarative-flow/internals/types';
import WordpressImporter from 'calypso/signup/steps/import-from/wordpress';
import { withImporterWrapper } from '../importer';
import './style.scss';

const ImporterWordpress: Step = function ( props ) {
const Importer = withImporterWrapper( WordpressImporter );

return <Importer importer={ 'wordpress' } { ...props } />;
};

export default ImporterWordpress;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@import 'signup/steps/import/style/base';
@import '../importer/style';

.importer-wordpress .step-container__content {
padding: 20px;
box-sizing: border-box;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useEffect } from 'react';
import { useSelector } from 'react-redux';
import { useQuery } from 'calypso/landing/stepper/hooks/use-query';
import { useSiteSlugParam } from 'calypso/landing/stepper/hooks/use-site-slug-param';
import { getSite } from 'calypso/state/sites/selectors';
import { updateQueryParams } from '../../import/util';

/**
* Update site slug when destination site is in transition from simple to atomic
*/
export function useAtomicTransferQueryParamUpdate( siteId: number | undefined ) {
const currentSearchParams = useQuery();
const siteItem = useSelector( ( state ) => getSite( state, siteId as number ) );
const siteSlug = useSiteSlugParam();

useEffect( checkSiteSlugUpdate, [ siteItem?.slug ] );

function checkSiteSlugUpdate() {
if ( siteItem?.slug && siteSlug !== siteItem.slug ) {
currentSearchParams.set( 'siteSlug', siteItem.slug );
updateQueryParams( currentSearchParams.toString() );
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useEffect, useState } from 'react';
import { useQuery } from 'calypso/landing/stepper/hooks/use-query';
import { updateQueryParams } from '../../import/util';

export function useInitialQueryRun( siteId: number | undefined ) {
const currentSearchParams = useQuery();
const [ runImportInitially, setRunImportInitially ] = useState( false );

useEffect( checkInitialRunState, [ siteId ] );

function checkInitialRunState() {
// run query param indicates that the import process can be run immediately,
// but before proceeding, remove it from the URL path
// because of the browser's back or refresh edge cases
if ( currentSearchParams.get( 'run' ) === 'true' ) {
setRunImportInitially( true );
currentSearchParams.delete( 'run' );
updateQueryParams( currentSearchParams.toString() );
}
}

return runImportInitially;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { addQueryArgs } from 'calypso/lib/route';
import { useCheckoutUrl } from 'calypso/signup/steps/import-from/hooks/use-checkout-url';
import { WPImportOption } from 'calypso/signup/steps/import-from/wordpress/types';
import { getWpOrgImporterUrl } from 'calypso/signup/steps/import/util';
import { StepPath } from '../../../steps-repository';
import { BASE_STEPPER_ROUTE } from '../../import/config';
import { removeLeadingSlash } from '../../import/util';
import type { NavigationControls } from '../../../types';
import type { StepNavigator } from 'calypso/signup/steps/import-from/types';

export function useStepNavigator(
navigation: NavigationControls,
siteId: number,
siteSlug: string,
fromSite: string
): StepNavigator {
const checkoutUrl = useCheckoutUrl( siteId as number, siteSlug as string );

function navigator( path: string ) {
const stepPath = removeLeadingSlash( path.replace( BASE_STEPPER_ROUTE, '' ) );
navigation.goToStep?.( stepPath as StepPath );
}

function goToIntentPage() {
navigation.goToStep?.( 'intent' );
}

function goToImportCapturePage() {
navigation.goToStep?.( 'import' );
}

function goToSiteViewPage() {
navigation.submit?.( {
type: 'redirect',
url: `/view/${ siteSlug || '' }`,
} );
}

function goToCheckoutPage() {
navigation.submit?.( {
type: 'redirect',
url: getCheckoutUrl(),
} );
}

function goToWpAdminImportPage() {
navigation.submit?.( {
type: 'redirect',
url: `/import/${ siteSlug }`,
} );
}

function goToWpAdminWordPressPluginPage() {
navigation.submit?.( {
type: 'redirect',
url: getWpOrgImporterUrl( siteSlug as string, 'wordpress' ),
} );
}

function getWordpressImportEverythingUrl(): string {
const queryParams = {
siteSlug: siteSlug,
from: fromSite,
option: WPImportOption.EVERYTHING,
run: true,
};

return addQueryArgs( queryParams, `/${ BASE_STEPPER_ROUTE }/importerWordpress` );
}

function getCheckoutUrl() {
const path = checkoutUrl;
const queryParams = {
redirect_to: getWordpressImportEverythingUrl(),
cancel_to: getWordpressImportEverythingUrl(),
};

return addQueryArgs( queryParams, path );
}

return {
goToIntentPage,
goToImportCapturePage,
goToSiteViewPage,
goToCheckoutPage,
goToWpAdminImportPage,
goToWpAdminWordPressPluginPage,
navigate: ( path ) => navigator( path ),
};
}
Loading

0 comments on commit 4b2a762

Please sign in to comment.