-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce base documentation for developers #17
- Loading branch information
Showing
17 changed files
with
872 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# WordPress Entities Search | ||
|
||
This package is a library exposing reusable Components and Utilities to help you build Entities searching and storage. | ||
|
||
At it's core it permits to build components and reuse the WordPress Rest API to search for Posts associated to one or | ||
more Post Types but also, to search for Terms belonging to one or more Taxonomies. | ||
|
||
## Table of Content | ||
|
||
1. [Installation](./docs/installation.md) | ||
2. [Development](./docs/development.md) | ||
3. [Api](./docs/api.md) | ||
4. [Components](./docs/components.md) | ||
5. [Hooks](./docs/hooks.md) | ||
6. [Logging](./docs/logging.md) | ||
7. [Storage](./docs/storage.md) | ||
|
||
## License | ||
|
||
This software is released under the ["GNU General Public License v2.0 or later"](./LICENSE) license. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
# Api | ||
|
||
The `api` define a `fetch` function which is there to hide the WordPress Fetch API. The reason behind this decision lies on the fact that we might want to add middlewares to the request in the future or introducing some global data manipulation. | ||
|
||
The package provide then the following functions to interact with the WordPress API: | ||
|
||
## `searchEntities` | ||
|
||
This function is a wrapper for the Search Endpoint of the WordPress API. It will return a `Set` of entities matching the search query. | ||
|
||
The possible query arguments are specified by the `EntitiesSearch.QueryArguments` type, an interface you can expand to add more arguments. | ||
|
||
This function is not directly consumed by the components, and it is not intended to be used internally. The idea is to follow the Tell don't Ask principle and let the consumer specify how to search the Entities. | ||
|
||
Below a possible example of its usage related to a component: | ||
|
||
```tsx | ||
// my-component.tsx | ||
type Search = EntitiesSearch.SearchEntitiesFunction<string, string>; | ||
type Props = { | ||
search: Search | ||
} | ||
|
||
const MyComponent (props: Props) => { | ||
const [entities, setEntities] = useState<Set>(new Set()); | ||
|
||
useEffect(() => { | ||
props | ||
.search( | ||
'my search phrase', | ||
'post', | ||
new Set(['page']), | ||
{ | ||
per_page: 10, | ||
_fields: ['slug', 'title'] | ||
} | ||
) | ||
.then(setEntities); | ||
}, []); | ||
|
||
// Do something with the entities | ||
} | ||
|
||
// index.tsx | ||
import {searchEntities} from 'wp-entities-search'; | ||
|
||
root.render(<MyComponent search={searchEntities} />); | ||
``` | ||
|
||
### Parameters | ||
|
||
The function accept four parameters | ||
|
||
- `type` - The root type. We have two built-int supported types `post`, `term`. | ||
- `subtype` - The subtype of the entity. For example, if the `type` is `post`, the `subtype` can be `page`, `post`, etc. WordPress exclude automatically the `attachment` subtype. | ||
- `phrase` - The search phrase. This becomes the `s` parameter for the Search Endpoint. | ||
- `queryArguments` - All the supported and extra query arguments. The `EntitiesSearch.QueryArguments` type is an interface you can expand to add more arguments. | ||
|
||
### Return | ||
|
||
The function return an immutable `Set` of entities. | ||
|
||
NOTE: The function does not handle possible request exceptions. | ||
|
||
### Aborting Requests | ||
|
||
The `searchEntities` will automatically abort the requests with the same parameters if a new request is made before the previous one is completed. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
# Components | ||
|
||
The Wp Entities Search provides a set of components you can use to build your search UI. | ||
|
||
We have to distinguish between two types of components: | ||
|
||
- **Base Components**: These are the basic building blocks of the search UI. They are the smallest components and are | ||
used to build more complex components. | ||
- **Composite Components**: These are more complex components that are built using the base ones. | ||
|
||
The _Composite Component_ does not have a direct dependency on the _Base Components_, indeed a composite component | ||
encapsulate the logic | ||
that deal with the state management of the data and pass it down to the base components in form of properties via | ||
the `children` function. | ||
|
||
In a nutshell, a _Composite Component_ is configured from the external via props, dialog with the Rest API and pass the | ||
data down to the _Base Components_. | ||
|
||
## Base Components | ||
|
||
- `PluralSelectControl` - A select control that allows multiple selections. | ||
- `SingleSelectControl` - A select control that allows a single selection. | ||
- `SearchControl` - An input control that allows the user to enter a search query. | ||
- `ToggleControl` - A multi selection control that allows the user to toggle between multiple options. | ||
- `RadioControl` - A single selection control that allows the user to select a single option. | ||
|
||
## Composite Components | ||
|
||
- `CompositeEntitiesByKind` - A composite component that displays a list of entities by kind. In this context _kind_ | ||
is `post` or `term` and _entity_ is the entity type of the content e.g. a `page` or a `category` term. | ||
|
||
## Composite Entities by Kind | ||
|
||
The `CompositeEntitiesByKind` is a _generic_ component that can be used to display a list of entities by kind. It acts | ||
as a controller that fetches the data from the Rest API using a given function and pass it down to one or more _Base | ||
Components_. | ||
It is up to you how to consume the data given to the children. Note, even if you consume just one part of the given | ||
data, | ||
the component will fetch all the data necessary to render the UI. | ||
|
||
This component is intended to fulfill a scenario where the user is looking for some entities belonging to a certain | ||
_kind_, | ||
for instance, selecting the `page` _kind_ will return a list of the available pages and to filter the result further it | ||
is | ||
possible to use a search function in conjunction with a `search` field. | ||
|
||
An example of its usage is: | ||
|
||
```jsx | ||
import { CompositeEntitiesByKind } from 'wp-entities-search'; | ||
|
||
export function MyComponent(props) { | ||
return <CompositeEntitiesByKind | ||
searchEntities={ | ||
async (phrase, kind, queryArguments) => convertEntitiesToControlOptions( | ||
await searchEntities('post', kind, phrase, queryArguments), | ||
'title', | ||
'id' | ||
) | ||
} | ||
entities={{ | ||
value: new Set([13, 24, 55]), | ||
onChange: (value) => { | ||
// Do something with the selected entities | ||
// Notice, here you'll receive the value as a string Set. | ||
} | ||
}} | ||
kind={{ | ||
value: new Set(['page']), | ||
options: [ | ||
{ label: 'Pages', value: 'page' }, | ||
{ label: 'Posts', value: 'post' }, | ||
], | ||
onChange={(value) => { | ||
// Do something with the selected kind | ||
}} | ||
}} | ||
> | ||
{({ entities, kind, search }) => ( | ||
<> | ||
<ToggleControl | ||
value={kind.value} | ||
options={kind.options} | ||
onChange={kind.onChange} | ||
/> | ||
<SearchControl | ||
onChange={search.onChange} | ||
/> | ||
<ToggleControl | ||
value={entities.value} | ||
onChange={entities.onChange} | ||
/> | ||
</> | ||
)} | ||
</CompositeEntitiesByKind>; | ||
} | ||
``` | ||
|
||
In the example above we are passing a `searchEntities` function that will be used to fetch the entities from the Rest | ||
API. This function is documented in the [api](./api.md) section. | ||
|
||
What's important to know is that the `queryArguments` do not include the `_fields` property because it's not a concern | ||
of the component decide which fields to retrieve, it's up to the consumer to decide which fields to retrieve. Anyhow, | ||
you can still override the given configuration, but you have to be aware this might produce an incorrect result if not | ||
done correctly. The component might pass arguments necessary to guarantee the consistency of the Set of the entities and | ||
the kind. | ||
|
||
In the following example we require `title` and `slug` fields to be retrieved from the Rest API. | ||
|
||
```typescript | ||
async (phrase, kind, queryArguments) => { | ||
const fields = ['title', 'slug']; | ||
return convertEntitiesToControlOptions( | ||
await searchEntities('post', kind, phrase, {...queryArguments, fields}), | ||
...fields | ||
) | ||
} | ||
``` | ||
|
||
The `entities` and `kind` properties are the initial configuration, they'll change depending on what happen within the | ||
respective `onChange` functions. | ||
|
||
The `children` function will receive the `entities`, `kind` and `search` properties; notice the `search` is a function, | ||
and it's up to the `SearchControl` to maintain the status of the value, the composite does hold the search value | ||
internally, but it does not share it outside. | ||
|
||
You're not forced to consume all the properties given to the `children`, you can remove the `SearchControl` | ||
and let the user select only the retrieved entities. Moreover, you can also not have the Kind control at all and just | ||
allow the search for one single kind. | ||
|
||
In the example below we only allow to select the entities belonging to the `page` or `landing-page` kind not permitting | ||
the user to switch between them. | ||
|
||
```jsx | ||
import { CompositeEntitiesByKind } from 'wp-entities-search'; | ||
|
||
export function MyComponent(props) { | ||
return <CompositeEntitiesByKind | ||
searchEntities={ | ||
async (phrase, kind, queryArguments) => convertEntitiesToControlOptions( | ||
await searchEntities('post', kind, phrase, queryArguments), | ||
'title', | ||
'id' | ||
) | ||
} | ||
entities={{ | ||
value: new Set([13, 24, 55]), | ||
onChange: (value) => { | ||
// Do something with the selected entities | ||
} | ||
}} | ||
kind={{ | ||
value: new Set(['page', 'landing-page']), | ||
options: Set([]), | ||
onChange={() => {}} | ||
}} | ||
> | ||
{({ entities, _, search }) => ( | ||
<> | ||
<SearchControl | ||
onChange={search.onChange} | ||
/> | ||
<ToggleControl | ||
value={entities.value} | ||
onChange={entities.onChange} | ||
/> | ||
</> | ||
)} | ||
</CompositeEntitiesByKind>; | ||
} | ||
``` | ||
|
||
Obviously depending on what you want to achieve you can use different _Base Components_ or create new ones, as mentioned | ||
above the package comes with a set of _Base Components_ that can be used out of the box. | ||
|
||
## About Singular Base Components | ||
|
||
The _Composite Component_ always give a collection of Entities and Kind even though you are consuming a Single* _Base Component_. | ||
|
||
The Singular Components always get a single value, therefore you have to consider to extract the first element out of the `Set`. | ||
|
||
```jsx | ||
import { CompositeEntitiesByKind } from 'wp-entities-search'; | ||
|
||
export function MyComponent(props) { | ||
return <CompositeEntitiesByKind | ||
/* ... */ | ||
> | ||
{({ entities, kind, search }) => ( | ||
<> | ||
<RadioControl | ||
value={kind.value.first()} | ||
options={Array.from(kind.options)} | ||
onChange={kind.onChange} | ||
/> | ||
/* ... */ | ||
</> | ||
)} | ||
</CompositeEntitiesByKind>; | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# Development | ||
|
||
The first thing to do is to clone the repository and install the dependencies: | ||
|
||
```bash | ||
$ git clone git@github.com:widoz/wp-entities-search.git | ||
$ cd wp-entities-search | ||
$ composer install | ||
$ yarn install | ||
``` | ||
|
||
After we have installed the dependencies we need to build the assets: | ||
|
||
```bash | ||
$ yarn build | ||
``` | ||
|
||
or alternatively we can run the dev script to compile the assets with source maps: | ||
|
||
```bash | ||
$ yarn build:dev | ||
``` | ||
|
||
## The Environment | ||
|
||
The project is making use of `@wordress/env` as a local environment to develop the solution. | ||
|
||
To start the environment we need to run the following command: | ||
|
||
```bash | ||
$ yarn wp-env start | ||
``` | ||
|
||
This will install WordPress and will set the current project as a plugin. The package contain a E2E module used by the plugin to help with the development. The module register two blocks; one for the Custom Post Type and one for the Custom Taxonomy. | ||
|
||
The WordPress installation is provided without content. To add some we can run the following commands from the root of the project: | ||
|
||
```bash | ||
./scripts/create-posts.sh --num 20 --title Post --type post | ||
./scripts/create-posts.sh --num 20 --title Page --type page | ||
./scripts/create-terms.sh --num 20 --name Category --taxonomy category | ||
./scripts/create-terms.sh --num 20 --name Tag --taxonomy post_tag | ||
``` | ||
|
||
## Basic Concepts | ||
|
||
The package implement a Value Object to handle immutability called `Set`. This is the main implementation used by the package to work with external data. Whenever a data is fetched from the WordPress API it will be wrapped in a `Set` object, but also all components and functions deal with this implementation. In a summary, every data entering the package will be wrapped in a `Set` object. | ||
|
||
Another commonly used data structure is the `ControlOption` which is not directly consumed but define the type of the `Set`. It should not be consumed directly but through the `Set` api. | ||
|
||
## Usage Examples | ||
|
||
As mentioned above the package have a E2E module which register two blocks to test the functionality. The first block is a Custom Post Type and the second one is a Custom Taxonomy. The two blocks are using the same component to render the results, you can get some insight about the usage from them. |
Oops, something went wrong.