diff --git a/.babelrc b/.babelrc new file mode 100755 index 0000000..48f7e45 --- /dev/null +++ b/.babelrc @@ -0,0 +1,5 @@ +{ + "presets": [ + "@10up/babel-preset-default" + ] +} diff --git a/.gitattributes b/.gitattributes index 8fb31de..5578f7a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -17,6 +17,7 @@ /.nvmrc export-ignore /.stylelintignore export-ignore /.stylelintrc export-ignore +/.wp-env.json export-ignore /babel.config.js export-ignore /CHANGELOG.md export-ignore /CODE_OF_CONDUCT.md export-ignore diff --git a/.github/workflows/cypress-tests.yml b/.github/workflows/cypress-tests.yml index 6f81857..b7c2f66 100644 --- a/.github/workflows/cypress-tests.yml +++ b/.github/workflows/cypress-tests.yml @@ -174,8 +174,14 @@ jobs: ${{ github.workspace }}/tests/cypress/videos/ ${{ github.workspace }}/tests/cypress/logs/ + - name: Delete Elasticsearch search templates + if: always() + run: | + ./bin/wp-env-cli tests-wordpress "wp --allow-root plugin activate elasticpress elasticpress-labs" + ./bin/wp-env-cli tests-wordpress "wp --allow-root elasticpress-tests delete-all-search-templates" + - name: Delete Elasticsearch indices if: always() run: | ./bin/wp-env-cli tests-wordpress "wp --allow-root plugin activate elasticpress elasticpress-labs" - ./bin/wp-env-cli tests-wordpress "wp --allow-root elasticpress-tests delete-all-indices" \ No newline at end of file + ./bin/wp-env-cli tests-wordpress "wp --allow-root elasticpress-tests delete-all-indices" diff --git a/.wp-env.json b/.wp-env.json index d36a180..7b6644e 100644 --- a/.wp-env.json +++ b/.wp-env.json @@ -15,6 +15,7 @@ "mappings": { "wp-content/plugins/elasticpress-labs": ".", ".htaccess": "./tests/cypress/wordpress-files/.htaccess", + "wp-content/mu-plugins/delete-all-templates.php": "./tests/cypress/wordpress-files/test-mu-plugins/delete-all-templates.php", "wp-content/mu-plugins/unique-index-name.php": "./tests/cypress/wordpress-files/test-mu-plugins/unique-index-name.php" } } diff --git a/assets/js/search-templates/apps/search-templates-list.js b/assets/js/search-templates/apps/search-templates-list.js new file mode 100644 index 0000000..cb0dc6b --- /dev/null +++ b/assets/js/search-templates/apps/search-templates-list.js @@ -0,0 +1,73 @@ +/** + * WordPress Dependencies. + */ +import { createInterpolateElement, WPElement } from '@wordpress/element'; +import { Panel, Spinner } from '@wordpress/components'; +import { __, sprintf } from '@wordpress/i18n'; + +/** + * Internal dependencies. + */ +import { useSearchTemplate } from '../provider'; +import TemplateRow from '../components/template-row'; +import NewTemplateRow from '../components/new-template-row'; +import { endpointExample, searchApiDocUrl } from '../config'; + +/** + * Search Templates app. + * + * @returns {WPElement} App element. + */ +export default () => { + const { isLoading, templates } = useSearchTemplate(); + + return ( + <> +

+ {createInterpolateElement( + __( + 'Search templates are Elasticsearch queries stored in ElasticPress.io servers used by the Search API. Please note that all the API fields are still available for custom search templates. Your templates do not to differ in post types, offset, pagination arguments, or even filters, as for those you can still use query parameters. The templates can be used for searching in different fields or applying different scores, for instance.', + 'elasticpress-labs', + ), + { a: }, // eslint-disable-line jsx-a11y/anchor-has-content, jsx-a11y/control-has-associated-label + )} +

+

+ {createInterpolateElement( + sprintf( + __( + 'Once you have a search template saved, you can start sending requests to your endpoint URL below. Your template needs to have {{ep_placeholder}} in all places where the search term needs to be used.', + 'elasticpress-labs', + ), + endpointExample, + ), + { code: }, + )} +

+

+ {createInterpolateElement( + sprintf( + __('Endpoint URL: %s', 'elasticpress-labs'), + endpointExample, + ), + { strong: , code: }, + )} +

+ + {isLoading ? ( +
+ {__('Loading...', 'elasticpress-labs')} + +
+ ) : ( + <> + {Object.keys(templates).map((templateName) => ( + + ))} + + + )} +
+ + ); +}; diff --git a/assets/js/search-templates/components/new-template-row.js b/assets/js/search-templates/components/new-template-row.js new file mode 100644 index 0000000..fbea861 --- /dev/null +++ b/assets/js/search-templates/components/new-template-row.js @@ -0,0 +1,89 @@ +/** + * WordPress Dependencies. + */ +import { Button, Flex, Notice, PanelBody, PanelRow, TextControl } from '@wordpress/components'; +import { useState, WPElement } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies. + */ +import { useSearchTemplate, useSearchTemplateDispatch } from '../provider'; +import { useSettingsScreen } from '../../settings-screen'; +import TemplateField from './template-field'; + +/** + * New Template Row component. + * + * @returns {WPElement} + */ +export default () => { + const [name, setName] = useState(''); + const [template, setTemplate] = useState(''); + const [disabled, setDisabled] = useState(false); + + const { templates } = useSearchTemplate(); + const { saveTemplate } = useSearchTemplateDispatch(); + const { createNotice } = useSettingsScreen(); + + const onAddNewTemplate = () => { + saveTemplate(name, template) + .then(() => { + setName(''); + setTemplate(''); + createNotice('success', __('Template saved.', 'elasticpress-labs')); + }) + .catch((error) => { + createNotice( + 'error', + error.message || + __('Could not save the template. Please try again.', 'elasticpress-labs'), + ); + // eslint-disable-next-line no-console + console.error(__('ElasticPress Labs Error: ', 'elasticpress-labs'), error); + }); + }; + + const onChangeName = (newName) => { + setName(newName); + setDisabled(Object.keys(templates).includes(newName)); + }; + + return ( + + + + {name && disabled && ( + + {__( + 'This name is already in use. You can change the existing template instead.', + 'elasticpress-labs', + )} + + )} + + + + + + + + + ); +}; diff --git a/assets/js/search-templates/components/template-field.js b/assets/js/search-templates/components/template-field.js new file mode 100644 index 0000000..330c8fc --- /dev/null +++ b/assets/js/search-templates/components/template-field.js @@ -0,0 +1,80 @@ +/** + * WordPress Dependencies. + */ +import { BaseControl, Button, Flex, Notice } from '@wordpress/components'; +import { createInterpolateElement, WPElement } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies. + */ +import { defaultTemplate } from '../config'; + +/** + * Template Field component. + * + * @param {object} props Component props. + * @param {string} props.value The search template as a string. + * @param {Function} props.onChange Function to be executed when the value changes. + * @param {boolean} props.disabled If the field should be enabled or not. + * @returns {WPElement} + */ +export default ({ value, onChange, disabled }) => { + const isValueValidJson = () => { + if (!value) { + return true; + } + + try { + return JSON.parse(value) && !!value; + } catch (e) { + return false; + } + }; + + return ( + {{ep_placeholder}}
, so it can be replaced by the actual search term.', + 'elasticpress-labs', + ), + { code: }, + )} + > + {isValueValidJson() || ( + + {__('This does not seem to be a valid JSON object.', 'elasticpress-labs')} + + )} + + + {__('Template', 'elasticpress-labs')} + + {defaultTemplate && ( + + )} + +