@eightshift/frontend-libs
Version:
A collection of useful frontend utility modules. powered by Eightshift
139 lines (122 loc) • 5.44 kB
JavaScript
import { truncate, unescapeHTML } from '@eightshift/ui-components/utilities';
import { addQueryArgs } from '@wordpress/url';
import apiFetch from '@wordpress/api-fetch';
function buildBaseParams(perPage, fields, searchColumns, additionalParams) {
const params = { per_page: perPage };
if (fields?.length > 0) {
params['_fields'] = fields;
}
if (searchColumns?.length > 0) {
params.search_columns = Array.isArray(searchColumns) ? searchColumns.join(',') : searchColumns;
}
return { ...params, ...additionalParams };
}
/**
* Returns a function that fetches data from WordPress REST API.
*
* @param {string} endpoint - Endpoint to fetch from.
* @param {Object} options - Additional options.
* @param {Function} [options.processId] - Function to process the ID. `(itemData: Object) => id: string | Number`
* @param {Function} [options.processLabel] - Function to process the label. `(itemData: Object) => label: string`
* @param {string} [options.labelProp='title'] - Property to use as a label. Overriden by `processLabel`.
* @param {Function} [options.processMetadata] - Function to process the metadata. `(itemData: Object) => metadata: Object`
* @param {number} [options.perPage=30] - Number of items to fetch per page.
* @param {string} [options.routePrefix='wp/v2'] - Route prefix for the API.
* @param {string} [options.fields='id,title'] - A comma-separated list of field names to fetch from the API. Good to include as it makes the query faster and the output terser.
* @param {SearchColumnsConfig} [options.searchColumns] - Allows narrowing the search scope.
* @param {boolean} [options.noUnescapeTitle] - If `true`, the post title will not unescape HTML entities.
* @param {Number?} [options.truncateTitle=32] - If set, the title will be truncated to this length.
*
* @returns {Function} The `(searchText, [abortSignal]) => Promise` function.
*
* @typedef {'post_title' | 'post_excerpt' | 'post_content'} SearchColumnsConfig
*
* @example
* const fetchFn = getFetchWpApi('pages');
* const data = await fetchFn('search text');
* const json = await data.json();
*
* console.log(json);
*
* @example
* <LinkInput
* value={buttonUrl}
* onChange={({url}) => setAttributes({ [getAttrKey('buttonUrl', attributes, manifest)]: url })}
* fetchSuggestions={getFetchWpApi('search')}
* />
*
*/
export function fetchFromWpRest(endpoint, options = {}) {
const { processId = ({ id }) => id, labelProp = 'title', processLabel = (item) => item[labelProp], processMetadata = () => null, perPage = 30, routePrefix = 'wp/v2', fields = 'id,title', searchColumns, noUnescapeTitle = false, truncateTitle = 32, ...additionalParams } = options;
return async (searchText = '', abortSignal) => {
const params = buildBaseParams(perPage, fields, searchColumns, additionalParams);
if (searchText?.length > 0) {
params.search = searchText;
}
const newData = await apiFetch({
path: addQueryArgs(`${routePrefix}/${endpoint}/`, params),
signal: abortSignal,
});
return newData.map((item) => {
const rawLabel = !noUnescapeTitle ? unescapeHTML(processLabel(item)) : processLabel(item);
const truncatedLabel = Number.isInteger(truncateTitle) && truncateTitle > 0 ? truncate(rawLabel, truncateTitle) : rawLabel;
return {
label: truncatedLabel,
value: processId(item),
metadata: processMetadata(item),
};
});
};
}
export const wpSearchRoute = fetchFromWpRest('search', {
processId: ({ url }) => url,
labelProp: 'title',
processMetadata: ({ type, subtype }) => ({ type, subtype }),
perPage: 5,
searchColumns: 'post_title',
fields: 'id,title,type,subtype,url',
type: 'post',
_locale: 'user',
});
/**
* Returns a function that fetches data from WordPress REST API.
*
* @param {string} endpoint - Endpoint to fetch from.
* @param {Object} options - Additional options.
* @param {number} [options.perPage=30] - Number of items to fetch per page.
* @param {string} [options.routePrefix='wp/v2'] - Route prefix for the API.
* @param {string} [options.fields='id,title'] - A comma-separated list of field names to fetch from the API. Good to include as it makes the query faster and the output terser.
* @param {string} [options.noSearch=false] - If `true`, only the URL will be returned, without the search query support.
* @param {SearchColumnsConfig} [options.searchColumns] - Allows narrowing the search scope.
*
* @returns {Function} The `(searchText) => url: string` function, or URL as `string` if `noSearch` is set.
*
* @typedef {'post_title' | 'post_excerpt' | 'post_content'} SearchColumnsConfig
*
* @example
* const data = await fetch(buildWpRestUrl('pages'));
* const json = await data.json();
*
* console.log(json);
*
* @example
* <AsyncSelect
* value={loremIpsum}
* onChange={(value) => setAttributes({ [getAttrKey('loremIpsum', attributes, manifest)]: value })}
* fetchUrl={buildWpRestUrl('pages')}
* />
*
*/
export function buildWpRestUrl(endpoint, options = {}) {
const { perPage = 30, routePrefix = 'wp/v2', fields = 'id,title', searchColumns, noSearch, ...additionalParams } = options;
const params = buildBaseParams(perPage, fields, searchColumns, additionalParams);
if (noSearch) {
return addQueryArgs(`${routePrefix}/${endpoint}/`, params);
}
return (searchText) => {
if (searchText?.length > 0) {
params.search = searchText;
}
return addQueryArgs(`${routePrefix}/${endpoint}/`, params);
};
}