Skip to content

Commit

Permalink
Merge branch 'content-search' into trunk
Browse files Browse the repository at this point in the history
  • Loading branch information
tlovett1 committed Feb 15, 2021
2 parents 5a63264 + ade6652 commit 639e86f
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 140 deletions.
38 changes: 36 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function MyComponent( props ) {

return (
<ContentPicker
onChange={ (pickedContent) => { console.log(pickedContent) } }
onPickChange={ (pickedContent) => { console.log(pickedContent) } }
mode="post"
label={ "Please select a Post or Page:" }
contentTypes={ [ 'post', 'page' ] }
Expand All @@ -34,7 +34,7 @@ function MyComponent( props ) {

| Name | Type | Default | Description |
| ---------------- | ---------- | --------------------- | ---------------------------------------------------------------------- |
| `onChange` | `function` | `undefined` | Callback function the list of picked content gets changed |
| `onPickChange` | `function` | `undefined` | Callback function the list of picked content gets changed |
| `label` | `string` | `''` | Renders a label for the Search Field. |
| `mode` | `string` | `'post'` | Either `post` or `term` |
| `placeholder` | `string` | `''` | Renders placeholder text inside the Search Field. |
Expand All @@ -53,6 +53,40 @@ apiFetch( {
path: `wp/v2/search/?search="${keyword}"&subtype="${contentTypes.join(',')}"&type=${mode}`
} )...
```
## ContentSearch

A component that lets you search through posts and pages. This component is used by Content Picker. This component provides only the searching functionality and does not maintain any list of chosen items.

### Usage

```js
import { ContentSearch } from '@10up/block-components';

function MyComponent( props ) {

return (
<ContentSearch
onSelectItem={ (item) => { console.log(item) } }
mode="post"
label={ "Please select a Post or Page:" }
contentTypes={ [ 'post', 'page' ] }
/>
)
}
```

#### Props

| Name | Type | Default | Description |
| ---------------- | ---------- | --------------------- | ---------------------------------------------------------------------- |
| `onSelectItem` | `function` | `undefined` | Function called when a searched item is clicke |
| `label` | `string` | `''` | Renders a label for the Search Field. |
| `mode` | `string` | `'post'` | Either `post` or `term` |
| `placeholder` | `string` | `''` | Renders placeholder text inside the Search Field. |
| `contentTypes` | `array` | `[ 'post', 'page' ]` | Names of the post types or taxonomies that should get searched |
| `excludeItems` | `array` | `[ { id: 1, type: 'post' ]` | Items to exclude from search |



## useHasSelectedInnerBlock

Expand Down
2 changes: 1 addition & 1 deletion components/ContentPicker/PickedItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const PickedItem = ({ item, isOrderable, handleItemDelete, sortIndex, mode, tota
aria-label={__('Delete item', '10up-block-components')}
>
&times;
</button>
</button>
</div>
) : (
<div />
Expand Down
153 changes: 19 additions & 134 deletions components/ContentPicker/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { TextControl, Button, Spinner, NavigableMenu } from '@wordpress/componen
import { select } from '@wordpress/data';
import { useState } from '@wordpress/element'; // eslint-disable-line
import { __ } from '@wordpress/i18n';
import SearchItem from './SearchItem';
import { ContentSearch } from '../ContentSearch';
import SortableList from './SortableList';

const NAMESPACE = '10up-block-components';
Expand All @@ -22,7 +22,7 @@ const ContentPicker = ({
mode,
contentTypes,
placeholder,
onChange,
onPickChange,
maxContentItems,
isOrderable,
singlePickedLabel,
Expand All @@ -31,10 +31,6 @@ const ContentPicker = ({
uniqueContentItems,
excludeCurrentPost,
}) => {
const [searchString, setSearchString] = useState('');
const [searchResults, setSearchResults] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [selectedItem, setSelectedItem] = useState(null);
const [content, setContent] = useState(presetContent);

const currentPostId = select('core/editor').getCurrentPostId();
Expand All @@ -48,97 +44,29 @@ const ContentPicker = ({
}
}

function handleItemSelection(item) {
setSearchResults([]);
setSearchString('');

function handleSelect(item) {
const newContent = [...content];

newContent.unshift({
id: item.id,
type: item.subtype,
});

onChange(newContent);
onPickChange(newContent);

setContent(newContent);
}

/**
* handleSearchStringChange
*
* Using the keyword and the list of tags that are linked to the parent block
* search for posts/terms that match and return them to the autocomplete component.
*
* @param {string} keyword search query string
*/
const handleSearchStringChange = (keyword) => {
setSearchString(keyword);
setIsLoading(true);

const searchQuery = `wp/v2/search/?search=${keyword}&subtype=${contentTypes.join(
',',
)}&type=${mode}`;

apiFetch({
path: searchQuery,
}).then((results) => {
const newResults = results.filter((result) => {
let keep = true;

if (content.length && uniqueContentItems) {
content.forEach((item) => {
if (item.id === result.id) {
keep = false;
}
});
}

if (
excludeCurrentPost &&
currentPostId &&
parseInt(result.id, 10) === parseInt(currentPostId, 10)
) {
keep = false;
}

return keep;
});

setSearchResults(newResults);
setIsLoading(false);
});
};

/**
* handleSelection
*
* update the selected item in state to either the selected item or null if the
* selected item does not have a valid id
*
* @param {*} item
*/
function handleSelection(item) {
if (item === 0) {
setSelectedItem(null);
}

setSelectedItem(item);
}
}

function handleItemDelete(item, sortIndex) {
const newContent = [...content];

newContent.splice(sortIndex, 1);

onChange(newContent);
onPickChange(newContent);

setContent(newContent);
}

const hasSearchString = !!searchString.length;
const hasSearchResults = !!searchResults.length;

/**
* Unfortunately, we had to use !important because on PickedItem we couldn't @emotion/styled css
* as it was breaking sortability from react-sortable-hoc
Expand All @@ -154,61 +82,18 @@ const ContentPicker = ({
}
`;

const excludeItems = uniqueContentItems ? [ ...content ] : [];

if (excludeCurrentPost && currentPostId) {
excludeItems.push({
id: currentPostId,
});
}

return (
<div className={`${NAMESPACE}`}>
{!content.length || (content.length && content.length < maxContentItems) ? (
<NavigableMenu onNavigate={handleSelection} orientation="vertical">
<TextControl
label={label}
value={searchString}
onChange={handleSearchStringChange}
placeholder={placeholder}
autoComplete="off"
/>
{hasSearchString ? (
<ul
className={`${NAMESPACE}-grid`}
style={{
marginTop: '0',
marginBottom: '0',
marginLeft: '0',
paddingLeft: '0',
listStyle: 'none',
}}
>
{isLoading && <Spinner />}
{!isLoading && !hasSearchResults && (
<li className={`${NAMESPACE}-grid-item`}>
<Button disabled>
{__('No Items found', '10up-block-components')}
</Button>
</li>
)}
{searchResults.map((item, index) => {
if (!item.title.length) {
return null;
}

return (
<li
key={item.id}
className={`${NAMESPACE}-grid-item`}
style={{
marginBottom: '0',
}}
>
<SearchItem
onClick={() => handleItemSelection(item)}
searchTerm={searchString}
suggestion={item}
isSelected={selectedItem === index + 1}
/>
</li>
);
})}
</ul>
) : null}
</NavigableMenu>
<ContentSearch excludeItem={excludeItems} onSelect={handleSelect} />
) : null}
{content.length ? (
<StyleWrapper>
Expand All @@ -230,8 +115,8 @@ const ContentPicker = ({
handleItemDelete={handleItemDelete}
onSortEnd={({ oldIndex, newIndex }) => {
const newContent = [...arrayMove(content, oldIndex, newIndex)];

onChange(newContent);
and
onPickChange(newContent);

setContent(newContent);
}}
Expand All @@ -245,7 +130,7 @@ const ContentPicker = ({
ContentPicker.defaultProps = {
label: '',
mode: 'post',
onChange: (ids) => {
onPickChange: (ids) => {
console.log('Content picker list change', ids); // eslint-disable-line no-console
},
contentTypes: ['post', 'page'],
Expand All @@ -268,7 +153,7 @@ ContentPicker.propTypes = {
multiPickedLabel: PropTypes.string,
singlePickedLabel: PropTypes.string,
isOrderable: PropTypes.bool,
onChange: PropTypes.func,
onPickChange: PropTypes.func,
uniqueContentItems: PropTypes.bool,
excludeCurrentPost: PropTypes.bool,
maxContentItems: PropTypes.number,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { safeDecodeURI, filterURLForDisplay } from '@wordpress/url';
import { decodeEntities } from '@wordpress/html-entities';
import { Button, TextHighlight } from '@wordpress/components';

/**
/**
* SearchItem
*
* @param {Object} props react props
Expand Down
Loading

0 comments on commit 639e86f

Please sign in to comment.