Skip to content
Open
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
63 changes: 63 additions & 0 deletions apps/ui/src/components/auth-actions/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { __ } from '@wordpress/i18n';
import { Button } from '@wordpress/ui';
import { clsx } from 'clsx';
import { useLogin } from '@/data/queries/use-auth-user';
import { useOffline } from '@/hooks/use-offline';
import styles from './style.module.css';

interface AuthActionsProps {
className?: string;
}

// Shared by the welcome, tour and connect screens so the sign-in prompts
// can't drift apart.
export function AuthActions( { className }: AuthActionsProps ) {
const isOffline = useOffline();
const login = useLogin();
const signup = useLogin( { signup: true } );
const authError = login.error ?? signup.error;
const offlineMessage = __( "You're currently offline." );
const arrow = (
<span aria-hidden className={ styles.arrow }>
{ '↗' }
</span>
);

return (
<div className={ clsx( styles.root, className ) }>
<div className={ styles.actions }>
<Button
type="button"
variant="minimal"
tone="neutral"
disabled={ isOffline || login.isPending }
title={ isOffline ? offlineMessage : undefined }
loading={ signup.isPending }
onClick={ () => signup.mutate() }
>
{ __( 'Sign up' ) }
{ arrow }
</Button>
<Button
type="button"
variant="solid"
tone="brand"
disabled={ isOffline || signup.isPending }
title={ isOffline ? offlineMessage : undefined }
loading={ login.isPending }
onClick={ () => login.mutate() }
>
{ __( 'Log in with WordPress.com' ) }
{ arrow }
</Button>
</div>
{ authError && (
<p role="alert" className={ styles.error }>
{ authError instanceof Error
? authError.message
: __( 'Authentication failed. Please try again.' ) }
</p>
) }
</div>
);
}
41 changes: 41 additions & 0 deletions apps/ui/src/components/auth-actions/style.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
.root {
display: flex;
flex-direction: column;
gap: 12px;
}

.actions {
display: flex;
align-items: center;
gap: 16px;
}

.actions > * {
flex-shrink: 0;
white-space: nowrap;
}

.arrow {
margin-inline-start: 4px;
display: inline-block;
}

[dir='rtl'] .arrow {
transform: scaleX(-1);
}

.error {
margin: 0;
padding: 10px 12px;
border: var(--wpds-border-width-xs) solid var(--wpds-color-stroke-surface-error);
border-radius: 6px;
background: var(--wpds-color-bg-surface-error-weak);
color: var(--wpds-color-fg-content-error);
}

@media (max-width: 600px) {
.actions {
align-items: stretch;
flex-direction: column;
}
}
27 changes: 27 additions & 0 deletions apps/ui/src/components/feature-list/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { check } from '@wordpress/icons';
import { Icon } from '@wordpress/ui';
import { clsx } from 'clsx';
import styles from './style.module.css';

interface FeatureListProps {
features: { title: string; body: string }[];
className?: string;
}

// The onboarding flow's checkmark feature columns, shared by the welcome
// screen and the tour steps so the two can't drift apart.
export function FeatureList( { features, className }: FeatureListProps ) {
return (
<ul className={ clsx( styles.features, className ) }>
{ features.map( ( { title, body } ) => (
<li key={ title }>
<h3 className={ styles.featureTitle }>
<Icon icon={ check } size={ 16 } />
{ title }
</h3>
<p className={ styles.featureBody }>{ body }</p>
</li>
) ) }
</ul>
);
}
51 changes: 51 additions & 0 deletions apps/ui/src/components/feature-list/style.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* Three left-aligned checkmark feature columns sharing one quiet bordered
container. Wider than the content column to make use of the horizontal
space. */
.features {
list-style: none;
margin: 16px 0 0;
padding: 16px 20px 20px;
border: var(--wpds-border-width-xs) solid var(--wpds-color-stroke-surface-neutral);
border-radius: 8px;
display: flex;
gap: 24px;
width: min(880px, calc(100vw - 100px));
text-align: start;
}

.features li {
flex: 1 1 0;
min-width: 0;
}

.featureTitle {
display: flex;
/* Titles wrap to two lines in narrow columns, so the check sits on the
first line rather than centred against the whole block. */
align-items: flex-start;
gap: 6px;
font-size: 13px;
/* Must be set: the global `h3` rule's line-height is sized for `lg` text
and leaves a huge gap between wrapped lines at 13px. */
line-height: 1.4;
font-weight: var(--wpds-typography-font-weight-medium);
margin: 0 0 2px;
text-wrap: balance;
}

.featureTitle svg {
fill: var(--wpds-color-fg-interactive-brand);
flex-shrink: 0;
/* Optically centre the 16px glyph on the first line of text. */
margin-top: calc((1.4em - 16px) / 2);
}

.featureBody {
color: var(--wpds-color-fg-content-neutral-weak);
font-size: 12px;
line-height: 1.4;
margin: 0;
/* Align with the title text, past the checkmark. */
margin-inline-start: 22px;
text-wrap: pretty;
}
Loading