diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 158432cca5c48..10ceb797e28c0 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -418,7 +418,7 @@ An organized collection of items displayed in a specific order. ([Source](https: - **Supports:** __unstablePasteTextInline, anchor, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), typography (fontSize, lineHeight), ~~html~~ - **Attributes:** ordered, placeholder, reversed, start, type, values -## List item +## List Item An individual item within a list. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/list-item)) @@ -534,7 +534,7 @@ Start with the basic building block of all narrative. ([Source](https://github.c - **Supports:** __unstablePasteTextInline, anchor, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), splitting, typography (fontSize, lineHeight), ~~className~~ - **Attributes:** align, content, direction, dropCap, placeholder -## Pattern placeholder +## Pattern Placeholder Show a block pattern. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/pattern)) @@ -672,7 +672,7 @@ Post terms. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages - **Supports:** color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), typography (fontSize, lineHeight), ~~html~~ - **Attributes:** prefix, separator, suffix, term, textAlign -## Time To Read +## Time to Read Show minutes required to finish reading the post. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/post-time-to-read)) @@ -718,7 +718,7 @@ An advanced block that allows displaying post types based on different query par - **Supports:** align (full, wide), interactivity, layout, ~~html~~ - **Attributes:** enhancedPagination, namespace, query, queryId, tagName -## No results +## No Results Contains the block elements used to render content when no query results are found. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/query-no-results)) diff --git a/packages/block-editor/src/hooks/custom-class-name.js b/packages/block-editor/src/hooks/custom-class-name.js index 91ef07506fd95..a7f8cab8c8223 100644 --- a/packages/block-editor/src/hooks/custom-class-name.js +++ b/packages/block-editor/src/hooks/custom-class-name.js @@ -123,7 +123,8 @@ export function addTransforms( result, source, index, results ) { // if source N does not exists we do nothing. if ( source[ index ] ) { const originClassName = source[ index ]?.attributes.className; - if ( originClassName ) { + // Avoid overriding classes if the transformed block already includes them. + if ( originClassName && result.attributes.className === undefined ) { return { ...result, attributes: { diff --git a/packages/block-library/src/archives/edit.js b/packages/block-library/src/archives/edit.js index d4f25da8507f3..81c5a735acf48 100644 --- a/packages/block-library/src/archives/edit.js +++ b/packages/block-library/src/archives/edit.js @@ -61,7 +61,7 @@ export default function ArchivesEdit( { attributes, setAttributes } ) { showLabel } + hasValue={ () => ! showLabel } onDeselect={ () => setAttributes( { showLabel: false } ) } @@ -102,7 +102,7 @@ export default function ArchivesEdit( { attributes, setAttributes } ) { !! type } + hasValue={ () => type !== 'monthly' } onDeselect={ () => setAttributes( { type: 'monthly' } ) } diff --git a/packages/block-library/src/categories/edit.js b/packages/block-library/src/categories/edit.js index 8bd2769b0ec71..d8ea455b9ba1f 100644 --- a/packages/block-library/src/categories/edit.js +++ b/packages/block-library/src/categories/edit.js @@ -7,12 +7,13 @@ import clsx from 'clsx'; * WordPress dependencies */ import { - PanelBody, Placeholder, SelectControl, Spinner, ToggleControl, VisuallyHidden, + __experimentalToolsPanel as ToolsPanel, + __experimentalToolsPanelItem as ToolsPanelItem, } from '@wordpress/components'; import { useInstanceId } from '@wordpress/compose'; import { @@ -25,6 +26,11 @@ import { __, sprintf } from '@wordpress/i18n'; import { pin } from '@wordpress/icons'; import { useEntityRecords } from '@wordpress/core-data'; +/** + * Internal dependencies + */ +import { useToolsPanelDropdownMenuProps } from '../utils/hooks'; + export default function CategoriesEdit( { attributes: { displayAsDropdown, @@ -180,72 +186,154 @@ export default function CategoriesEdit( { const blockProps = useBlockProps( { className: classes, } ); + const dropdownMenuProps = useToolsPanelDropdownMenuProps(); return ( - + { + setAttributes( { + taxonomy: 'category', + displayAsDropdown: false, + showHierarchy: false, + showPostCounts: false, + showOnlyTopLevel: false, + showEmpty: false, + showLabel: true, + } ); + } } + dropdownMenuProps={ dropdownMenuProps } + > { Array.isArray( taxonomies ) && ( - { + return taxonomySlug !== 'category'; + } } label={ __( 'Taxonomy' ) } - options={ taxonomies.map( ( t ) => ( { - label: t.name, - value: t.slug, - } ) ) } - value={ taxonomySlug } - onChange={ ( selectedTaxonomy ) => - setAttributes( { - taxonomy: selectedTaxonomy, - } ) - } - /> + onDeselect={ () => { + setAttributes( { taxonomy: 'category' } ); + } } + isShownByDefault + > + ( { + label: t.name, + value: t.slug, + } ) ) } + value={ taxonomySlug } + onChange={ ( selectedTaxonomy ) => + setAttributes( { + taxonomy: selectedTaxonomy, + } ) + } + /> + ) } - !! displayAsDropdown } label={ __( 'Display as dropdown' ) } - checked={ displayAsDropdown } - onChange={ toggleAttribute( 'displayAsDropdown' ) } - /> - { displayAsDropdown && ( + onDeselect={ () => + setAttributes( { displayAsDropdown: false } ) + } + isShownByDefault + > + + { displayAsDropdown && ( + ! showLabel } + label={ __( 'Show label' ) } + onDeselect={ () => + setAttributes( { showLabel: true } ) + } + isShownByDefault + > + + ) } - !! showPostCounts } label={ __( 'Show post counts' ) } - checked={ showPostCounts } - onChange={ toggleAttribute( 'showPostCounts' ) } - /> - { isHierarchicalTaxonomy && ( + onDeselect={ () => + setAttributes( { showPostCounts: false } ) + } + isShownByDefault + > + + { isHierarchicalTaxonomy && ( + !! showOnlyTopLevel } + label={ __( 'Show only top level terms' ) } + onDeselect={ () => + setAttributes( { showOnlyTopLevel: false } ) + } + isShownByDefault + > + + ) } - !! showEmpty } label={ __( 'Show empty terms' ) } - checked={ showEmpty } - onChange={ toggleAttribute( 'showEmpty' ) } - /> - { isHierarchicalTaxonomy && ! showOnlyTopLevel && ( + onDeselect={ () => + setAttributes( { showEmpty: false } ) + } + isShownByDefault + > + + { isHierarchicalTaxonomy && ! showOnlyTopLevel && ( + !! showHierarchy } + label={ __( 'Show hierarchy' ) } + onDeselect={ () => + setAttributes( { showHierarchy: false } ) + } + isShownByDefault + > + + ) } - + { isResolving && ( diff --git a/packages/block-library/src/embed/transforms.js b/packages/block-library/src/embed/transforms.js index cf29511fde7af..82c46da1fa7f9 100644 --- a/packages/block-library/src/embed/transforms.js +++ b/packages/block-library/src/embed/transforms.js @@ -7,6 +7,7 @@ import { createBlock } from '@wordpress/blocks'; * Internal dependencies */ import metadata from './block.json'; +import { removeAspectRatioClasses } from './util'; const { name: EMBED_BLOCK } = metadata; @@ -33,13 +34,14 @@ const transforms = { type: 'block', blocks: [ 'core/paragraph' ], isMatch: ( { url } ) => !! url, - transform: ( { url, caption } ) => { + transform: ( { url, caption, className } ) => { let value = `${ url }`; if ( caption?.trim() ) { value += `
${ caption }`; } return createBlock( 'core/paragraph', { content: value, + className: removeAspectRatioClasses( className ), } ); }, }, diff --git a/packages/block-library/src/form/variations.js b/packages/block-library/src/form/variations.js index cf12cef2ea38f..5eab3e247f88b 100644 --- a/packages/block-library/src/form/variations.js +++ b/packages/block-library/src/form/variations.js @@ -61,7 +61,7 @@ const variations = [ }, { name: 'wp-privacy-form', - title: __( 'Experimental privacy request form' ), + title: __( 'Experimental Privacy Request Form' ), keywords: [ 'GDPR' ], description: __( 'A form to request data exports and/or deletion.' ), attributes: { diff --git a/packages/block-library/src/latest-comments/edit.js b/packages/block-library/src/latest-comments/edit.js index 85e66cf2e9dc6..432cd389efacc 100644 --- a/packages/block-library/src/latest-comments/edit.js +++ b/packages/block-library/src/latest-comments/edit.js @@ -4,13 +4,19 @@ import { InspectorControls, useBlockProps } from '@wordpress/block-editor'; import { Disabled, - PanelBody, RangeControl, ToggleControl, + __experimentalToolsPanel as ToolsPanel, + __experimentalToolsPanelItem as ToolsPanelItem, } from '@wordpress/components'; import ServerSideRender from '@wordpress/server-side-render'; import { __ } from '@wordpress/i18n'; +/** + * Internal dependencies + */ +import { useToolsPanelDropdownMenuProps } from '../utils/hooks'; + /** * Minimum number of comments a user can show using this block. * @@ -36,49 +42,103 @@ export default function LatestComments( { attributes, setAttributes } ) { }, }; + const dropdownMenuProps = useToolsPanelDropdownMenuProps(); + return (
- - { + setAttributes( { + commentsToShow: 5, + displayAvatar: true, + displayDate: true, + displayExcerpt: true, + } ); + } } + dropdownMenuProps={ dropdownMenuProps } + > + ! displayAvatar } label={ __( 'Display avatar' ) } - checked={ displayAvatar } - onChange={ () => - setAttributes( { displayAvatar: ! displayAvatar } ) + onDeselect={ () => + setAttributes( { displayAvatar: true } ) } - /> - + + setAttributes( { + displayAvatar: ! displayAvatar, + } ) + } + /> + + + ! displayDate } label={ __( 'Display date' ) } - checked={ displayDate } - onChange={ () => - setAttributes( { displayDate: ! displayDate } ) + onDeselect={ () => + setAttributes( { displayDate: true } ) } - /> - + + setAttributes( { displayDate: ! displayDate } ) + } + /> + + + ! displayExcerpt } label={ __( 'Display excerpt' ) } - checked={ displayExcerpt } - onChange={ () => - setAttributes( { - displayExcerpt: ! displayExcerpt, - } ) + onDeselect={ () => + setAttributes( { displayExcerpt: true } ) } - /> - + + setAttributes( { + displayExcerpt: ! displayExcerpt, + } ) + } + /> + + + commentsToShow !== 5 } label={ __( 'Number of comments' ) } - value={ commentsToShow } - onChange={ ( value ) => - setAttributes( { commentsToShow: value } ) + onDeselect={ () => + setAttributes( { commentsToShow: 5 } ) } - min={ MIN_COMMENTS } - max={ MAX_COMMENTS } - required - /> - + isShownByDefault + > + + setAttributes( { commentsToShow: value } ) + } + min={ MIN_COMMENTS } + max={ MAX_COMMENTS } + required + /> + + { await triggerBlockListLayout( listBlock ); // Get List item - const listItemBlock = await getBlock( screen, 'List item' ); + const listItemBlock = await getBlock( screen, 'List Item' ); fireEvent.press( listItemBlock ); expect( listItemBlock ).toBeVisible(); @@ -75,7 +75,7 @@ describe( 'List block', () => { // Select List Item block const [ listItemBlock ] = screen.getAllByLabelText( - /List item Block\. Row 1/ + /List Item Block\. Row 1/ ); fireEvent.press( listItemBlock ); @@ -124,7 +124,7 @@ describe( 'List block', () => { // Select List Item block const [ firstNestedLevelBlock ] = within( listBlock ).getAllByLabelText( - /List item Block\. Row 2/ + /List Item Block\. Row 2/ ); fireEvent.press( firstNestedLevelBlock ); await triggerBlockListLayout( firstNestedLevelBlock ); @@ -159,7 +159,7 @@ describe( 'List block', () => { // Select Second List Item block const [ listItemBlock ] = screen.getAllByLabelText( - /List item Block\. Row 2/ + /List Item Block\. Row 2/ ); fireEvent.press( listItemBlock ); @@ -169,7 +169,7 @@ describe( 'List block', () => { // Await recently indented list item layout const [ listItemBlock1 ] = screen.getAllByLabelText( - /List item Block\. Row 1/ + /List Item Block\. Row 1/ ); await triggerBlockListLayout( listItemBlock1 ); @@ -203,7 +203,7 @@ describe( 'List block', () => { // Select List Item block const [ firstNestedLevelBlock ] = within( listBlock ).getAllByLabelText( - /List item Block\. Row 1/ + /List Item Block\. Row 1/ ); fireEvent.press( firstNestedLevelBlock ); await triggerBlockListLayout( firstNestedLevelBlock ); @@ -217,7 +217,7 @@ describe( 'List block', () => { // Select nested List Item block const [ listItemBlock ] = within( innerBlockList ).getAllByLabelText( - /List item Block\. Row 1/ + /List Item Block\. Row 1/ ); fireEvent.press( listItemBlock ); @@ -500,7 +500,7 @@ describe( 'List block', () => { // Select List Item block const [ listItemBlock ] = within( listBlock ).getAllByLabelText( - /List item Block\. Row 1/ + /List Item Block\. Row 1/ ); fireEvent.press( listItemBlock ); @@ -560,7 +560,7 @@ describe( 'List block', () => { // Select List Item block const [ listItemBlock ] = within( listBlock ).getAllByLabelText( - /List item Block\. Row 1/ + /List Item Block\. Row 1/ ); fireEvent.press( listItemBlock ); diff --git a/packages/block-library/src/page-list/edit.js b/packages/block-library/src/page-list/edit.js index 8f1409f864f9b..458b8075749e5 100644 --- a/packages/block-library/src/page-list/edit.js +++ b/packages/block-library/src/page-list/edit.js @@ -322,58 +322,60 @@ export default function PageListEdit( { return ( <> - - { - setAttributes( { parentPageID: 0 } ); - } } - dropdownMenuProps={ dropdownMenuProps } - > - { pagesTree.length > 0 && ( - parentPageID !== 0 } - onDeselect={ () => - setAttributes( { parentPageID: 0 } ) - } - isShownByDefault - > - - setAttributes( { - parentPageID: value ?? 0, - } ) + { ( pagesTree.length > 0 || allowConvertToLinks ) && ( + + { + setAttributes( { parentPageID: 0 } ); + } } + dropdownMenuProps={ dropdownMenuProps } + > + { pagesTree.length > 0 && ( + parentPageID !== 0 } + onDeselect={ () => + setAttributes( { parentPageID: 0 } ) } - help={ __( - 'Choose a page to show only its subpages.' - ) } - /> - - ) } - - { allowConvertToLinks && ( -
-

{ convertDescription }

- -
- ) } -
-
+ + setAttributes( { + parentPageID: value ?? 0, + } ) + } + help={ __( + 'Choose a page to show only its subpages.' + ) } + /> +
+ ) } + + { allowConvertToLinks && ( +
+

{ convertDescription }

+ +
+ ) } +
+
+ ) } { allowConvertToLinks && ( <> diff --git a/packages/block-library/src/paragraph/edit.js b/packages/block-library/src/paragraph/edit.js index f1c2e15537b99..9e625a8bdd004 100644 --- a/packages/block-library/src/paragraph/edit.js +++ b/packages/block-library/src/paragraph/edit.js @@ -20,16 +20,13 @@ import { useBlockProps, useSettings, useBlockEditingMode, - store as blockEditorStore, } from '@wordpress/block-editor'; -import { useSelect } from '@wordpress/data'; import { getBlockSupport } from '@wordpress/blocks'; import { formatLtr } from '@wordpress/icons'; /** * Internal dependencies */ import { useOnEnter } from './use-enter'; -import { unlock } from '../lock-unlock'; function ParagraphRTLControl( { direction, setDirection } ) { return ( @@ -111,11 +108,7 @@ function ParagraphBlock( { isSelected: isSingleSelected, name, } ) { - const isZoomOut = useSelect( ( select ) => - unlock( select( blockEditorStore ) ).isZoomOut() - ); - - const { align, content, direction, dropCap } = attributes; + const { align, content, direction, dropCap, placeholder } = attributes; const blockProps = useBlockProps( { ref: useOnEnter( { clientId, content } ), className: clsx( { @@ -125,12 +118,6 @@ function ParagraphBlock( { style: { direction }, } ); const blockEditingMode = useBlockEditingMode(); - let { placeholder } = attributes; - if ( isZoomOut ) { - placeholder = ''; - } else if ( ! placeholder ) { - placeholder = __( 'Type / to choose a block' ); - } return ( <> @@ -182,10 +169,8 @@ function ParagraphBlock( { : __( 'Block: Paragraph' ) } data-empty={ RichText.isEmpty( content ) } - placeholder={ placeholder } - data-custom-placeholder={ - placeholder && ! isZoomOut ? true : undefined - } + placeholder={ placeholder || __( 'Type / to choose a block' ) } + data-custom-placeholder={ placeholder ? true : undefined } __unstableEmbedURLOnPaste __unstableAllowPrefixTransformations /> diff --git a/packages/block-library/src/paragraph/editor.scss b/packages/block-library/src/paragraph/editor.scss index 369cc5cb1d63a..ad4043bee610a 100644 --- a/packages/block-library/src/paragraph/editor.scss +++ b/packages/block-library/src/paragraph/editor.scss @@ -22,3 +22,10 @@ .block-editor-block-list__block[data-type="core/paragraph"].has-text-align-left[style*="writing-mode: vertical-lr"] { rotate: 180deg; } + +// Hide the placeholder when the editor is in zoomed out mode. +.is-zoomed-out .block-editor-block-list__block[data-empty="true"] { + [data-rich-text-placeholder] { + opacity: 0; + } +} diff --git a/packages/block-library/src/pattern/block.json b/packages/block-library/src/pattern/block.json index 13fc9d154b15a..c30be8458e636 100644 --- a/packages/block-library/src/pattern/block.json +++ b/packages/block-library/src/pattern/block.json @@ -2,7 +2,7 @@ "$schema": "https://schemas.wp.org/trunk/block.json", "apiVersion": 3, "name": "core/pattern", - "title": "Pattern placeholder", + "title": "Pattern Placeholder", "category": "theme", "description": "Show a block pattern.", "supports": { diff --git a/packages/block-library/src/post-date/edit.js b/packages/block-library/src/post-date/edit.js index 36de2f7e5d725..0599e86090465 100644 --- a/packages/block-library/src/post-date/edit.js +++ b/packages/block-library/src/post-date/edit.js @@ -179,9 +179,7 @@ export default function PostDateEdit( { dropdownMenuProps={ dropdownMenuProps } > - format !== undefined && format !== siteFormat - } + hasValue={ () => !! format } label={ __( 'Date Format' ) } onDeselect={ () => setAttributes( { format: undefined } ) diff --git a/packages/block-library/src/post-navigation-link/variations.js b/packages/block-library/src/post-navigation-link/variations.js index e49be1542685e..125f10942cd15 100644 --- a/packages/block-library/src/post-navigation-link/variations.js +++ b/packages/block-library/src/post-navigation-link/variations.js @@ -8,7 +8,7 @@ const variations = [ { isDefault: true, name: 'post-next', - title: __( 'Next post' ), + title: __( 'Next Post' ), description: __( 'Displays the post link that follows the current post.' ), @@ -24,7 +24,7 @@ const variations = [ }, { name: 'post-previous', - title: __( 'Previous post' ), + title: __( 'Previous Post' ), description: __( 'Displays the post link that precedes the current post.' ), diff --git a/packages/block-library/src/post-time-to-read/block.json b/packages/block-library/src/post-time-to-read/block.json index ce0ab2be6c02f..a9f09895556c7 100644 --- a/packages/block-library/src/post-time-to-read/block.json +++ b/packages/block-library/src/post-time-to-read/block.json @@ -3,7 +3,7 @@ "apiVersion": 3, "__experimental": true, "name": "core/post-time-to-read", - "title": "Time To Read", + "title": "Time to Read", "category": "theme", "description": "Show minutes required to finish reading the post.", "textdomain": "default", diff --git a/packages/block-library/src/query-no-results/block.json b/packages/block-library/src/query-no-results/block.json index c2b7224aa40b1..44d2ceef987e2 100644 --- a/packages/block-library/src/query-no-results/block.json +++ b/packages/block-library/src/query-no-results/block.json @@ -2,7 +2,7 @@ "$schema": "https://schemas.wp.org/trunk/block.json", "apiVersion": 3, "name": "core/query-no-results", - "title": "No results", + "title": "No Results", "category": "theme", "description": "Contains the block elements used to render content when no query results are found.", "ancestor": [ "core/query" ], diff --git a/packages/block-library/src/query-pagination-numbers/edit.js b/packages/block-library/src/query-pagination-numbers/edit.js index cf2f92f41791f..a03ed8419bb08 100644 --- a/packages/block-library/src/query-pagination-numbers/edit.js +++ b/packages/block-library/src/query-pagination-numbers/edit.js @@ -67,7 +67,7 @@ export default function QueryPaginationNumbersEdit( { > midSize !== undefined } + hasValue={ () => midSize !== 2 } onDeselect={ () => setAttributes( { midSize: 2 } ) } isShownByDefault > diff --git a/packages/block-library/src/query-title/edit.js b/packages/block-library/src/query-title/edit.js index 3b739e2b20b4f..0da6da47ef614 100644 --- a/packages/block-library/src/query-title/edit.js +++ b/packages/block-library/src/query-title/edit.js @@ -141,6 +141,7 @@ export default function QueryTitleEdit( { showSearchTerm: true, } ) } + dropdownMenuProps={ dropdownMenuProps } > ! showSearchTerm } diff --git a/packages/block-library/src/spacer/controls.js b/packages/block-library/src/spacer/controls.js index fde06d3ee8c33..b5f73a259419d 100644 --- a/packages/block-library/src/spacer/controls.js +++ b/packages/block-library/src/spacer/controls.js @@ -24,6 +24,7 @@ import { View } from '@wordpress/primitives'; */ import { unlock } from '../lock-unlock'; import { MIN_SPACER_SIZE } from './constants'; +import { useToolsPanelDropdownMenuProps } from '../utils/hooks'; const { useSpacingSizes } = unlock( blockEditorPrivateApis ); @@ -93,6 +94,8 @@ export default function SpacerControls( { width, isResizing, } ) { + const dropdownMenuProps = useToolsPanelDropdownMenuProps(); + return ( { orientation === 'horizontal' && ( { /* translators: Setting to play videos within the webpage on mobile browsers rather than opening in a fullscreen player. */ label={ __( 'Play inline' ) } onChange={ toggleFactory.playsInline } - checked={ playsInline } + checked={ !! playsInline } help={ __( 'When enabled, videos will play directly within the webpage on mobile browsers, instead of opening in a fullscreen player.' ) } diff --git a/packages/edit-site/src/components/style-book/constants.ts b/packages/edit-site/src/components/style-book/constants.ts index dcd41287fa239..284816a55da90 100644 --- a/packages/edit-site/src/components/style-book/constants.ts +++ b/packages/edit-site/src/components/style-book/constants.ts @@ -168,7 +168,7 @@ export const STYLE_BOOK_ALL_BLOCKS_SUBCATEGORIES: StyleBookCategory[] = [ }, ]; -// Style book preview categories are organised slightly differently to the editor ones. +// Style book preview categories are organized slightly differently to the editor ones. export const STYLE_BOOK_PREVIEW_CATEGORIES: StyleBookCategory[] = [ { slug: 'overview', diff --git a/packages/react-native-editor/__device-tests__/pages/editor-page.js b/packages/react-native-editor/__device-tests__/pages/editor-page.js index 77f4a700b1137..2333d2dc899d5 100644 --- a/packages/react-native-editor/__device-tests__/pages/editor-page.js +++ b/packages/react-native-editor/__device-tests__/pages/editor-page.js @@ -1118,7 +1118,7 @@ const blockNames = { image: 'Image', latestPosts: 'Latest Posts', list: 'List', - listItem: 'List item', + listItem: 'List Item', more: 'More', paragraph: 'Paragraph', search: 'Search',