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
156 changes: 85 additions & 71 deletions docs/7.javascript-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ You can read more about installation and setup in the [package readme](../packag

## Core API Functions

### getAbilities()
### `getAbilities( args = {} )`

Returns an array of all registered abilities (both server-side and client-side).

**Parameters:** None
**Parameters:** `args` (object, optional) - Query arguments to filter abilities. Supported arguments:

- `category` (string) - Filter abilities by category slug

**Returns:** `Promise<Array>` - Array of ability objects

Expand All @@ -34,33 +36,40 @@ console.log(`Found ${abilities.length} abilities`);
abilities.forEach(ability => {
console.log(`${ability.name}: ${ability.description}`);
});

// Get abilities in a specific category
const dataAbilities = await getAbilities( { category: 'data-retrieval' } );

console.log( `Found ${ dataAbilities.length } data retrieval abilities` );
```

### getAbility(name)
### getAbility( name )

Retrieves a specific ability by name.

**Parameters:**

- `name` (string) - The ability name (e.g., 'my-plugin/get-posts')

**Returns:** `Promise<Object|null>` - The ability object or null if not found

**Example:**

```javascript
const ability = await getAbility('my-plugin/get-site-info');
if (ability) {
console.log('Label:', ability.label);
console.log('Description:', ability.description);
console.log('Input Schema:', ability.input_schema);
const ability = await getAbility( 'my-plugin/get-site-info' );
if ( ability ) {
console.log( 'Label:', ability.label );
console.log( 'Description:', ability.description );
console.log( 'Input Schema:', ability.input_schema );
}
```

### executeAbility(name, input)
### `executeAbility( name, input = null )`

Executes an ability with the provided input data.

**Parameters:**

- `name` (string) - The ability name
- `input` (any, optional) - Input data for the ability

Expand All @@ -70,22 +79,23 @@ Executes an ability with the provided input data.

```javascript
// Execute without input
const siteTitle = await executeAbility('my-plugin/get-site-title');
console.log('Site:', siteTitle);
const siteTitle = await executeAbility( 'my-plugin/get-site-title' );
console.log( 'Site:', siteTitle );

// Execute with input parameters
const posts = await executeAbility('my-plugin/get-posts', {
category: 'news',
limit: 5
});
posts.forEach(post => console.log(post.title));
const posts = await executeAbility( 'my-plugin/get-posts', {
category: 'news',
limit: 5,
} );
posts.forEach( ( post ) => console.log( post.title ) );
```

### registerAbility(ability)
### `registerAbility( ability )`

Registers a client-side ability that runs in the browser.

**Parameters:**

- `ability` (object) - The ability configuration object

**Returns:** `void`
Expand All @@ -94,63 +104,67 @@ Registers a client-side ability that runs in the browser.

```javascript
// showNotification function
const showNotification = (message) => {
new Notification(message);
const showNotification = ( message ) => {
new Notification( message );
return { success: true, displayed: message };
}
};

// Register a notification ability which calls the showNotification function
registerAbility({
name: 'my-plugin/show-notification',
label: 'Show Notification',
description: 'Display a notification message to the user',
input_schema: {
type: 'object',
properties: {
message: { type: 'string' },
type: { type: 'string', enum: ['success', 'error', 'warning', 'info'] }
},
required: ['message']
},
callback: async ({ message, type = 'info' }) => {
// Show browser notification
if (!("Notification" in window)) {
alert("This browser does not support desktop notification");
return { success: false, error: 'Browser does not support notifications' };
}
if (Notification.permission !== 'granted') {
Notification.requestPermission().then((permission) => {
if (permission === "granted") {
return showNotification(message);
}
});
}
return showNotification(message);
registerAbility( {
name: 'my-plugin/show-notification',
label: 'Show Notification',
description: 'Display a notification message to the user',
input_schema: {
type: 'object',
properties: {
message: { type: 'string' },
type: { type: 'string', enum: [ 'success', 'error', 'warning', 'info' ] },
},
permissionCallback: () => {
return !!wp.data.select('core').getCurrentUser();
required: [ 'message' ],
},
callback: async ( { message, type = 'info' } ) => {
// Show browser notification
if ( ! ( 'Notification' in window ) ) {
alert( 'This browser does not support desktop notification' );
return {
success: false,
error: 'Browser does not support notifications',
};
}
});
if ( Notification.permission !== 'granted' ) {
Notification.requestPermission().then( ( permission ) => {
if ( permission === 'granted' ) {
return showNotification( message );
}
} );
}
return showNotification( message );
},
permissionCallback: () => {
return !! wp.data.select( 'core' ).getCurrentUser();
},
} );

// Use the registered ability
const result = await executeAbility('my-plugin/show-notification', {
message: 'Hello World!',
type: 'success'
});
const result = await executeAbility( 'my-plugin/show-notification', {
message: 'Hello World!',
type: 'success',
} );
```

### unregisterAbility(name)
### `unregisterAbility( name )`

Removes a previously registered client-side ability.

**Parameters:**

- `name` (string) - The ability name to unregister

**Example:**

```javascript
// Unregister an ability
unregisterAbility('my-plugin/old-ability');
unregisterAbility( 'my-plugin/old-ability' );
```

## Error Handling
Expand All @@ -159,21 +173,21 @@ All functions return promises that may reject with specific error codes:

```javascript
try {
const result = await executeAbility('my-plugin/restricted-action', input);
console.log('Success:', result);
} catch (error) {
switch (error.code) {
case 'ability_permission_denied':
console.error('Permission denied:', error.message);
break;
case 'ability_invalid_input':
console.error('Invalid input:', error.message);
break;
case 'rest_ability_not_found':
console.error('Ability not found:', error.message);
break;
default:
console.error('Execution failed:', error.message);
}
const result = await executeAbility( 'my-plugin/restricted-action', input );
console.log( 'Success:', result );
} catch ( error ) {
switch ( error.code ) {
case 'ability_permission_denied':
console.error( 'Permission denied:', error.message );
break;
case 'ability_invalid_input':
console.error( 'Invalid input:', error.message );
break;
case 'rest_ability_not_found':
console.error( 'Ability not found:', error.message );
break;
default:
console.error( 'Execution failed:', error.message );
}
}
```
75 changes: 69 additions & 6 deletions packages/client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,21 @@ function MyComponent() {

### Functions

#### `getAbilities(): Promise<Ability[]>`
#### `getAbilities( args: AbilitiesQueryArgs = {} ): Promise<Ability[]>`

Returns all registered abilities. Automatically handles pagination to fetch all abilities across multiple pages if needed.
Returns all registered abilities. Optionally filter by category slug. Automatically handles pagination to fetch all abilities across multiple pages if needed.

```javascript
// Get all abilities
const abilities = await getAbilities();
console.log( `Found ${ abilities.length } abilities` );

// Get abilities in a specific category
const dataAbilities = await getAbilities( { category: 'data-retrieval' } );
console.log( `Found ${ dataAbilities.length } data retrieval abilities` );
```

#### `getAbility(name: string): Promise<Ability | null>`
#### `getAbility( name: string ): Promise<Ability | null>`

Returns a specific ability by name, or null if not found.

Expand All @@ -95,7 +100,33 @@ if ( ability ) {
}
```

#### `executeAbility(name: string, input?: Record<string, any>): Promise<any>`
#### `registerAbility( ability: Ability ): void`

Registers a client-side ability. Client abilities are executed locally in the browser and must include a callback function and a valid category.

```javascript
import { registerAbility } from '@wordpress/abilities';

registerAbility( {
name: 'my-plugin/navigate',
label: 'Navigate to URL',
description: 'Navigates to a URL within WordPress admin',
category: 'navigation',
input_schema: {
type: 'object',
properties: {
url: { type: 'string' },
},
required: [ 'url' ],
},
callback: async ( { url } ) => {
window.location.href = url;
return { success: true };
},
} );
```

#### `executeAbility( name: string, input?: Record<string, any> ): Promise<any>`

Executes an ability with optional input parameters. The HTTP method is automatically determined based on the ability's annotations:

Expand All @@ -119,8 +150,40 @@ const result = await executeAbility( 'my-plugin/create-item', {

When using with `@wordpress/data`:

- `getAbilities()` - Returns all abilities from the store
- `getAbility(name)` - Returns a specific ability from the store
- `getAbilities( args: AbilitiesQueryArgs = {} )` - Returns all abilities from the store, optionally filtered by query arguments
- `getAbility( name: string )` - Returns a specific ability from the store

```javascript
import { useSelect } from '@wordpress/data';
import { store as abilitiesStore } from '@wordpress/abilities';

function MyComponent() {
// Get all abilities
const allAbilities = useSelect(
( select ) => select( abilitiesStore ).getAbilities(),
[]
);

// Get abilities in a specific category
const dataAbilities = useSelect(
( select ) =>
select( abilitiesStore ).getAbilities( { category: 'data-retrieval' } ),
[]
);

return (
<div>
<h2>All Abilities ({ allAbilities.length })</h2>
<h2>Data Retrieval Abilities</h2>
<ul>
{ dataAbilities.map( ( ability ) => (
<li key={ ability.name }>{ ability.label }</li>
) ) }
</ul>
</div>
);
}
```

## Development and Testing

Expand Down
Loading