diff --git a/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/launch-steps/domain-step/index.tsx b/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/launch-steps/domain-step/index.tsx index 376d5ed7d11d1..47429ffd1c984 100644 --- a/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/launch-steps/domain-step/index.tsx +++ b/apps/editing-toolkit/editing-toolkit-plugin/editor-site-launch/src/launch-steps/domain-step/index.tsx @@ -22,15 +22,9 @@ import './styles.scss'; const DomainStep: React.FunctionComponent< LaunchStepProps > = ( { onPrevStep, onNextStep } ) => { const { plan, domain } = useSelect( ( select ) => select( LAUNCH_STORE ).getState() ); const { currentDomainName } = useSite(); - const { domainSearch } = useDomainSearch(); + const { domainSearch, setDomainSearch } = useDomainSearch(); - const { - setDomain, - unsetDomain, - setDomainSearch, - unsetPlan, - confirmDomainSelection, - } = useDispatch( LAUNCH_STORE ); + const { setDomain, unsetDomain, unsetPlan, confirmDomainSelection } = useDispatch( LAUNCH_STORE ); const handleNext = () => { confirmDomainSelection(); diff --git a/packages/launch/jest.config.js b/packages/launch/jest.config.js new file mode 100644 index 0000000000000..bc9ea12d23b55 --- /dev/null +++ b/packages/launch/jest.config.js @@ -0,0 +1,7 @@ +module.exports = { + preset: '../../test/packages/jest-preset.js', + testEnvironment: 'jsdom', + globals: { + __i18n_text_domain__: 'default', + }, +}; diff --git a/packages/launch/package.json b/packages/launch/package.json index db9e3947a9acf..994dabb5714a5 100644 --- a/packages/launch/package.json +++ b/packages/launch/package.json @@ -30,11 +30,12 @@ "watch": "tsc --build ./tsconfig.json --watch" }, "dependencies": { - "@automattic/react-i18n": "^1.0.0-alpha.0", + "@automattic/calypso-analytics": "^1.0.0-alpha.1", "@automattic/data-stores": "^1.0.0-alpha.1", "@automattic/domain-picker": "^1.0.0-alpha.0", "@automattic/onboarding": "^1.0.0", "@automattic/plans-grid": "^1.0.0-alpha.0", + "@automattic/react-i18n": "^1.0.0-alpha.0", "@wordpress/components": "^10.0.5", "@wordpress/icons": "^2.4.0", "@wordpress/url": "^2.17.0", @@ -44,7 +45,8 @@ "devDependencies": { "@automattic/typography": "^1.0.0", "@wordpress/base-styles": "^2.0.1", - "copyfiles": "^2.3.0" + "copyfiles": "^2.3.0", + "@testing-library/react": "^10.0.5" }, "peerDependencies": { "@wordpress/data": "^4.22.3", diff --git a/packages/launch/src/constants.ts b/packages/launch/src/constants.ts new file mode 100644 index 0000000000000..76e50dc50742f --- /dev/null +++ b/packages/launch/src/constants.ts @@ -0,0 +1 @@ +export const FOCUSED_LAUNCH_FLOW_ID = 'focused-launch'; diff --git a/packages/launch/src/focused-launch/domain-details/index.tsx b/packages/launch/src/focused-launch/domain-details/index.tsx index 780197d5d9db1..65afc87ced873 100644 --- a/packages/launch/src/focused-launch/domain-details/index.tsx +++ b/packages/launch/src/focused-launch/domain-details/index.tsx @@ -3,22 +3,76 @@ /** * External dependencies */ -import React from 'react'; -import { Link } from 'react-router-dom'; +import * as React from 'react'; +import { useHistory } from 'react-router-dom'; + import { __ } from '@wordpress/i18n'; +import DomainPicker, { ITEM_TYPE_BUTTON } from '@automattic/domain-picker'; +import { Title, SubTitle } from '@automattic/onboarding'; +import { recordTracksEvent } from '@automattic/calypso-analytics'; +import type { DomainSuggestions } from '@automattic/data-stores'; /** * Internal dependencies */ -import { Route } from '../route'; +import { useSite, useDomainSearch, useDomainSelection } from '../../hooks'; +import { FOCUSED_LAUNCH_FLOW_ID } from '../../constants'; +import GoBackButton from '../go-back-button'; import './style.scss'; +const ANALYTICS_UI_LOCATION = 'domain_step'; + const DomainDetails: React.FunctionComponent = () => { + const { currentDomainName } = useSite(); + const { domainSearch, setDomainSearch } = useDomainSearch(); + const { onDomainSelect, onExistingSubdomainSelect, selectedDomain } = useDomainSelection(); + const history = useHistory(); + + const goBack = () => { + history.goBack(); + }; + + const handleDomainSelect = ( suggestion: DomainSuggestions.DomainSuggestion ) => { + onDomainSelect( suggestion ); + goBack(); + }; + + const trackDomainSearchInteraction = ( query: string ) => { + recordTracksEvent( 'calypso_newsite_domain_search_blur', { + flow: FOCUSED_LAUNCH_FLOW_ID, + query, + where: ANALYTICS_UI_LOCATION, + } ); + }; + return ( -
- { __( 'Go back', __i18n_text_domain__ ) } -

{ __( 'Choose a domain', __i18n_text_domain__ ) }

+
+
+
+ +
+ { __( 'Choose a domain', __i18n_text_domain__ ) } + + { __( 'Free for the first year with any paid plan.', __i18n_text_domain__ ) } + +
+
+ +
); }; diff --git a/packages/launch/src/focused-launch/domain-details/style.scss b/packages/launch/src/focused-launch/domain-details/style.scss index e69de29bb2d1d..cc126dbc2a390 100644 --- a/packages/launch/src/focused-launch/domain-details/style.scss +++ b/packages/launch/src/focused-launch/domain-details/style.scss @@ -0,0 +1,18 @@ +@import '~@automattic/onboarding/styles/mixins'; + +.focused-launch-domain-details__header { + margin-bottom: 38px; + + @include break-medium { + margin-bottom: 64px; + } + + .focused-launch-domain-details__back-link { + button.action-buttons__back { + color: var( --studio-black ); + margin-bottom: 16px; + display: flex; + align-items: center; + } + } +} \ No newline at end of file diff --git a/packages/launch/src/focused-launch/domain-details/test/index.tsx b/packages/launch/src/focused-launch/domain-details/test/index.tsx new file mode 100644 index 0000000000000..2cdc464d8fa62 --- /dev/null +++ b/packages/launch/src/focused-launch/domain-details/test/index.tsx @@ -0,0 +1,44 @@ +/** + * External dependencies + */ +import React from 'react'; +import { MemoryRouter as Router } from 'react-router-dom'; +import { render, screen } from '@testing-library/react'; + +/** + * Internal dependencies + */ +import DomainStep from '../'; + +describe( 'DomainStep', () => { + test( 'Has title and sub-title', () => { + render( + + + + ); + + expect( screen.queryByText( /Choose a domain/i ) ).toBeTruthy(); + expect( screen.queryByText( /Free for the first year with any paid plan/i ) ).toBeTruthy(); + } ); + + test( 'Has domain search input', () => { + render( + + + + ); + + expect( screen.queryByPlaceholderText( /Search for a domain/i ) ).toBeTruthy(); + } ); + + test( 'Has a back button', () => { + render( + + + + ); + + expect( screen.queryByText( /Go back/i ) ).toBeTruthy(); + } ); +} ); diff --git a/packages/launch/src/focused-launch/style.scss b/packages/launch/src/focused-launch/style.scss index e69de29bb2d1d..5d165cc32d792 100644 --- a/packages/launch/src/focused-launch/style.scss +++ b/packages/launch/src/focused-launch/style.scss @@ -0,0 +1,3 @@ +.focused-launch-container { + padding: 0 10px; +} \ No newline at end of file diff --git a/packages/launch/src/focused-launch/summary/index.tsx b/packages/launch/src/focused-launch/summary/index.tsx index 918c9f54ada7d..33f3b9bd3f766 100644 --- a/packages/launch/src/focused-launch/summary/index.tsx +++ b/packages/launch/src/focused-launch/summary/index.tsx @@ -20,10 +20,19 @@ import FocusedLaunchSummaryItem, { * Internal dependencies */ import { Route } from '../route'; -import { useTitle, useDomainSearch, useSiteDomains, useSite, usePlans } from '../../hooks'; +import { + useTitle, + useDomainSearch, + useSiteDomains, + useDomainSelection, + useSite, + usePlans, +} from '../../hooks'; + import { LAUNCH_STORE } from '../../stores'; import LaunchContext from '../../context'; import { isDefaultSiteTitle } from '../../utils'; +import { FOCUSED_LAUNCH_FLOW_ID } from '../../constants'; import './style.scss'; @@ -181,8 +190,8 @@ const DomainStep: React.FunctionComponent< DomainStepProps > = ( { onExistingSubdomainSelect={ onExistingSubdomainSelect } initialDomainSearch={ initialDomainSearch } showSearchField={ false } - analyticsFlowId="focused-launch" - analyticsUiAlgo="focused_launch_domain_picker" + analyticsFlowId={ FOCUSED_LAUNCH_FLOW_ID } + analyticsUiAlgo="summary_domain_step" quantity={ 3 } quantityExpanded={ 3 } itemType="individual-item" @@ -437,8 +446,8 @@ const Summary: React.FunctionComponent = () => { const { title, updateTitle, saveTitle, isSiteTitleStepVisible, showSiteTitleStep } = useTitle(); const { sitePrimaryDomain, siteSubdomain, hasPaidDomain } = useSiteDomains(); + const { onDomainSelect, onExistingSubdomainSelect } = useDomainSelection(); const selectedDomain = useSelect( ( select ) => select( LAUNCH_STORE ).getSelectedDomain() ); - const { setDomain, unsetDomain } = useDispatch( LAUNCH_STORE ); const { domainSearch, isLoading } = useDomainSearch(); const site = useSite(); @@ -483,14 +492,14 @@ const Summary: React.FunctionComponent = () => { currentDomain={ selectedDomain?.domain_name ?? sitePrimaryDomain?.domain } initialDomainSearch={ domainSearch } hasPaidDomain={ hasPaidDomain } - onDomainSelect={ setDomain } isLoading={ isLoading } + onDomainSelect={ onDomainSelect } /** NOTE: this makes the assumption that the user has a free domain, * thus when they click the free domain, we just remove the value from the store * this is a valid strategy in this context because they won't even see this step if * they already have a paid domain * */ - onExistingSubdomainSelect={ unsetDomain } + onExistingSubdomainSelect={ onExistingSubdomainSelect } locale={ locale } /> ); diff --git a/packages/launch/src/hooks/index.ts b/packages/launch/src/hooks/index.ts index 2791fbbdc7d02..c3352bc88acea 100644 --- a/packages/launch/src/hooks/index.ts +++ b/packages/launch/src/hooks/index.ts @@ -2,6 +2,7 @@ export * from './use-site'; export * from './use-on-launch'; export * from './use-domain-suggestion'; export * from './use-domain-search'; +export * from './use-domain-selection'; export * from './use-title'; export * from './use-site-domains'; export * from './use-plans'; diff --git a/packages/launch/src/hooks/use-domain-search.ts b/packages/launch/src/hooks/use-domain-search.ts index fd48aa1a132f2..2ede9ac2f4d69 100644 --- a/packages/launch/src/hooks/use-domain-search.ts +++ b/packages/launch/src/hooks/use-domain-search.ts @@ -2,7 +2,7 @@ * External dependencies */ import { useSelect } from '@wordpress/data'; - +import { useDispatch } from '@wordpress/data'; /** * External dependencies */ @@ -10,10 +10,15 @@ import { LAUNCH_STORE } from '../stores'; import { useSite, useTitle } from './'; import { isDefaultSiteTitle } from '../utils'; -export function useDomainSearch(): { domainSearch: string; isLoading: boolean } { +export function useDomainSearch(): { + domainSearch: string; + isLoading: boolean; + setDomainSearch: ( search: string ) => void; +} { const { domainSearch } = useSelect( ( select ) => select( LAUNCH_STORE ).getState() ); const { title } = useTitle(); const { currentDomainName, isLoadingSite } = useSite(); + const { setDomainSearch } = useDispatch( LAUNCH_STORE ); let search = domainSearch.trim() || title; @@ -24,5 +29,6 @@ export function useDomainSearch(): { domainSearch: string; isLoading: boolean } return { domainSearch: search, isLoading: isLoadingSite, + setDomainSearch, }; } diff --git a/packages/launch/src/hooks/use-domain-selection.ts b/packages/launch/src/hooks/use-domain-selection.ts new file mode 100644 index 0000000000000..c7cbff0755375 --- /dev/null +++ b/packages/launch/src/hooks/use-domain-selection.ts @@ -0,0 +1,34 @@ +/** + * External dependencies + */ +import { useDispatch, useSelect } from '@wordpress/data'; +import type { DomainSuggestions } from '@automattic/data-stores'; + +/** + * Internal dependencies + */ +import { LAUNCH_STORE } from '../stores'; + +export function useDomainSelection() { + const { plan } = useSelect( ( select ) => select( LAUNCH_STORE ).getState() ); + const { setDomain, unsetDomain, unsetPlan, confirmDomainSelection } = useDispatch( LAUNCH_STORE ); + const { domain: selectedDomain } = useSelect( ( select ) => select( LAUNCH_STORE ).getState() ); + + function onDomainSelect( suggestion: DomainSuggestions.DomainSuggestion ) { + confirmDomainSelection(); + setDomain( suggestion ); + if ( plan?.isFree ) { + unsetPlan(); + } + } + + function onExistingSubdomainSelect() { + unsetDomain(); + } + + return { + onDomainSelect, + onExistingSubdomainSelect, + selectedDomain, + }; +} diff --git a/packages/launch/tsconfig.json b/packages/launch/tsconfig.json index dd7d81c212a68..85c13d0e68efb 100644 --- a/packages/launch/tsconfig.json +++ b/packages/launch/tsconfig.json @@ -38,6 +38,7 @@ { "path": "../components" }, { "path": "../onboarding" }, { "path": "../domain-picker" }, - { "path": "../plans-grid" } + { "path": "../plans-grid" }, + { "path": "../calypso-analytics" } ] }