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
65 changes: 65 additions & 0 deletions packages/block-library/src/navigation-link/block-inserter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { useSelect } from '@wordpress/data';
import {
store as blockEditorStore,
privateApis as blockEditorPrivateApis,
} from '@wordpress/block-editor';

/**
* Internal dependencies
*/
import DialogWrapper from './dialog-wrapper';
import { unlock } from '../lock-unlock';

const { PrivateQuickInserter: QuickInserter } = unlock(
blockEditorPrivateApis
);

/**
* Component for inserting blocks within the Navigation Link UI.
*
* @param {Object} props Component props.
* @param {string} props.clientId Client ID of the navigation link block.
* @param {Function} props.onBack Callback when user wants to go back.
* @param {Function} props.onBlockInsert Callback when a block is inserted.
*/
function LinkUIBlockInserter( { clientId, onBack, onBlockInsert } ) {
const { rootBlockClientId } = useSelect(
( select ) => {
const { getBlockRootClientId } = select( blockEditorStore );

return {
rootBlockClientId: getBlockRootClientId( clientId ),
};
},
[ clientId ]
);

if ( ! clientId ) {
return null;
}

return (
<DialogWrapper
className="link-ui-block-inserter"
title={ __( 'Add block' ) }
description={ __( 'Choose a block to add to your Navigation.' ) }
onBack={ onBack }
>
<QuickInserter
rootClientId={ rootBlockClientId }
clientId={ clientId }
isAppender={ false }
prioritizePatterns={ false }
selectBlockOnInsert={ ! onBlockInsert }
onSelect={ onBlockInsert ? onBlockInsert : undefined }
hasSearch={ false }
/>
</DialogWrapper>
);
}

export default LinkUIBlockInserter;
74 changes: 74 additions & 0 deletions packages/block-library/src/navigation-link/dialog-wrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* WordPress dependencies
*/
import { Button, VisuallyHidden } from '@wordpress/components';
import { __, isRTL } from '@wordpress/i18n';
import { chevronLeftSmall, chevronRightSmall } from '@wordpress/icons';
import { useInstanceId, useFocusOnMount } from '@wordpress/compose';

/**
* Shared BackButton component for consistent navigation across LinkUI sub-components.
*
* @param {Object} props Component props.
* @param {string} props.className CSS class name for the button.
* @param {Function} props.onBack Callback when user wants to go back.
*/
function BackButton( { className, onBack } ) {
return (
<Button
className={ className }
icon={ isRTL() ? chevronRightSmall : chevronLeftSmall }
onClick={ ( e ) => {
e.preventDefault();
onBack();
} }
size="small"
>
{ __( 'Back' ) }
</Button>
);
}

/**
* Shared DialogWrapper component for consistent dialog structure across LinkUI sub-components.
*
* @param {Object} props Component props.
* @param {string} props.className CSS class name for the dialog container.
* @param {string} props.title Dialog title for accessibility.
* @param {string} props.description Dialog description for accessibility.
* @param {Function} props.onBack Callback when user wants to go back.
* @param {Object} props.children Child components to render inside the dialog.
*/
function DialogWrapper( { className, title, description, onBack, children } ) {
const dialogTitleId = useInstanceId(
DialogWrapper,
'link-ui-dialog-title'
);
const dialogDescriptionId = useInstanceId(
DialogWrapper,
'link-ui-dialog-description'
);
const focusOnMountRef = useFocusOnMount( 'firstElement' );
const backButtonClassName = `${ className }__back`;

return (
<div
className={ className }
role="dialog"
aria-labelledby={ dialogTitleId }
aria-describedby={ dialogDescriptionId }
ref={ focusOnMountRef }
>
<VisuallyHidden>
<h2 id={ dialogTitleId }>{ title }</h2>
<p id={ dialogDescriptionId }>{ description }</p>
</VisuallyHidden>

<BackButton className={ backButtonClassName } onBack={ onBack } />

{ children }
</div>
);
}

export default DialogWrapper;
96 changes: 9 additions & 87 deletions packages/block-library/src/navigation-link/link-ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,8 @@ import {
VisuallyHidden,
__experimentalVStack as VStack,
} from '@wordpress/components';
import { __, isRTL } from '@wordpress/i18n';
import {
LinkControl,
store as blockEditorStore,
privateApis as blockEditorPrivateApis,
useBlockEditingMode,
} from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n';
import { LinkControl, useBlockEditingMode } from '@wordpress/block-editor';
import {
useMemo,
useState,
Expand All @@ -23,19 +18,14 @@ import {
forwardRef,
} from '@wordpress/element';
import { useResourcePermissions } from '@wordpress/core-data';
import { useSelect } from '@wordpress/data';
import { chevronLeftSmall, chevronRightSmall, plus } from '@wordpress/icons';
import { useInstanceId, useFocusOnMount } from '@wordpress/compose';
import { plus } from '@wordpress/icons';
import { useInstanceId } from '@wordpress/compose';

/**
* Internal dependencies
*/
import { unlock } from '../lock-unlock';
import { LinkUIPageCreator } from './page-creator';

const { PrivateQuickInserter: QuickInserter } = unlock(
blockEditorPrivateApis
);
import LinkUIBlockInserter from './block-inserter';

/**
* Given the Link block's type attribute, return the query params to give to
Expand Down Expand Up @@ -75,74 +65,6 @@ export function getSuggestionsQuery( type, kind ) {
}
}

function LinkUIBlockInserter( { clientId, onBack, onBlockInsert } ) {
const { rootBlockClientId } = useSelect(
( select ) => {
const { getBlockRootClientId } = select( blockEditorStore );

return {
rootBlockClientId: getBlockRootClientId( clientId ),
};
},
[ clientId ]
);

const focusOnMountRef = useFocusOnMount( 'firstElement' );

const dialogTitleId = useInstanceId(
LinkControl,
`link-ui-block-inserter__title`
);
const dialogDescriptionId = useInstanceId(
LinkControl,
`link-ui-block-inserter__description`
);

if ( ! clientId ) {
return null;
}

return (
<div
className="link-ui-block-inserter"
role="dialog"
aria-labelledby={ dialogTitleId }
aria-describedby={ dialogDescriptionId }
ref={ focusOnMountRef }
>
<VisuallyHidden>
<h2 id={ dialogTitleId }>{ __( 'Add block' ) }</h2>

<p id={ dialogDescriptionId }>
{ __( 'Choose a block to add to your Navigation.' ) }
</p>
</VisuallyHidden>

<Button
className="link-ui-block-inserter__back"
icon={ isRTL() ? chevronRightSmall : chevronLeftSmall }
onClick={ ( e ) => {
e.preventDefault();
onBack();
} }
size="small"
>
{ __( 'Back' ) }
</Button>

<QuickInserter
rootClientId={ rootBlockClientId }
clientId={ clientId }
isAppender={ false }
prioritizePatterns={ false }
selectBlockOnInsert={ ! onBlockInsert }
onSelect={ onBlockInsert ? onBlockInsert : undefined }
hasSearch={ false }
/>
</div>
);
}

function UnforwardedLinkUI( props, ref ) {
const { label, url, opensInNewTab, type, kind } = props.link;
const postType = type || 'page';
Expand All @@ -151,7 +73,7 @@ function UnforwardedLinkUI( props, ref ) {
const [ addingPage, setAddingPage ] = useState( false );
const [ focusAddBlockButton, setFocusAddBlockButton ] = useState( false );
const [ focusAddPageButton, setFocusAddPageButton ] = useState( false );
const permissions = useResourcePermissions( {
const { canCreate: canCreatePage } = useResourcePermissions( {
kind: 'postType',
name: postType,
} );
Expand All @@ -176,11 +98,11 @@ function UnforwardedLinkUI( props, ref ) {

const dialogTitleId = useInstanceId(
LinkUI,
`link-ui-link-control__title`
'link-ui-link-control__title'
);
const dialogDescriptionId = useInstanceId(
LinkUI,
`link-ui-link-control__description`
'link-ui-link-control__description'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What is the difference between backticks and single quotes in this context?

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.

Just an error when I remove some interpolation from this string. Single quotes are preferred. Suprised the linter doesn't pick up on it!

);

const blockEditingMode = useBlockEditingMode();
Expand Down Expand Up @@ -233,8 +155,8 @@ function UnforwardedLinkUI( props, ref ) {
setAddingPage( true );
setFocusAddPageButton( false );
} }
canCreatePage={ permissions.canCreate }
blockEditingMode={ blockEditingMode }
canCreatePage={ canCreatePage }
/>
)
}
Expand Down
33 changes: 13 additions & 20 deletions packages/block-library/src/navigation-link/page-creator.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ import {
__experimentalVStack as VStack,
__experimentalHStack as HStack,
} from '@wordpress/components';
import { __, isRTL } from '@wordpress/i18n';
import { __ } from '@wordpress/i18n';
import { useSelect, useDispatch } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { decodeEntities } from '@wordpress/html-entities';
import { useState } from '@wordpress/element';
import { chevronLeftSmall, chevronRightSmall } from '@wordpress/icons';
import { useFocusOnMount } from '@wordpress/compose';

/**
* Internal dependencies
*/
import DialogWrapper from './dialog-wrapper';

/**
* Component for creating new pages within the Navigation Link UI.
Expand All @@ -35,9 +38,6 @@ export function LinkUIPageCreator( {
const [ title, setTitle ] = useState( initialTitle );
const [ shouldPublish, setShouldPublish ] = useState( false );

// Focus the first element when the component mounts
const focusOnMountRef = useFocusOnMount( 'firstElement' );

// Check if the title is valid for submission
const isTitleValid = title.trim().length > 0;

Expand Down Expand Up @@ -95,19 +95,12 @@ export function LinkUIPageCreator( {
const isSubmitDisabled = isSaving || ! isTitleValid;

return (
<div className="link-ui-page-creator" ref={ focusOnMountRef }>
<Button
className="link-ui-page-creator__back"
icon={ isRTL() ? chevronRightSmall : chevronLeftSmall }
onClick={ ( e ) => {
e.preventDefault();
onBack();
} }
size="small"
>
{ __( 'Back' ) }
</Button>

<DialogWrapper
className="link-ui-page-creator"
title={ __( 'Create page' ) }
description={ __( 'Create a new page to add to your Navigation.' ) }
onBack={ onBack }
>
<VStack className="link-ui-page-creator__inner" spacing={ 4 }>
<form onSubmit={ createPage }>
<VStack spacing={ 4 }>
Expand Down Expand Up @@ -159,6 +152,6 @@ export function LinkUIPageCreator( {
</VStack>
</form>
</VStack>
</div>
</DialogWrapper>
);
}
Loading