Skip to content

Commit

Permalink
Merge pull request #2682 from woocommerce/feature/2458-2459-2460-2509
Browse files Browse the repository at this point in the history
Streamline onboarding, post-onboarding and campaign setup
  • Loading branch information
eason9487 authored Nov 25, 2024
2 parents 3975201 + 6c15833 commit 4173854
Show file tree
Hide file tree
Showing 226 changed files with 5,905 additions and 7,672 deletions.
51 changes: 31 additions & 20 deletions js/src/components/account-card/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
*/
import { __ } from '@wordpress/i18n';
import classnames from 'classnames';
import { Flex, FlexItem, FlexBlock } from '@wordpress/components';
import GridiconPhone from 'gridicons/dist/phone';
import { Icon, store as storeIcon } from '@wordpress/icons';

/**
Expand All @@ -30,7 +28,6 @@ export const APPEARANCE = {
GOOGLE: 'google',
GOOGLE_MERCHANT_CENTER: 'google_merchant_center',
GOOGLE_ADS: 'google_ads',
PHONE: 'phone',
ADDRESS: 'address',
FINAL_URL: 'final_url',
};
Expand Down Expand Up @@ -105,10 +102,6 @@ const appearanceDict = {
'google-listings-and-ads'
),
},
[ APPEARANCE.PHONE ]: {
icon: <GridiconPhone size={ 32 } />,
title: __( 'Phone number', 'google-listings-and-ads' ),
},
[ APPEARANCE.ADDRESS ]: {
icon: <Icon icon={ storeIcon } size={ 32 } />,
title: __( 'Store address', 'google-listings-and-ads' ),
Expand All @@ -122,7 +115,12 @@ const appearanceDict = {
// The `center` is the default alignment, and no need to append any additional class name.
const alignStyleName = {
center: false,
top: `gla-account-card__styled--align-top`,
top: 'gla-account-card__styled--align-top',
};

const indicatorAlignStyleName = {
...alignStyleName,
toDetail: 'gla-account-card__indicator--align-to-detail',
};

/**
Expand All @@ -142,6 +140,9 @@ const alignStyleName = {
* @param {JSX.Element} [props.indicator] Indicator of actions or status on the right side of the card.
* @param {'center'|'top'} [props.alignIcon='center'] Specify the vertical alignment of leading icon.
* @param {'center'|'top'} [props.alignIndicator='center'] Specify the vertical alignment of `indicator`.
* @param {JSX.Element} [props.detail] Detail content below the card description.
* @param {boolean} [props.expandedDetail=false] Whether to expand the detail content.
* @param {JSX.Element} [props.actions] Actions content below the card detail.
* @param {Array<JSX.Element>} [props.children] Children to be rendered if needs more content within the card.
* @param {Object} [props.restProps] Props to be forwarded to Section.Card.
*/
Expand All @@ -156,12 +157,16 @@ export default function AccountCard( {
alignIcon = 'center',
indicator,
alignIndicator = 'center',
detail,
expandedDetail = false,
actions,
children,
...restProps
} ) {
const cardClassName = classnames(
'gla-account-card',
disabled ? 'gla-account-card--is-disabled' : false,
expandedDetail ? 'gla-account-card--is-expanded-detail' : false,
className
);

Expand All @@ -172,19 +177,15 @@ export default function AccountCard( {

const indicatorClassName = classnames(
'gla-account-card__indicator',
alignStyleName[ alignIndicator ]
indicatorAlignStyleName[ alignIndicator ]
);

return (
<Section.Card className={ cardClassName } { ...restProps }>
<Section.Card.Body>
<Flex gap={ 4 }>
{ icon && (
<FlexItem className={ iconClassName }>
{ icon }
</FlexItem>
) }
<FlexBlock>
<div className="gla-account-card__body-layout">
{ icon && <div className={ iconClassName }>{ icon }</div> }
<div className="gla-account-card__subject">
{ title && (
<Subsection.Title className="gla-account-card__title">
{ title }
Expand All @@ -200,13 +201,23 @@ export default function AccountCard( {
{ helper }
</div>
) }
</FlexBlock>
</div>
{ detail && (
<div className="gla-account-card__detail">
{ detail }
</div>
) }
{ indicator && (
<FlexItem className={ indicatorClassName }>
<div className={ indicatorClassName }>
{ indicator }
</FlexItem>
</div>
) }
{ actions && (
<div className="gla-account-card__actions">
{ actions }
</div>
) }
</Flex>
</div>
</Section.Card.Body>
{ children }
</Section.Card>
Expand Down
52 changes: 48 additions & 4 deletions js/src/components/account-card/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,60 @@
opacity: 0.5;
}

&--is-expanded-detail {
.gla-account-card__indicator {
grid-area: 1/3;
}

.gla-account-card__detail {
grid-area: 2/2/auto/span 2;
}
}

&__styled {
&--align-top {
align-self: flex-start;
}
}

&__body-layout {
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
}

&__icon {
grid-area: 1/1/span 2;
margin-right: $grid-unit-20;
line-height: 0;
}

&__subject {
grid-area: 1/2;
}

&__indicator {
grid-area: 1/3/span 2;
margin-left: $grid-unit-20;

&--align-to-detail {
grid-area: 2/3;
margin-top: $grid-unit-15;
}
}

&__detail {
grid-area: 2/2;
margin-top: $grid-unit-15;
}

&__actions {
grid-area: 3/2/auto/span 2;
margin-top: $grid-unit-15;
}

&__title {
margin-bottom: $grid-unit-05;
margin: 0;
color: $black;
}

Expand All @@ -25,6 +67,7 @@
flex-direction: column;
align-items: flex-start;
gap: 1em;
margin-top: $grid-unit-05;
color: $gray-900;

> p {
Expand Down Expand Up @@ -65,11 +108,12 @@
}

@media (max-width: $break-small) {
.components-card__body > .components-flex {
&__body-layout {
display: flex;
flex-direction: column;
align-items: flex-start;
> .components-flex-block,
> .components-flex-item {

> div {
margin: $grid-unit-10;
}
}
Expand Down
64 changes: 64 additions & 0 deletions js/src/components/ads-account-select-control/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* External dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { getSetting } from '@woocommerce/settings'; // eslint-disable-line import/no-unresolved

/**
* Internal dependencies
*/
import AppSelectControl from '.~/components/app-select-control';
import useExistingGoogleAdsAccounts from '.~/hooks/useExistingGoogleAdsAccounts';
import useGoogleAdsAccount from '.~/hooks/useGoogleAdsAccount';

/**
* @param {Object} props The component props
* @return {JSX.Element} An enhanced AppSelectControl component.
*/
const AdsAccountSelectControl = ( props ) => {
const { existingAccounts } = useExistingGoogleAdsAccounts();
const { googleAdsAccount, hasGoogleAdsConnection } = useGoogleAdsAccount();

const accountIdExists = existingAccounts?.some(
( existingAccount ) => existingAccount.id === googleAdsAccount?.id
);

// If the account ID is not in the list of existing accounts, fake the select options by displaying the connected account ID only.
if ( ! accountIdExists && hasGoogleAdsConnection ) {
const domain = new URL( getSetting( 'homeUrl' ) ).host;

return (
<AppSelectControl
autoSelectFirstOption
nonInteractive
value={ googleAdsAccount.id }
options={ [
{
value: googleAdsAccount.id,
label: sprintf(
// translators: 1: account domain, 2: account ID.
__( '%1$s (%2$s)', 'google-listings-and-ads' ),
domain,
googleAdsAccount.id
),
},
] }
/>
);
}

const options = existingAccounts?.map( ( acc ) => ( {
value: acc.id,
label: `${ acc.name } (${ acc.id })`,
} ) );

return (
<AppSelectControl
options={ options }
autoSelectFirstOption
{ ...props }
/>
);
};

export default AdsAccountSelectControl;
2 changes: 2 additions & 0 deletions js/src/components/app-input-control/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@
color: $gray-700;
}

&.has-error .components-input-control__backdrop,
&--error-character-count .components-input-control .components-input-control__container .components-input-control__backdrop {
border-color: $alert-red;
box-shadow: none;
}

&.has-error .components-base-control__help,
&--error-character-count &__character-count {
color: $alert-red;
}
Expand Down
63 changes: 55 additions & 8 deletions js/src/components/app-select-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
* External dependencies
*/
import { SelectControl } from '@wordpress/components';
import { useEffect, useRef } from '@wordpress/element';
import classNames from 'classnames';
import { noop } from 'lodash';

/**
* Internal dependencies
Expand All @@ -12,18 +14,63 @@ import './index.scss';
/**
* Renders a `@wordpress/components`'s `SelectControl` with margin-bottom removed.
*
* If you provide `className` via props,
* it will be added to the container div's `className`,
* so that you can further control its style.
*
* @param {*} props
* @param {*} props The component props.
* @param {Array} [props.options=[]] Array of options for the select dropdown. Each option should be an object containing `label` and `value` properties.
* @param {string} [props.className] Additional classname to further control the style of the component.
* @param {Function} [props.onChange=noop] Callback function triggered when the selected value changes. Receives the new value as an argument.
* @param {string} [props.value] The currently selected value. This component should be used as a controlled component. A special case is that after mounting, when `autoSelectFirstOption` is true and `value` is undefined, it tries to call back `onChange` once to select the first option so that the `value` can be consistent with the `<select>` element's own value.
* @param {boolean} [props.autoSelectFirstOption=false] If true, automatically triggers the onChange callback with the first option as value when no value is provided. If only one option is available, the select control is also changed to non-interactive.
* @param {boolean} [props.nonInteractive=false] If true, the select control is changed to non-interactive.
* @param {*} [props.rest] Additional props passed to the `SelectControl` component.
*/
const AppSelectControl = ( props ) => {
const { className, ...rest } = props;
const {
options = [],
className,
onChange = noop,
value,
autoSelectFirstOption = false,
nonInteractive = false,
...rest
} = props;
const shouldAutoSelectOnceRef = useRef( autoSelectFirstOption === true );

useEffect( () => {
if ( ! shouldAutoSelectOnceRef.current ) {
return;
}

if ( value === undefined && options.length ) {
shouldAutoSelectOnceRef.current = false;
onChange( options[ 0 ].value );
}
}, [ value, options, onChange ] );

let selectProps = {
options,
value,
onChange,
...rest,
};

const isNonInteractive =
( autoSelectFirstOption && options?.length === 1 ) || nonInteractive;
if ( isNonInteractive ) {
selectProps = {
...selectProps,
readOnly: true,
suffix: ' ',
tabIndex: '-1',
};
}

return (
<div className={ classNames( 'app-select-control', className ) }>
<SelectControl { ...rest } />
<div
className={ classNames( 'app-select-control', className, {
'app-select-control--is-non-interactive': isNonInteractive,
} ) }
>
<SelectControl { ...selectProps } />
</div>
);
};
Expand Down
4 changes: 4 additions & 0 deletions js/src/components/app-select-control/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@
.components-base-control__field {
margin-bottom: 0;
}

&.app-select-control--is-non-interactive {
pointer-events: none;
}
}
Loading

0 comments on commit 4173854

Please sign in to comment.