diff --git a/docs/manifest.json b/docs/manifest.json
index 5d71772ac69dd..42f2c0ae80a8e 100644
--- a/docs/manifest.json
+++ b/docs/manifest.json
@@ -1145,6 +1145,12 @@
"markdown_source": "../packages/components/src/scrollable/README.md",
"parent": "components"
},
+ {
+ "title": "SearchControl",
+ "slug": "search-control",
+ "markdown_source": "../packages/components/src/search-control/README.md",
+ "parent": "components"
+ },
{
"title": "SelectControl",
"slug": "select-control",
diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js
index a781b86dc2524..6daa8ddd4df35 100644
--- a/packages/block-editor/src/components/index.js
+++ b/packages/block-editor/src/components/index.js
@@ -121,7 +121,6 @@ export { default as DefaultBlockAppender } from './default-block-appender';
export { default as __unstableEditorStyles } from './editor-styles';
export { default as Inserter } from './inserter';
export { default as __experimentalLibrary } from './inserter/library';
-export { default as __experimentalSearchForm } from './inserter/search-form';
export { default as BlockEditorKeyboardShortcuts } from './keyboard-shortcuts';
export { MultiSelectScrollIntoView } from './selection-scroll-into-view';
export { default as NavigableToolbar } from './navigable-toolbar';
diff --git a/packages/block-editor/src/components/inserter/menu.js b/packages/block-editor/src/components/inserter/menu.js
index 9db480c49d5b0..19b01b5a66b3e 100644
--- a/packages/block-editor/src/components/inserter/menu.js
+++ b/packages/block-editor/src/components/inserter/menu.js
@@ -2,7 +2,7 @@
* WordPress dependencies
*/
import { useState, useCallback, useMemo } from '@wordpress/element';
-import { VisuallyHidden } from '@wordpress/components';
+import { VisuallyHidden, SearchControl } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useSelect } from '@wordpress/data';
@@ -10,7 +10,6 @@ import { useSelect } from '@wordpress/data';
* Internal dependencies
*/
import Tips from './tips';
-import InserterSearchForm from './search-form';
import InserterPreviewPanel from './preview-panel';
import BlockTypesTab from './block-types-tab';
import BlockPatternsTabs from './block-patterns-tab';
@@ -171,7 +170,8 @@ function InserterMenu( {
{ /* the following div is necessary to fix the sticky position of the search form */ }
-
{
if ( hoveredItem ) setHoveredItem( null );
setFilterValue( value );
diff --git a/packages/block-editor/src/components/inserter/menu.native.js b/packages/block-editor/src/components/inserter/menu.native.js
index ffac41438fb03..afaffa950028f 100644
--- a/packages/block-editor/src/components/inserter/menu.native.js
+++ b/packages/block-editor/src/components/inserter/menu.native.js
@@ -14,13 +14,16 @@ import {
import { useEffect, useState, useCallback } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import { createBlock } from '@wordpress/blocks';
-import { BottomSheet, BottomSheetConsumer } from '@wordpress/components';
+import {
+ BottomSheet,
+ BottomSheetConsumer,
+ SearchControl,
+} from '@wordpress/components';
/**
* Internal dependencies
*/
import InserterSearchResults from './search-results';
-import InserterSearchForm from './search-form';
import { store as blockEditorStore } from '../../store';
import InserterTabs from './tabs';
import styles from './style.scss';
@@ -192,7 +195,7 @@ function InserterMenu( {
header={
<>
{ showSearchForm && (
-
diff --git a/packages/block-editor/src/components/inserter/quick-inserter.js b/packages/block-editor/src/components/inserter/quick-inserter.js
index ca4ff4c2fa274..e9060e827a938 100644
--- a/packages/block-editor/src/components/inserter/quick-inserter.js
+++ b/packages/block-editor/src/components/inserter/quick-inserter.js
@@ -8,13 +8,12 @@ import classnames from 'classnames';
*/
import { useState, useEffect } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
-import { Button } from '@wordpress/components';
+import { Button, SearchControl } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
/**
* Internal dependencies
*/
-import InserterSearchForm from './search-form';
import InserterSearchResults from './search-results';
import useInsertionPoint from './hooks/use-insertion-point';
import usePatternsState from './hooks/use-patterns-state';
@@ -87,7 +86,8 @@ export default function QuickInserter( {
} ) }
>
{ showSearch && (
- {
setFilterValue( value );
diff --git a/packages/block-editor/src/components/inserter/search-form.js b/packages/block-editor/src/components/inserter/search-form.js
deleted file mode 100644
index 7914e842fd3dc..0000000000000
--- a/packages/block-editor/src/components/inserter/search-form.js
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * External dependencies
- */
-import classnames from 'classnames';
-
-/**
- * WordPress dependencies
- */
-import { useInstanceId } from '@wordpress/compose';
-import { __ } from '@wordpress/i18n';
-import { VisuallyHidden, Button } from '@wordpress/components';
-import { Icon, search, closeSmall } from '@wordpress/icons';
-import { useRef } from '@wordpress/element';
-
-function InserterSearchForm( {
- className,
- onChange,
- value,
- label,
- placeholder,
-} ) {
- const instanceId = useInstanceId( InserterSearchForm );
- const searchInput = useRef();
-
- return (
-
-
- { label || placeholder }
-
-
onChange( event.target.value ) }
- autoComplete="off"
- value={ value || '' }
- />
-
- { !! value && (
-
-
- );
-}
-
-export default InserterSearchForm;
diff --git a/packages/block-editor/src/components/inserter/style.native.scss b/packages/block-editor/src/components/inserter/style.native.scss
index a0f017931a62a..1ed3d8ab6a3bc 100644
--- a/packages/block-editor/src/components/inserter/style.native.scss
+++ b/packages/block-editor/src/components/inserter/style.native.scss
@@ -13,37 +13,6 @@
padding-top: 8;
}
-.searchForm {
- height: 46px;
- border-radius: 8px;
- color: $gray-dark;
- margin: $grid-unit-30;
- background-color: $gray-light;
- flex-direction: row;
- justify-content: space-between;
-}
-
-.searchFormDark {
- background-color: rgba($white, 0.07);
-}
-
-.searchFormInput {
- color: $gray-dark;
- flex: 2;
-}
-
-.searchFormInputDark {
- color: $white;
-}
-
-.searchFormPlaceholder {
- color: $gray;
-}
-
-.searchFormPlaceholderDark {
- color: rgba($white, 0.8);
-}
-
.inserter-tabs__wrapper {
overflow: hidden;
}
diff --git a/packages/block-editor/src/components/inserter/style.scss b/packages/block-editor/src/components/inserter/style.scss
index cb8f94f93eb6e..8792dfe969909 100644
--- a/packages/block-editor/src/components/inserter/style.scss
+++ b/packages/block-editor/src/components/inserter/style.scss
@@ -99,55 +99,14 @@ $block-inserter-tabs-height: 44px;
}
.block-editor-inserter__search {
+ background: $white;
padding: $grid-unit-20;
position: sticky;
top: 0;
- background: $white;
z-index: z-index(".block-editor-inserter__search");
- input[type="search"].block-editor-inserter__search-input {
- @include input-control;
- display: block;
- padding: $grid-unit-20 $grid-unit-60 $grid-unit-20 $grid-unit-20;
- background: $gray-100;
- border: none;
- width: 100%;
- height: $grid-unit-60;
-
- /* Fonts smaller than 16px causes mobile safari to zoom. */
- font-size: $mobile-text-min-font-size;
- @include break-small {
- font-size: $default-font-size;
- }
-
- &:focus {
- background: $white;
- box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
- }
-
- &::placeholder {
- color: $gray-700;
- }
-
- &::-webkit-search-decoration,
- &::-webkit-search-cancel-button,
- &::-webkit-search-results-button,
- &::-webkit-search-results-decoration {
- -webkit-appearance: none;
- }
- }
-}
-
-.block-editor-inserter__search-icon {
- position: absolute;
- top: 0;
- right: $grid-unit-10 + ($grid-unit-60 - $icon-size) / 2;
- bottom: 0;
- display: flex;
- align-items: center;
-
- > svg {
- margin: $grid-unit-10;
+ .components-search-control__icon {
+ right: $grid-unit-10 + ($grid-unit-60 - $icon-size) / 2;
}
}
diff --git a/packages/block-library/src/template-part/edit/selection/index.js b/packages/block-library/src/template-part/edit/selection/index.js
index 2ed5ac835f81f..4bdef9130e33d 100644
--- a/packages/block-library/src/template-part/edit/selection/index.js
+++ b/packages/block-library/src/template-part/edit/selection/index.js
@@ -1,7 +1,7 @@
/**
* WordPress dependencies
*/
-import { __experimentalSearchForm as SearchForm } from '@wordpress/block-editor';
+import { SearchControl } from '@wordpress/components';
import { useState } from '@wordpress/element';
import { LEFT, RIGHT, UP, DOWN, BACKSPACE, ENTER } from '@wordpress/keycodes';
/**
@@ -34,7 +34,7 @@ export default function TemplatePartSelection( {
onKeyPress={ stopKeyPropagation }
onKeyDown={ preventArrowKeysPropagation }
>
-
+ );
+}
+```
+
+### Props
+
+The set of props accepted by the component will be specified below.
+Props not included in this set will be applied to the input element.
+
+#### label
+
+If this property is added, a label will be generated using label property as the content.
+
+- Type: `String`
+- Required: Yes
+
+#### placeholder
+
+If this property is added, a specific placeholder will be used for the input.
+
+- Type: `String`
+- Required: No
+#### value
+
+The current value of the input.
+
+- Type: `String | Number`
+- Required: Yes
+
+#### className
+
+The class that will be added to the classes of the wrapper div.
+
+- Type: `String`
+- Required: No
+
+#### onChange
+
+A function that receives the value of the input.
+
+- Type: `function`
+- Required: Yes
+
+#### help
+
+If this property is added, a help text will be generated using help property as the content.
+
+- Type: `String|WPElement`
+- Required: No
+### hideLabelFromVision
+
+If true, the label will only be visible to screen readers.
+
+- Type: `Boolean`
+- Required: No
+
+## Related components
+
+- To offer users more constrained options for input, use TextControl, SelectControl, RadioControl, CheckboxControl, or RangeControl.
diff --git a/packages/components/src/search-control/index.js b/packages/components/src/search-control/index.js
new file mode 100644
index 0000000000000..fc0bd900f3b15
--- /dev/null
+++ b/packages/components/src/search-control/index.js
@@ -0,0 +1,70 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+
+/**
+ * WordPress dependencies
+ */
+import { useInstanceId } from '@wordpress/compose';
+import { __ } from '@wordpress/i18n';
+import { Icon, search, closeSmall } from '@wordpress/icons';
+import { useRef } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import { Button } from '../';
+import BaseControl from '../base-control';
+
+function SearchControl( {
+ className,
+ onChange,
+ value,
+ label,
+ placeholder = __( 'Search' ),
+ hideLabelFromVision = true,
+ help,
+} ) {
+ const instanceId = useInstanceId( SearchControl );
+ const searchInput = useRef();
+ const id = `components-search-control-${ instanceId }`;
+
+ return (
+
+
+
onChange( event.target.value ) }
+ autoComplete="off"
+ value={ value || '' }
+ />
+
+ { !! value && (
+
+
+
+ );
+}
+
+export default SearchControl;
diff --git a/packages/block-editor/src/components/inserter/search-form.native.js b/packages/components/src/search-control/index.native.js
similarity index 91%
rename from packages/block-editor/src/components/inserter/search-form.native.js
rename to packages/components/src/search-control/index.native.js
index f23d6b95b9100..0ab3e4e69112b 100644
--- a/packages/block-editor/src/components/inserter/search-form.native.js
+++ b/packages/components/src/search-control/index.native.js
@@ -22,7 +22,12 @@ import {
*/
import styles from './style.scss';
-function InserterSearchForm( { value, onChange } ) {
+function SearchControl( {
+ value,
+ onChange,
+ label,
+ placeholder = __( 'Search' ),
+} ) {
const [ isActive, setIsActive ] = useState( false );
const inputRef = useRef();
@@ -57,7 +62,7 @@ function InserterSearchForm( { value, onChange } ) {
/>
) : (
{
inputRef.current.focus();
@@ -72,7 +77,7 @@ function InserterSearchForm( { value, onChange } ) {
onChangeText={ onChange }
onFocus={ () => setIsActive( true ) }
value={ value }
- placeholder={ __( 'Search blocks' ) }
+ placeholder={ placeholder }
/>
{ !! value && (
@@ -89,4 +94,4 @@ function InserterSearchForm( { value, onChange } ) {
);
}
-export default InserterSearchForm;
+export default SearchControl;
diff --git a/packages/components/src/search-control/stories/index.js b/packages/components/src/search-control/stories/index.js
new file mode 100644
index 0000000000000..2710542a5d6a2
--- /dev/null
+++ b/packages/components/src/search-control/stories/index.js
@@ -0,0 +1,36 @@
+/**
+ * External dependencies
+ */
+import { boolean, text } from '@storybook/addon-knobs';
+
+/**
+ * WordPress dependencies
+ */
+import { useState } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import SearchControl from '../';
+
+export default {
+ title: 'Components/SearchControl',
+ component: SearchControl,
+};
+
+export const _default = () => {
+ const [ value, setValue ] = useState();
+ const label = text( 'Label', 'Label Text' );
+ const hideLabelFromVision = boolean( 'Hide Label From Vision', true );
+ const help = text( 'Help Text', 'Help text to explain the input.' );
+
+ return (
+
+ );
+};
diff --git a/packages/components/src/search-control/style.native.scss b/packages/components/src/search-control/style.native.scss
new file mode 100644
index 0000000000000..8617ad232552f
--- /dev/null
+++ b/packages/components/src/search-control/style.native.scss
@@ -0,0 +1,30 @@
+.searchForm {
+ height: 46px;
+ border-radius: 8px;
+ color: $gray-dark;
+ margin: $grid-unit-30;
+ background-color: $gray-light;
+ flex-direction: row;
+ justify-content: space-between;
+}
+
+.searchFormDark {
+ background-color: rgba($white, 0.07);
+}
+
+.searchFormInput {
+ color: $gray-dark;
+ flex: 2;
+}
+
+.searchFormInputDark {
+ color: $white;
+}
+
+.searchFormPlaceholder {
+ color: $gray;
+}
+
+.searchFormPlaceholderDark {
+ color: rgba($white, 0.8);
+}
diff --git a/packages/components/src/search-control/style.scss b/packages/components/src/search-control/style.scss
new file mode 100644
index 0000000000000..247b726fc9dd9
--- /dev/null
+++ b/packages/components/src/search-control/style.scss
@@ -0,0 +1,52 @@
+.components-search-control {
+ position: relative;
+
+ input[type="search"].components-search-control__input {
+ @include input-control;
+ display: block;
+ padding: $grid-unit-20 $grid-unit-60 $grid-unit-20 $grid-unit-20;
+ background: $gray-100;
+ border: none;
+ width: 100%;
+ height: $grid-unit-60;
+
+ /* Fonts smaller than 16px causes mobile safari to zoom. */
+ font-size: $mobile-text-min-font-size;
+ @include break-small {
+ font-size: $default-font-size;
+ }
+
+ &:focus {
+ background: $white;
+ box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
+ }
+
+ &::placeholder {
+ color: $gray-700;
+ }
+
+ &::-webkit-search-decoration,
+ &::-webkit-search-cancel-button,
+ &::-webkit-search-results-button,
+ &::-webkit-search-results-decoration {
+ -webkit-appearance: none;
+ }
+ }
+}
+
+.components-search-control__icon {
+ position: absolute;
+ top: 0;
+ right: ( $grid-unit-60 - $icon-size ) / 2;
+ bottom: 0;
+ display: flex;
+ align-items: center;
+
+ > svg {
+ margin: $grid-unit-10 0;
+ }
+}
+
+.components-search-control__input-wrapper {
+ position: relative;
+}
diff --git a/packages/components/src/style.scss b/packages/components/src/style.scss
index 13c2049b894bf..21f13e7d492f5 100644
--- a/packages/components/src/style.scss
+++ b/packages/components/src/style.scss
@@ -35,6 +35,7 @@
@import "./responsive-wrapper/style.scss";
@import "./sandbox/style.scss";
@import "./scroll-lock/style.scss";
+@import "./search-control/style.scss";
@import "./select-control/style.scss";
@import "./snackbar/style.scss";
@import "./swatch/style.scss";
diff --git a/packages/e2e-test-utils/src/inserter.js b/packages/e2e-test-utils/src/inserter.js
index 48a4985bfe62f..1bcce329f07df 100644
--- a/packages/e2e-test-utils/src/inserter.js
+++ b/packages/e2e-test-utils/src/inserter.js
@@ -7,7 +7,7 @@ import { canvas } from './canvas';
// This selector is written to support the current and old inserter markup
// because the performance tests need to be able to run across versions.
const INSERTER_SEARCH_SELECTOR =
- '.block-editor-inserter__search-input,input.block-editor-inserter__search';
+ '.block-editor-inserter__search input,.block-editor-inserter__search-input,input.block-editor-inserter__search';
/**
* Opens the global block inserter.
diff --git a/packages/e2e-tests/specs/editor/plugins/cpt-locking.test.js b/packages/e2e-tests/specs/editor/plugins/cpt-locking.test.js
index aee6398871a9b..03abfc56d189b 100644
--- a/packages/e2e-tests/specs/editor/plugins/cpt-locking.test.js
+++ b/packages/e2e-tests/specs/editor/plugins/cpt-locking.test.js
@@ -123,12 +123,12 @@ describe( 'cpt locking', () => {
await page.click(
'.wp-block-column .block-editor-button-block-appender'
);
- await page.type( '.block-editor-inserter__search-input', 'image' );
+ await page.type( '.block-editor-inserter__search input', 'image' );
await pressKeyTimes( 'Tab', 2 );
await page.keyboard.press( 'Enter' );
await page.click( '.edit-post-header-toolbar__inserter-toggle' );
await page.type(
- '.block-editor-inserter__search-input',
+ '.block-editor-inserter__search input',
'gallery'
);
await pressKeyTimes( 'Tab', 2 );
diff --git a/packages/e2e-tests/specs/editor/various/inserting-blocks.test.js b/packages/e2e-tests/specs/editor/various/inserting-blocks.test.js
index e4a0e07a13d81..acc7965c5621d 100644
--- a/packages/e2e-tests/specs/editor/various/inserting-blocks.test.js
+++ b/packages/e2e-tests/specs/editor/various/inserting-blocks.test.js
@@ -147,7 +147,7 @@ describe( 'Inserting blocks', () => {
() =>
document.activeElement &&
document.activeElement.classList.contains(
- 'block-editor-inserter__search-input'
+ 'components-search-control__input'
)
);
await page.keyboard.type( 'para' );
@@ -185,7 +185,7 @@ describe( 'Inserting blocks', () => {
() => document.activeElement.classList
);
expect( Object.values( activeElementClassList ) ).toContain(
- 'block-editor-inserter__search-input'
+ 'components-search-control__input'
);
// Try using the up arrow key (vertical navigation triggers the issue described in #9583).
@@ -196,7 +196,7 @@ describe( 'Inserting blocks', () => {
() => document.activeElement.classList
);
expect( Object.values( activeElementClassList ) ).toContain(
- 'block-editor-inserter__search-input'
+ 'components-search-control__input'
);
// Tab to the block list
@@ -250,7 +250,7 @@ describe( 'Inserting blocks', () => {
);
// Insert a paragraph block.
- await page.waitForSelector( '.block-editor-inserter__search-input' );
+ await page.waitForSelector( '.block-editor-inserter__search input' );
// Search for the paragraph block if it's not in the list of blocks shown.
if ( ! page.$( '.editor-block-list-item-paragraph' ) ) {
diff --git a/packages/e2e-tests/specs/editor/various/writing-flow.test.js b/packages/e2e-tests/specs/editor/various/writing-flow.test.js
index 3b8e6746ac866..566303fabfeaf 100644
--- a/packages/e2e-tests/specs/editor/various/writing-flow.test.js
+++ b/packages/e2e-tests/specs/editor/various/writing-flow.test.js
@@ -29,7 +29,7 @@ const addParagraphsAndColumnsDemo = async () => {
await page.keyboard.press( 'Enter' );
await page.click( ':focus [aria-label="Two columns; equal split"]' );
await page.click( ':focus .block-editor-button-block-appender' );
- await page.waitForSelector( ':focus.block-editor-inserter__search-input' );
+ await page.waitForSelector( '.block-editor-inserter__search input:focus' );
await page.keyboard.type( 'Paragraph' );
await pressKeyTimes( 'Tab', 2 ); // Tab to paragraph result.
await page.keyboard.press( 'Enter' ); // Insert paragraph.
@@ -40,7 +40,7 @@ const addParagraphsAndColumnsDemo = async () => {
// is a temporary solution.
await page.focus( '.wp-block[data-type="core/column"]:nth-child(2)' );
await page.click( ':focus .block-editor-button-block-appender' );
- await page.waitForSelector( ':focus.block-editor-inserter__search-input' );
+ await page.waitForSelector( '.block-editor-inserter__search input:focus' );
await page.keyboard.type( 'Paragraph' );
await pressKeyTimes( 'Tab', 2 ); // Tab to paragraph result.
await page.keyboard.press( 'Enter' ); // Insert paragraph.
diff --git a/packages/edit-post/src/components/block-manager/index.js b/packages/edit-post/src/components/block-manager/index.js
index 8dc3aea8ec91d..b183f8f7e1874 100644
--- a/packages/edit-post/src/components/block-manager/index.js
+++ b/packages/edit-post/src/components/block-manager/index.js
@@ -8,10 +8,9 @@ import { filter, includes, isArray } from 'lodash';
*/
import { store as blocksStore } from '@wordpress/blocks';
import { withSelect } from '@wordpress/data';
-import { VisuallyHidden, TextControl } from '@wordpress/components';
+import { SearchControl } from '@wordpress/components';
import { __, _n, sprintf } from '@wordpress/i18n';
import { useState } from '@wordpress/element';
-import { useInstanceId } from '@wordpress/compose';
/**
* Internal dependencies
@@ -27,7 +26,6 @@ function BlockManager( {
numberOfHiddenBlocks,
} ) {
const [ search, setSearch ] = useState( '' );
- const instanceId = useInstanceId( BlockManager );
// Filtering occurs here (as opposed to `withSelect`) to avoid
// wasted renders by consequence of `Array#filter` producing
@@ -55,15 +53,8 @@ function BlockManager( {
) }
) }
-
- { __( 'Search for a block' ) }
-
-
setSearch( nextSearch ) }
diff --git a/packages/edit-post/src/components/block-manager/style.scss b/packages/edit-post/src/components/block-manager/style.scss
index e375b5091df2b..3905eeea059f7 100644
--- a/packages/edit-post/src/components/block-manager/style.scss
+++ b/packages/edit-post/src/components/block-manager/style.scss
@@ -6,19 +6,6 @@
.edit-post-block-manager__search {
margin: $grid-unit-20 0;
-
- .components-base-control__field {
- margin-bottom: 0;
- }
-
- .components-base-control__label {
- margin-top: -0.25 * $grid-unit-20;
- }
-
- input[type="search"].components-text-control__input {
- padding: $grid-unit-10;
- border-radius: $radius-block-ui;
- }
}
.edit-post-block-manager__disabled-blocks-count {