Skip to content

Commit

Permalink
Site Migration: Add migration survey (#95408)
Browse files Browse the repository at this point in the history
* Add generic survey component

* Move folder

* Implement migration survey

* Add survey manager

* Update survey layout

* Add missing asset

* Track events

* Limit to display the survey only for english locales
  • Loading branch information
gabrielcaires authored Oct 24, 2024
1 parent 3f6d739 commit 8095abc
Show file tree
Hide file tree
Showing 10 changed files with 547 additions and 0 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useIsEnglishLocale } from '@automattic/i18n-utils';
import {
MIGRATION_FLOW,
SITE_MIGRATION_FLOW,
HOSTED_SITE_MIGRATION_FLOW,
MIGRATION_SIGNUP_FLOW,
} from '@automattic/onboarding';
import { Suspense } from 'react';
import { useFlowNavigation } from '../../hooks/use-flow-navigation';
import AsyncMigrationSurvey from '../../steps-repository/components/migration-survey/async';

const MIGRATION_SURVEY_FLOWS = [
MIGRATION_FLOW,
SITE_MIGRATION_FLOW,
HOSTED_SITE_MIGRATION_FLOW,
MIGRATION_SIGNUP_FLOW,
];

const SurveyManager = () => {
const { params } = useFlowNavigation();
const isEnLocale = useIsEnglishLocale();

// Skip survey for non-English locales
if ( ! isEnLocale ) {
return null;
}

if ( ! params.flow ) {
return null;
}

if ( ! MIGRATION_SURVEY_FLOWS.includes( params.flow ) ) {
return null;
}

return (
<Suspense>
<AsyncMigrationSurvey />
</Suspense>
);
};

export default SurveyManager;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { lazy } from 'react';

const AsyncMigrationSurvey = lazy( () => import( './index' ) );

export default AsyncMigrationSurvey;
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Button } from '@wordpress/components';
import { translate } from 'i18n-calypso';
import surveyImage from 'calypso/assets/images/onboarding/migrations/survey/wordpress-half-logo.png';
import { Survey, SurveyProps, SurveyTriggerAccept, SurveyTriggerSkip } from '../survey';
import './style.scss';

type MigrationSurveyProps = Pick< SurveyProps, 'isOpen' >;

const MigrationSurvey = ( { isOpen }: MigrationSurveyProps ) => {
return (
<Survey
name="migration-survey"
className="migration-survey"
title={ translate( 'Migration Survey' ) }
isOpen={ isOpen }
>
<div className="migration-survey__popup-img">
<img src={ surveyImage } alt={ translate( 'Code editor' ) } />
</div>
<div className="migration-survey__popup-content">
<h3 className="migration-survey__popup-content-title">
{ translate( 'Shape the Future of WordPress.com' ) }
</h3>
<div className="migration-survey__popup-content-description">
{ translate(
'Got a minute? Tell us about your WordPress.com journey in our brief survey and help us serve you better.'
) }
</div>
<div className="migration-survey__popup-content-buttons">
<SurveyTriggerSkip asChild>
<Button className="migration-survey__popup-content-buttons-cancel">
{ translate( 'Maybe later' ) }
</Button>
</SurveyTriggerSkip>
<SurveyTriggerAccept asChild>
<Button
variant="primary"
target="_blank"
href="https://automattic.survey.fm/wp-com-migration-survey"
>
{ translate( 'Take survey' ) }
</Button>
</SurveyTriggerAccept>
</div>
</div>
</Survey>
);
};

export default MigrationSurvey;
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.migration-survey {
.migration-survey__popup-head {
background: #3858e9;
}

.migration-survey__popup-content {
padding: 18px 24px 30px;
background: var(--studio-white);

.migration-survey__popup-content-title {
font-weight: 500;
padding-bottom: 8px;
}

.migration-survey__popup-content-description {
font-size: rem(14px);
line-height: 20px;
padding-bottom: 18px;
}

.migration-survey__popup-content-buttons {
display: flex;
justify-content: flex-end;
}
.migration-survey__popup-img {
background: #0675c4;
padding-bottom: 57.9%;

img {
width: 100%;
display: block;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { Gridicon } from '@automattic/components';
import { Button } from '@wordpress/components';
import clsx from 'clsx';
import cookie from 'cookie';
import React, { cloneElement, useCallback, useContext, useMemo, useState } from 'react';
import { recordTracksEvent } from 'calypso/lib/analytics/tracks';
import {
type SurveyContextType,
type SurveyActionsContextType,
type TriggerProps,
type SurveyProps,
} from './types';
export * from './types';
import './style.scss';

const SurveyContext = React.createContext< SurveyContextType | undefined >( undefined );

export const SurveyActionsContext = React.createContext< SurveyActionsContextType >( {
accept: () => {},
skip: () => {},
} );

const Trigger = ( { asChild, children, onClick, as }: TriggerProps ) => {
if ( asChild ) {
return cloneElement( children, { onClick } );
}
const Tag = as ?? 'button';
return <Tag onClick={ onClick }>{ children }</Tag>;
};

export const SurveyTriggerAccept = ( {
children,
as = 'button',
asChild,
}: Omit< TriggerProps, 'onClick' > ) => {
const { accept } = useContext( SurveyActionsContext );
return (
<Trigger as={ as } onClick={ accept } asChild={ asChild }>
{ children }
</Trigger>
);
};

export const SurveyTriggerSkip = ( {
children,
as = 'span',
asChild,
}: Omit< TriggerProps, 'onClick' > ) => {
const { skip } = useContext( SurveyActionsContext );
return (
<Trigger as={ as } onClick={ skip } asChild={ asChild }>
{ children }
</Trigger>
);
};

const bemElement =
( customClassName?: string ) =>
( element: string ): string | undefined => {
if ( customClassName ) {
return `${ customClassName }__${ element }`;
}

return undefined;
};

const ONE_YEAR_IN_SECONDS = 1000 * 60 * 60 * 24 * 365;
const ONE_DAY_IN_SECONDS = 1000 * 60 * 60 * 24;
/**
* Generic Survey component
* @example
* ```tsx
* <Survey name="survey-name">
* <div>
* <h1>Survey</h1>
* <img src="https://wordpress.org/about/assets/images/wp-logo.png" alt="WordPress" />
* <SurveyTriggerAccept>
* <Button>Take the survey</Button>
* </SurveyTriggerAccept>
* <SurveyTriggerSkip>
* <Button>Skip the survey</Button>
* </SurveyTriggerSkip>
* </div>
* </Survey>
* ```
*/
export const Survey = ( {
children,
name,
onAccept,
onSkip,
isOpen = true,
title,
className,
}: SurveyProps ) => {
const cookieValue = cookie.parse( document.cookie );
const shouldShow = ! cookieValue[ name ];
const [ shouldShowSurvey, setShouldShowSurvey ] = useState( isOpen && shouldShow );
const element = bemElement( className );

const handleClose = useCallback(
( reason: 'skip' | 'accept' | 'skip_backdrop' ) => {
const PERIOD = reason === 'skip_backdrop' ? ONE_DAY_IN_SECONDS : ONE_YEAR_IN_SECONDS;

document.cookie = cookie.serialize( name, reason, {
expires: new Date( Date.now() + PERIOD ),
} );

if ( reason === 'accept' ) {
recordTracksEvent( 'calypso_survey_accepted', { survey: name, action: reason } );
onAccept?.();
}

if ( reason === 'skip' ) {
recordTracksEvent( 'calypso_survey_skipped', { survey: name, action: reason } );
onSkip?.();
}

setShouldShowSurvey( false );
},
[ name, onAccept, onSkip ]
);

const actions = useMemo(
() => ( {
accept: () => handleClose( 'accept' ),
skip: () => handleClose( 'skip' ),
} ),
[ handleClose ]
);

if ( ! shouldShowSurvey ) {
return null;
}

return (
<SurveyContext.Provider value={ { isOpen } }>
<SurveyActionsContext.Provider value={ actions }>
<div aria-label={ name } className={ clsx( 'survey-notice', className ) }>
<SurveyTriggerSkip asChild>
<button className={ clsx( 'survey-notice__backdrop', element( 'backdrop' ) ) } />
</SurveyTriggerSkip>
<div className={ clsx( 'survey-notice__popup', element( 'popup' ) ) }>
<div className={ clsx( 'survey-notice__popup-head', element( 'popup-head' ) ) }>
<div
className={ clsx(
'survey-notice__popup-head-title',
element( 'popup-head-title' )
) }
>
{ title }
</div>
<SurveyTriggerSkip asChild>
<Button
className={ clsx(
'survey-notice__popup-head-close',
element( 'popup-head-close' )
) }
>
<Gridicon icon="cross" size={ 16 } />
</Button>
</SurveyTriggerSkip>
</div>
{ children }
</div>
</div>
</SurveyActionsContext.Provider>
</SurveyContext.Provider>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
.survey-notice {
position: fixed;
left: 0;
top: 0;
height: 100%;
width: 100%;
z-index: 1000;

.survey-notice__backdrop {
background: var(--studio-black);
opacity: 0.2;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
cursor: default;
}

.survey-notice__popup {
position: absolute;
right: 25px;
bottom: 25px;
width: 416px;
max-width: calc(100% - 50px);
z-index: 999;
border-radius: 2px;
box-shadow:
0 3px 1px 0 rgba(0, 0, 0, 0.04),
0 3px 8px 0 rgba(0, 0, 0, 0.12);
overflow: hidden;
background-color: var(--studio-white);
}

.survey-notice__popup-head {
background: #0675c4;
border-bottom: 1px solid #f6f7f7;
height: 56px;
padding: 0 14px 0 16px;
display: flex;
align-items: center;
justify-content: space-between;

.survey-notice__popup-head-title {
color: var(--studio-white);
font-size: rem(14px);
font-weight: 500;
line-height: 20px;
letter-spacing: -0.15px;
}

.survey-notice__popup-head-close svg {
fill: var(--studio-white);
}
}
}
Loading

0 comments on commit 8095abc

Please sign in to comment.