Skip to content
Merged
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
Expand Up @@ -27,6 +27,11 @@ export type ConnectionIconProps = {
profilePicture: string;
disabled?: boolean;
className?: string;
// Visual size of the avatar + overlapping service icon. `small` (default)
// is 28×28 avatar + 14×14 service icon for compact rows / dataviews;
// `medium` is 32×32 avatar + 16×16 service icon for roomier rows such as
// the chassis Overview "Connected accounts" list.
size?: 'small' | 'medium';
};

/**
Expand All @@ -40,6 +45,7 @@ export function ConnectionIcon( {
profilePicture,
disabled,
className,
size = 'small',
}: ConnectionIconProps ) {
const [ imageErrorFor, setImageErrorFor ] = useState( null );

Expand All @@ -55,6 +61,7 @@ export function ConnectionIcon( {
<div
className={ clsx(
styles.wrapper,
styles[ size ],
{
[ styles.disabled ]: disabled,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,54 @@
.wrapper {
position: relative;
width: 28px;
height: 28px;
align-items: center;
block-size: 28px;
display: flex;
inline-size: 28px;
justify-content: center;
align-items: center;
position: relative;

&.disabled {
opacity: 0.4;
}

img,
.avatar {
background-color: var(--wpds-color-bg-surface-neutral-strong, #fff);
block-size: 24px;
border: 2px solid var(--wpds-color-bg-surface-neutral-strong, #fff);
border-radius: 50%;
border: 2px solid #fff;
background-color: #fff;
width: 24px;
height: 24px;
inline-size: 24px;
}

.social-icon {
position: absolute;
background-color: var(--wpds-color-bg-surface-neutral-strong, #fff);
block-size: 14px;
border-radius: var(--wpds-border-radius-sm, 2px);
inline-size: 14px;
inset-block-end: 0;
inset-inline-end: 0;
width: 14px;
height: 14px;
border-radius: 2px;
background-color: #fff;
position: absolute;
}

// Roomier variant for the chassis Overview "Connected accounts" list.
// Default (`size="small"`) is the 28/24/14 set above so any consumer
// that drops the prop — or that lands in a bundle context where the
// CSS-modules-scoped `.small` class doesn't reach the element (e.g.
// the block editor's `<Button icon={...}>` slot) — still renders at
// a sensible avatar size.
&.medium {
block-size: 32px;
inline-size: 32px;

img,
.avatar {
block-size: 32px;
border: 0;
inline-size: 32px;
}

.social-icon {
block-size: 16px;
inline-size: 16px;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { useSelect } from '@wordpress/data';
import { useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { chevronDown, info } from '@wordpress/icons';
import { Collapsible, Icon, IconButton, Stack, Text } from '@wordpress/ui';
import { store as socialStore } from '../../social-store';
import ConnectionIcon from '../connection-icon';
import { XNotice } from '../services/x-notice';
import { ConnectionName } from './connection-name';
import { ConnectionStatus, ConnectionStatusProps } from './connection-status';
import { ConnectionTemplateEditor } from './connection-template';
import { Disconnect } from './disconnect';
import { MarkAsShared } from './mark-as-shared';
import styles from './style-modern.module.scss';
import type { SyntheticEvent } from 'react';

type ConnectionInfoProps = ConnectionStatusProps & {
canMarkAsShared: boolean;
};

const stopPropagation = ( event: SyntheticEvent ) => event.stopPropagation();

/**
* Connection info component
*
* @param {ConnectionInfoProps} props - component props
*
* @return React element
*/
export function ModernConnectionInfo( {
connection,
service,
canMarkAsShared,
}: ConnectionInfoProps ) {
const [ isPanelOpen, setIsPanelOpen ] = useState( false );

const { canManageConnection, isUnsupported } = useSelect(
select => {
const { canUserManageConnection, getServicesBy } = select( socialStore );

return {
canManageConnection: canUserManageConnection( connection ),
isUnsupported: getServicesBy( 'status', 'unsupported' ).some(
( { id } ) => id === connection.service_name
),
};
},
[ connection ]
);

const hasStatus =
connection.status === 'broken' || connection.status === 'must_reauth' || isUnsupported;

const markAsSharedHelp = __(
'If enabled, the connection will be available to all administrators, editors, and authors.',
'jetpack-publicize-pkg'
);

return (
<Collapsible.Root open={ isPanelOpen } onOpenChange={ setIsPanelOpen }>
<Collapsible.Trigger
className={ styles[ 'connection-row' ] }
nativeButton={ false }
render={ <div /> }
>
<ConnectionIcon
serviceName={ connection.service_name }
label={ connection.display_name }
profilePicture={ connection.profile_picture }
size="medium"
/>
<Stack direction="column" gap="xs" className={ styles[ 'connection-name-wrapper' ] }>
{ /*
* The profile-name link lives inside the row, which doubles as
* the Collapsible.Trigger. Without stopping propagation a click
* on the link would also toggle the disclosure (and an anchor
* inside role="button" is invalid nesting). Mirror the
* connection-status-wrap below so the link opens the profile
* without toggling the panel.
*/ }
<Text
variant="body-lg"
className={ styles[ 'connection-item-name' ] }
onClick={ stopPropagation }
onKeyDown={ stopPropagation }
role="presentation"
>
<ConnectionName connection={ connection } tone="neutral" />
</Text>
{ hasStatus ? (
<Stack
direction="column"
gap="xs"
onClick={ stopPropagation }
onKeyDown={ stopPropagation }
role="presentation"
>
<ConnectionStatus connection={ connection } service={ service } />
</Stack>
) : (
<Text variant="body-md" className={ styles[ 'connection-network' ] }>
{ service?.label }
</Text>
) }
</Stack>
<Icon className={ styles.chevron } icon={ chevronDown } />
</Collapsible.Trigger>
<Collapsible.Panel className={ styles[ 'connection-panel' ] }>
<div className={ styles[ 'connection-panel-inner' ] }>
{ canMarkAsShared && (
<Stack
direction="row"
align="center"
gap="sm"
className={ styles[ 'mark-shared-wrap' ] }
>
<MarkAsShared connection={ connection } />
<IconButton
variant="minimal"
tone="neutral"
size="small"
label={ markAsSharedHelp }
icon={ info }
/>
</Stack>
) }
<div className={ styles[ 'connection-template-wrap' ] }>
<ConnectionTemplateEditor connection={ connection } />
</div>
{ canManageConnection ? (
<Disconnect connection={ connection } size="compact" tone="neutral" />
) : (
<Text className={ styles.description }>
{ __( 'This connection is added by a site administrator.', 'jetpack-publicize-pkg' ) }
</Text>
) }
{ service?.id === 'x' && <XNotice /> }
</div>
</Collapsible.Panel>
</Collapsible.Root>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,20 @@ import styles from './style.module.scss';

type ConnectionNameProps = {
connection: Connection;
/** Link tone. Defaults to the WPDS link default; the modernized chassis passes "neutral". */
tone?: 'neutral';
};

/**
* Connection name component
*
* @param {ConnectionNameProps} props - component props
* @param {ConnectionNameProps} props - component props
* @param {Connection} props.connection - the connection to render
* @param {string} props.tone - optional WPDS link tone
*
* @return {import('react').ReactNode} - React element
*/
export function ConnectionName( { connection }: ConnectionNameProps ) {
export function ConnectionName( { connection, tone }: ConnectionNameProps ) {
const isUpdating = useSelect(
select => {
return select( socialStore ).getUpdatingConnections().includes( connection.connection_id );
Expand All @@ -30,7 +34,12 @@ export function ConnectionName( { connection }: ConnectionNameProps ) {
{ ! connection.profile_link ? (
<span className={ styles[ 'profile-link' ] }>{ connection.display_name }</span>
) : (
<Link openInNewTab className={ styles[ 'profile-link' ] } href={ connection.profile_link }>
<Link
openInNewTab
tone={ tone }
className={ styles[ 'profile-link' ] }
href={ connection.profile_link }
>
{ connection.display_name }
</Link>
) }
Expand Down
Loading
Loading