Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Activation screen: migrate the buttons, form controls, error notice, and support links to @wordpress/ui primitives.
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import jetpackAnalytics from '@automattic/jetpack-analytics';
import { JetpackLogo } from '@automattic/jetpack-components';
import { Button, TextControl, SelectControl, Spinner } from '@wordpress/components';
import { createInterpolateElement } from '@wordpress/element';
import { sprintf, __ } from '@wordpress/i18n';
import { Button, InputControl, SelectControl, Stack, Text } from '@wordpress/ui';
import PropTypes from 'prop-types';
import { useCallback, useEffect, useMemo, useState } from 'react';
import ActivationScreenError from '../activation-screen-error';
import { LICENSE_ERRORS } from '../activation-screen-error/constants';
import './style.scss';

// Sentinel value for the "add a license key manually" option so the SelectControl
// shows that option (rather than the empty-value placeholder) while the manual
// text input is displayed.
const MANUAL_ENTRY = '__manual_entry__';

/**
* The Activation Screen Controls component.
*
Expand All @@ -23,13 +28,11 @@ const ManualLicenseKeyInput = props => {
const { className, disabled, onChange, value } = props;

return (
<TextControl
__nextHasNoMarginBottom={ true }
__next40pxDefaultSize
<InputControl
className={ className }
label={ __( 'License key', 'jetpack-licensing' ) }
value={ value }
onChange={ onChange }
onValueChange={ onChange }
disabled={ disabled }
/>
);
Expand Down Expand Up @@ -75,7 +78,7 @@ const SelectableLicenseKeyInput = props => {
} ),
{
label: __( 'I want to add a license key manually', 'jetpack-licensing' ),
value: '',
value: MANUAL_ENTRY,
},
];
}, [ availableLicenses, isFetching ] );
Expand All @@ -89,34 +92,33 @@ const SelectableLicenseKeyInput = props => {
}, [ options ] );

const onSelectionChange = useCallback(
val => {
item => {
const val = item?.value ?? '';
setSelectedOption( val );
onChange( val );
// Manual entry has no key yet — clear the stored license so the button
// stays disabled until the user types one.
onChange( val === MANUAL_ENTRY ? '' : val );
},
[ onChange ]
);

return (
<>
<SelectControl
__nextHasNoMarginBottom={ true }
__next40pxDefaultSize
className={ className }
disabled={ disabled }
label={ __( 'Select a license key', 'jetpack-licensing' ) }
value={ selectedOption }
options={ options }
onChange={ onSelectionChange }
value={ options.find( option => option.value === selectedOption ) ?? null }
items={ options }
onValueChange={ onSelectionChange }
/>

{ ! isFetching && ! selectedOption && (
<TextControl
__nextHasNoMarginBottom={ true }
__next40pxDefaultSize
{ ! isFetching && selectedOption === MANUAL_ENTRY && (
<InputControl
className={ className }
label={ __( 'Input a license key', 'jetpack-licensing' ) }
value={ value }
onChange={ onChange }
onValueChange={ onChange }
disabled={ disabled }
/>
) }
Expand Down Expand Up @@ -173,52 +175,65 @@ const ActivationScreenControls = props => {
const hasAvailableLicenseKey = availableLicenses && availableLicenses.length;

return (
<div className="jp-license-activation-screen-controls">
<div className="jp-license-activation-screen-controls--content">
<JetpackLogo showText={ false } height={ 48 } />
<h1>{ __( 'Add a license key', 'jetpack-licensing' ) }</h1>
<p>
{ createInterpolateElement(
__(
'<strong>Purchased a plan?</strong><br />Check your email for your license key and paste it below.',
'jetpack-licensing'
),
{
strong: <strong />,
br: <br />,
}
<Stack
className="jp-license-activation-screen-controls"
direction="column"
justify="space-between"
gap="xl"
>
<Stack direction="column" gap="2xl">
<Stack direction="column" gap="2xl" align="flex-start">
<JetpackLogo showText={ false } height={ 48 } />
<Text render={ <h1 /> } variant="heading-2xl">
{ __( 'Add a license key', 'jetpack-licensing' ) }
</Text>
</Stack>
<Stack direction="column" gap="lg">
<Text variant="body-lg">
{ createInterpolateElement(
__(
'<strong>Purchased a plan?</strong><br />Check your email for your license key and paste it below.',
'jetpack-licensing'
),
{
strong: <strong />,
br: <br />,
}
) }
</Text>
{ fetchingAvailableLicenses || hasAvailableLicenseKey ? (
<SelectableLicenseKeyInput
className={ className }
disabled={ fetchingAvailableLicenses || isActivating }
onChange={ onLicenseChange }
availableLicenses={ fetchingAvailableLicenses ? null : availableLicenses }
value={ license }
/>
) : (
<ManualLicenseKeyInput
className={ className }
disabled={ isActivating }
onChange={ onLicenseChange }
value={ license }
/>
) }
</p>
{ fetchingAvailableLicenses || hasAvailableLicenseKey ? (
<SelectableLicenseKeyInput
className={ className }
disabled={ fetchingAvailableLicenses || isActivating }
onChange={ onLicenseChange }
availableLicenses={ fetchingAvailableLicenses ? null : availableLicenses }
value={ license }
/>
) : (
<ManualLicenseKeyInput
className={ className }
disabled={ isActivating }
onChange={ onLicenseChange }
value={ license }
/>
) }
{ licenseError && (
<ActivationScreenError licenseError={ licenseError } errorType={ errorType } />
) }
</div>
{ licenseError && (
<ActivationScreenError licenseError={ licenseError } errorType={ errorType } />
) }
</Stack>
</Stack>
<div>
<Button
className="jp-license-activation-screen-controls--button"
onClick={ activateLicense }
disabled={ ! license }
disabled={ ! license || isActivating }
loading={ isActivating }
loadingAnnouncement={ __( 'Activating license…', 'jetpack-licensing' ) }
>
{ isActivating ? <Spinner /> : __( 'Activate', 'jetpack-licensing' ) }
{ __( 'Activate', 'jetpack-licensing' ) }
</Button>
</div>
</div>
</Stack>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,127 +3,22 @@
.jp-license-activation-screen-controls {
background: var(--jp-white);
padding: 32px;
display: flex;
flex-direction: column;
justify-content: space-between;

h1 {
// typography-exception
font-size: 44px;
font-weight: 700;

// This is inherited in the Jetpack plugin from the top-level
// Jetpack page. Doesn't carry over to My Jetpack.
line-height: 1.4;

// Exception here due to being overridden by Text component
// styles when used in My Jetpack.
margin: 0.67em 0;
}

p {
font-size: var(--font-body);

// Exception here due to being overridden by Text component
// styles when used in My Jetpack.
margin: 1em 0;
}

label {
font-weight: 600;
font-size: var(--font-body);
}

@media screen and (min-width: 780px) {
padding: 64px;
}


.jp-license-activation-screen-controls--license-field,
.jp-license-activation-screen-controls--license-field-with-error,
.jp-license-activation-screen-controls--license-field-with-success {
max-width: 500px;
margin-top: 10px;

.components-input-control__label.components-input-control__label.components-input-control__label {
font-weight: 600;
font-size: var(--font-body);
}

input.components-text-control__input,
select.components-select-control__input {
border-radius: var(--jp-border-radius);
font-size: 18px;
line-height: 24px;
margin: 0;
min-height: 48px;
}
}

.jp-license-activation-screen-controls--license-field {

input.components-text-control__input {
border: 1px solid var(--jp-gray-40);
}
}

.jp-license-activation-screen-controls--license-field-with-error {

input.components-text-control__input,
select.components-select-control__input {
border: 1px solid var(--jp-red);
}
}

.jp-license-activation-screen-controls--license-field-with-success {

input.components-text-control__input,
select.components-select-control__input {
border: 1px solid var(--jp-green);
}
}

.jp-license-activation-screen-controls--button,
.jp-license-activation-screen-controls--button:active {
background-color: var(--jp-black);
border-radius: 4px;
color: var(--jp-white);
display: flex;
font-size: 16px;
font-size: var(--font-body);
font-style: normal;
font-weight: 600;
justify-content: center;
line-height: 24px;
margin-top: 24px;
min-height: 48px;
min-width: 158px;
padding: 13.5px 45px 13.5px 45px;
width: 100%;

@media screen and (min-width: 480px) {
width: auto;
}

&.components-button:hover {
background-color: var(--jp-black-80);
color: var(--jp-white);
}

&:focus {
background-color: var(--jp-black-80);
border: 1px solid var(--jp-white);
color: var(--jp-white);
}

&[disabled],
&:disabled {
background-color: var(--jp-gray);
color: var(--jp-gray-20);
}

.components-spinner {
width: 100%;
}
.jp-license-activation-screen-controls--button {
font-weight: var(--wpds-typography-font-weight-medium);
font-size: var(--wpds-typography-font-size-lg);
width: auto;
padding-inline: var(--wpds-dimension-padding-3xl);
padding-block: var(--wpds-dimension-padding-lg);
Comment on lines +18 to +22

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need these style overrides? Ideally, we'd use the design system as-is without overrides, which are unwieldy to maintain.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks quite off without them, there is not variant for size. Good thing is they are all under a single class, we can easily remove them. Let me get screenshots before calling for some design advise.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yah it might just need a design change/decision or keep as custom but good to make that decision intentionally rather than just leave be because that's how it was before. 🙂

}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import jetpackAnalytics from '@automattic/jetpack-analytics';
import { Icon, cautionFilled as warning, check } from '@wordpress/icons';
import { Notice } from '@wordpress/ui';
import { useEffect } from 'react';
import { LICENSE_ERRORS } from './constants';
import { useGetErrorContent } from './use-get-error-content';
Expand Down Expand Up @@ -31,21 +31,20 @@ const ActivationScreenError: FC< Props > = ( { licenseError, errorType } ) => {
return null;
}

const { ACTIVE_ON_SAME_SITE } = LICENSE_ERRORS;
const isLicenseAlreadyAttached = ACTIVE_ON_SAME_SITE === errorType;

const errorMessageClass = isLicenseAlreadyAttached
? 'activation-screen-error__message--success'
: 'activation-screen-error__message--error';
const isLicenseAlreadyAttached = LICENSE_ERRORS.ACTIVE_ON_SAME_SITE === errorType;

return (
<>
<div className={ `activation-screen-error__message ${ errorMessageClass }` }>
<Icon icon={ isLicenseAlreadyAttached ? check : warning } size={ 20 } />
<span>{ errorMessage }</span>
</div>
{ errorInfo && <div className="activation-screen-error__info">{ errorInfo }</div> }
</>
<Notice.Root
className="activation-screen-error"
intent={ isLicenseAlreadyAttached ? 'success' : 'error' }
>
<Notice.Title>{ errorMessage }</Notice.Title>
{ errorInfo && (
// errorInfo is rich block content (<p>/<ol>/links), so render the
// Description as a <div> rather than its default inline <span>.
<Notice.Description render={ <div /> }>{ errorInfo }</Notice.Description>
) }
</Notice.Root>
);
};

Expand Down
Loading
Loading