From 6533af8757f4cfe54a2adeec05706445b400c610 Mon Sep 17 00:00:00 2001 From: mehidi258 Date: Tue, 8 Feb 2022 22:48:27 +0600 Subject: [PATCH 01/63] Add weghting screen page in react --- assets/css/dashboard.css | 109 ++++++++- assets/js/weighting/components/field-group.js | 181 ++++++++++++++ assets/js/weighting/components/field-item.js | 102 ++++++++ assets/js/weighting/components/meta-Item.js | 72 ++++++ .../js/weighting/components/undo-changes.js | 20 ++ assets/js/weighting/dummyData.js | 224 ++++++++++++++++++ assets/js/weighting/index.js | 214 +++++++++++++++++ assets/js/weighting/utils.js | 7 + includes/classes/Feature/Search/Weighting.php | 92 +++---- package-lock.json | 45 ++-- package.json | 3 +- 11 files changed, 982 insertions(+), 87 deletions(-) create mode 100644 assets/js/weighting/components/field-group.js create mode 100644 assets/js/weighting/components/field-item.js create mode 100644 assets/js/weighting/components/meta-Item.js create mode 100644 assets/js/weighting/components/undo-changes.js create mode 100644 assets/js/weighting/dummyData.js create mode 100644 assets/js/weighting/index.js create mode 100644 assets/js/weighting/utils.js diff --git a/assets/css/dashboard.css b/assets/css/dashboard.css index 19f2d6bbb3..72f24ca62d 100644 --- a/assets/css/dashboard.css +++ b/assets/css/dashboard.css @@ -658,18 +658,97 @@ h2.ep-list-features { * Weighting */ -.weighting-settings .postbox { +.weighting-settings { box-sizing: border-box; max-width: 650px; - & * { - box-sizing: border-box; + & .postbox { + + & .components-range-control__number { + display: none; + } + + & .postbox__header { + align-items: center; + border-bottom: 1px solid #c3c4c7; + display: flex; + } + + & .undo-changes { + background-color: transparent; + border: 0; + color: #1e8cbe; + cursor: pointer; + margin-left: auto; + padding: 0 10px; + + &:hover { + text-decoration: underline; + } + } + + & * { + box-sizing: border-box; + } + } + + & .add-meta-key-wrap { + align-items: center; + display: flex; + + & legend { + top: 0; + } + } + + & .add-meta-key { + width: 300px; + } + + & .submit__button { + align-items: center; + background: #fff; + bottom: 0; + display: flex; + padding: 10px; + position: sticky; + + & button { + margin-right: 10px; + } + } + +} + +.components-range-control__wrapper { + min-width: 100px; + + & span:last-child { + background-color: #1e8cbe; + pointer-events: none; } } +div.components-base-control__field { + align-items: center; + display: flex; + height: 20px; + margin: 0; + + & .components-base-control__label { + margin: 0; + } +} + +.field-item .components-base-control__help { + margin-top: -10px; + padding: 0; +} + .weighting-settings .postbox h2.hndle { color: #444; cursor: inherit; + width: 110px; } .weighting-settings fieldset { @@ -702,9 +781,26 @@ h2.ep-list-features { background: #f9f9f9; } -.weighting-settings .searchable { +.field-item { + align-items: center; + display: flex; +} + +.remove-meta-item { + cursor: pointer; + + & path { + fill: #d84440; + } +} + +.field-item p { display: inline-block; - width: 120px; + padding: 0 10px; + + & a { + text-decoration: none; + } } .weighting-settings .weighting { @@ -713,7 +809,6 @@ h2.ep-list-features { } .weighting-settings .weighting label { - margin-right: 10px; min-width: 80px; } @@ -724,7 +819,7 @@ h2.ep-list-features { height: 1em; margin: 0; vertical-align: middle; - width: 200px; + width: 120px; } .weighting-settings input[type="range"]:focus { diff --git a/assets/js/weighting/components/field-group.js b/assets/js/weighting/components/field-group.js new file mode 100644 index 0000000000..57777abce8 --- /dev/null +++ b/assets/js/weighting/components/field-group.js @@ -0,0 +1,181 @@ +/** + * Wordpress Dependecies. + */ +import { useState } from '@wordpress/element'; +import AsyncSelect from 'react-select/async'; +import { __ } from '@wordpress/i18n'; +import PropTypes from 'prop-types'; + +/** + * Internal Dependencies. + */ +import FieldItem from './field-item'; +import MetaItem from './meta-Item'; +import { sortListByOrder } from '../utils'; + +const FieldGroup = ({ + postType, + onChangeHandler, + getCurrentFormData, + formData, + setFormData, + undoHandler, + savedData, +}) => { + const [selectedValue, setSelectedValue] = useState(null); + + // handle selection + const handleChange = (value) => { + let currentFormData = getCurrentFormData(postType.name); + if (currentFormData.metas.findIndex((metaItem) => metaItem.name === value.name) === -1) { + currentFormData = { + ...currentFormData, + metas: [...currentFormData.metas, value], + }; + } + const excludedFormData = formData.filter((item) => item.name !== postType.name); + const newFormData = [...excludedFormData, currentFormData]; + setFormData(sortListByOrder(newFormData)); + setSelectedValue(value); + }; + + // load options using API call + const loadOptions = (inputValue) => { + return fetch( + `https://jsonplaceholder.typicode.com/posts?search=${inputValue}$post_type=${postType.name}`, + ) + .then((res) => res.json()) + .then((result) => { + const newResult = []; + result.forEach((item) => { + newResult.push({ + name: item.title, + searchable: false, + weight: 10, + }); + }); + return newResult; + }); + }; + + const removeMetaItem = (metaName) => { + const newCurrentFormData = { + ...getCurrentFormData(postType.name), + metas: getCurrentFormData(postType.name).metas.filter((item) => item.name !== metaName), + }; + + const excludedFormData = formData.filter((item) => item.name !== postType.name); + const newFormData = [...excludedFormData, newCurrentFormData]; + setFormData(sortListByOrder(newFormData)); + }; + + const currentOriginalData = savedData.find((item) => item.name === postType.name); + + return ( + <> +
+

{__('Attributes', 'elasticpress')}

+
+ {postType.attributes.map((attribute, index) => { + const fieldType = 'attributes'; + const isAttributeChanged = + JSON.stringify(attribute) !== + JSON.stringify(currentOriginalData[fieldType][index]); + return ( + + ); + })} +
+
+
+

{__('Taxonomies', 'elasticpress')}

+
+ {postType.taxonomies.map((taxonomy, index) => { + const fieldType = 'taxonomies'; + const isAttributeChanged = + JSON.stringify(taxonomy) !== + JSON.stringify(currentOriginalData[fieldType][index]); + + return ( + + ); + })} +
+
+
+

{__('Meta to be indexed', 'elasticpress')}

+
+ {getCurrentFormData(postType.name) && + getCurrentFormData(postType.name).metas && + getCurrentFormData(postType.name).metas.map((meta, index) => { + const fieldType = 'metas'; + const isAttributeChanged = + JSON.stringify(meta) !== + JSON.stringify(currentOriginalData[fieldType][index]); + return ( + + ); + })} +
+ {__('Add Meta Key:', 'elasticpress')} +
+ e.name} + getOptionValue={(e) => e.name} + loadOptions={loadOptions} + // onInputChange={handleInputChange} + onChange={handleChange} + /> +
+
+
+
+ + ); +}; + +FieldGroup.propTypes = { + postType: PropTypes.object.isRequired, + onChangeHandler: PropTypes.func.isRequired, + getCurrentFormData: PropTypes.func.isRequired, + formData: PropTypes.array.isRequired, + setFormData: PropTypes.func.isRequired, + undoHandler: PropTypes.func.isRequired, + savedData: PropTypes.array.isRequired, +}; + +export default FieldGroup; diff --git a/assets/js/weighting/components/field-item.js b/assets/js/weighting/components/field-item.js new file mode 100644 index 0000000000..4e5c8baf45 --- /dev/null +++ b/assets/js/weighting/components/field-item.js @@ -0,0 +1,102 @@ +/** + * Wordpress Dependencies. + */ +import { RangeControl } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import PropTypes from 'prop-types'; + +/** + * Internal Dependecies. + */ +import UndoChanges from './undo-changes'; + +const FieldItem = (props) => { + const { + postType, + attribute, + fieldType, + onChangeHandler, + getCurrentFormData, + currentIndex, + undoHandler, + isAttributeChanged, + } = props; + + return ( +
+ {attribute.label} + +

+ +

+ + {getCurrentFormData(postType.name)[fieldType][currentIndex].indexable && ( +

+ +

+ )} + + {getCurrentFormData(postType.name)[fieldType][currentIndex].searchable && + getCurrentFormData(postType.name)[fieldType][currentIndex].indexable && ( +

+ + onChangeHandler(value, postType.name, fieldType, attribute.name) + } + min={1} + max={100} + /> +

+ )} + + {isAttributeChanged && } +
+ ); +}; + +FieldItem.propTypes = { + postType: PropTypes.object.isRequired, + attribute: PropTypes.object.isRequired, + fieldType: PropTypes.string.isRequired, + onChangeHandler: PropTypes.func.isRequired, + getCurrentFormData: PropTypes.func.isRequired, + currentIndex: PropTypes.number.isRequired, + undoHandler: PropTypes.func.isRequired, + isAttributeChanged: PropTypes.bool.isRequired, +}; + +export default FieldItem; diff --git a/assets/js/weighting/components/meta-Item.js b/assets/js/weighting/components/meta-Item.js new file mode 100644 index 0000000000..877dae5a00 --- /dev/null +++ b/assets/js/weighting/components/meta-Item.js @@ -0,0 +1,72 @@ +/** + * WordPress dependencies. + */ +import { RangeControl } from '@wordpress/components'; +import { closeSmall, Icon } from '@wordpress/icons'; +import { __ } from '@wordpress/i18n'; +import PropTypes from 'prop-types'; + +const MetaItem = ({ + postType, + attribute, + fieldType, + onChangeHandler, + getCurrentFormData, + currentIndex, + removeMetaItem, +}) => { + return ( +
+ + removeMetaItem(attribute.name)} icon={closeSmall} /> + + {attribute.name} + +

+ +

+ + {getCurrentFormData(postType.name)[fieldType][currentIndex].searchable && ( +

+ + onChangeHandler(value, postType.name, fieldType, attribute.name) + } + min={1} + max={100} + /> +

+ )} +
+ ); +}; + +MetaItem.propTypes = { + postType: PropTypes.object.isRequired, + attribute: PropTypes.object.isRequired, + fieldType: PropTypes.string.isRequired, + onChangeHandler: PropTypes.func.isRequired, + getCurrentFormData: PropTypes.func.isRequired, + currentIndex: PropTypes.number.isRequired, + removeMetaItem: PropTypes.func.isRequired, +}; + +export default MetaItem; diff --git a/assets/js/weighting/components/undo-changes.js b/assets/js/weighting/components/undo-changes.js new file mode 100644 index 0000000000..689d88147a --- /dev/null +++ b/assets/js/weighting/components/undo-changes.js @@ -0,0 +1,20 @@ +/** + * Wordpress Dependencies. + */ +import { __ } from '@wordpress/i18n'; +import PropTypes from 'prop-types'; + +const UndoChanges = ({ undoHandler, undoProps }) => { + return ( + + ); +}; + +UndoChanges.propTypes = { + undoHandler: PropTypes.func.isRequired, + undoProps: PropTypes.object.isRequired, +}; + +export default UndoChanges; diff --git a/assets/js/weighting/dummyData.js b/assets/js/weighting/dummyData.js new file mode 100644 index 0000000000..98e5482731 --- /dev/null +++ b/assets/js/weighting/dummyData.js @@ -0,0 +1,224 @@ +export const dummyData = [ + { + label: 'Posts', + name: 'post', + indexable: true, + order: 0, + + attributes: [ + { + label: 'Title', + name: 'post_title', + indexable: true, + searchable: true, + weight: 40, + }, + { + label: 'Content', + name: 'post_content', + indexable: false, + searchable: false, + weight: 1, + }, + { + label: 'Excerpt', + name: 'post_excerpt', + indexable: false, + searchable: false, + weight: 1, + }, + { + label: 'Author', + name: 'post_author', + indexable: false, + searchable: false, + weight: 1, + }, + ], + + taxonomies: [ + { + label: 'Categories', + name: 'post_categories', + indexable: false, + searchable: false, + weight: 1, + }, + { + label: 'Tags', + name: 'post_tags', + indexable: false, + searchable: false, + weight: 1, + }, + { + label: 'Formats', + name: 'post_formats', + indexable: false, + searchable: false, + weight: 1, + }, + ], + + metas: [ + // { + // name: 'example_key', + // searchable: false, + // weight: 10, + // }, + // { + // name: 'another_key', + // searchable: false, + // weight: 10, + // }, + // { + // name: 'one_more_key', + // searchable: false, + // weight: 10, + // }, + ], + }, + { + label: 'Pages', + name: 'page', + indexable: true, + order: 1, + + attributes: [ + { + label: 'Title', + name: 'post_title', + indexable: false, + searchable: false, + weight: 1, + }, + { + label: 'Content', + name: 'post_content', + indexable: false, + searchable: false, + weight: 1, + }, + { + label: 'Excerpt', + name: 'post_excerpt', + indexable: false, + searchable: false, + weight: 1, + }, + { + label: 'Author', + name: 'post_author', + indexable: false, + searchable: false, + weight: 1, + }, + ], + + taxonomies: [ + { + label: 'Categories', + name: 'post_categories', + indexable: false, + searchable: false, + weight: 1, + }, + { + label: 'Tags', + name: 'post_tags', + indexable: false, + searchable: false, + weight: 1, + }, + { + label: 'Formats', + name: 'post_formats', + indexable: false, + searchable: false, + weight: 1, + }, + ], + + metas: [], + }, + { + label: 'Product', + name: 'product', + indexable: true, + order: 2, + + attributes: [ + { + label: 'Title', + name: 'post_title', + indexable: false, + searchable: false, + weight: 1, + }, + { + label: 'Content', + name: 'post_content', + indexable: false, + searchable: false, + weight: 1, + }, + { + label: 'Excerpt', + name: 'post_excerpt', + indexable: false, + searchable: false, + weight: 1, + }, + { + label: 'Author', + name: 'post_author', + indexable: false, + searchable: false, + weight: 1, + }, + ], + + taxonomies: [ + { + label: 'Categories', + name: 'post_categories', + indexable: false, + searchable: false, + weight: 1, + }, + { + label: 'Tags', + name: 'post_tags', + indexable: false, + searchable: false, + weight: 1, + }, + { + label: 'Formats', + name: 'post_formats', + indexable: false, + searchable: false, + weight: 1, + }, + ], + metas: [], + }, +]; + +export const dummyMetaKeys = [ + { + name: 'example_key', + searchable: false, + weight: 10, + }, + { + name: 'another_key', + searchable: false, + weight: 10, + }, + { + name: 'one_more_key', + searchable: false, + weight: 10, + }, +]; diff --git a/assets/js/weighting/index.js b/assets/js/weighting/index.js new file mode 100644 index 0000000000..9c4d94daef --- /dev/null +++ b/assets/js/weighting/index.js @@ -0,0 +1,214 @@ +/** + * WordPress dependencies. + */ +import { render, WPElement, useState, useEffect } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal Dependencies. + */ +import FieldGroup from './components/field-group'; +import { dummyData } from './dummyData'; +import { sortListByOrder } from './utils'; + +/** + * component. + * + * @return {WPElement} Element. + */ +const App = () => { + const [formData, setFormData] = useState([]); + const [savedData, setSavedData] = useState([]); + const [isSaving, setIsSaving] = useState(false); + + const hasChanges = JSON.stringify(savedData) !== JSON.stringify(formData); + + /** + * Fetch api response on component mount and set instead of dummy data. + */ + useEffect(() => { + setTimeout(() => { + setSavedData(dummyData); + setFormData(dummyData); + }, 200); + }, []); + + /** + * Get currently editing form type + * + * @param {string} postType type of the wp post. + * @return {Object} currently editing form. + */ + const getCurrentFormData = (postType) => formData.find((item) => item.name === postType); + + /** + * Get currently editing form type + * + * @param {string} postType type of the wp post. + * @return {Object} currently editing form. + */ + const getSavedFormData = (postType) => savedData.find((item) => item.name === postType); + + const undoHandler = (props) => { + const { currentIndex, fieldType, postType, attribute } = props; + let currentFormData = getCurrentFormData(postType.name); + const savedFormData = getSavedFormData(postType.name); + + currentFormData = { + ...currentFormData, + [fieldType]: currentFormData[fieldType].map((item) => { + let newItem = item; + if (item.name === attribute.name) { + newItem = savedFormData[fieldType][currentIndex]; + } + return newItem; + }), + }; + + const excludeOldCurrentFormData = formData.filter((item) => item.name !== postType.name); + + const newFormData = [...excludeOldCurrentFormData, currentFormData]; + + setFormData(sortListByOrder(newFormData)); + }; + + /** + * Handle input changes. + * + * @param {Object} event react synthetic event + * @param {string} postType wp post type + * @param {string} type type of the field + * @param {string} attributeName field attribute name + */ + const onChangeHandler = (event, postType, type, attributeName) => { + let currentFormData = getCurrentFormData(postType); + + if (type === 'root') { + currentFormData = { ...currentFormData, indexable: !currentFormData.indexable }; + } else { + currentFormData = { + ...currentFormData, + [type]: currentFormData[type].map((item) => { + let newItem = item; + if (typeof event === 'number' && item.name === attributeName) { + newItem = { ...newItem, weight: event }; + } else if (item.name === attributeName) { + newItem = { ...newItem, [event.target.name]: !newItem[event.target.name] }; + } + return newItem; + }), + }; + } + + const excludeOldCurrentFormData = formData.filter((item) => item.name !== postType); + + const newFormData = [...excludeOldCurrentFormData, currentFormData]; + + setFormData(sortListByOrder(newFormData)); + }; + + /** + * Reset all changes of the form. + */ + const resetAllChanges = () => { + setFormData(sortListByOrder(dummyData)); + }; + + /** + * Handle for submission. + * + * @param {Object} event react synthetic event. + */ + const handleSubmit = (event) => { + event.preventDefault(); + setIsSaving(true); + + // do api request to save formData + setTimeout(() => { + console.log({ formData }); + }, 500); + + setIsSaving(false); + }; + + return ( +
+

{__('Manage Search Fields & Weighting', 'elasticpress')}

+
+

+ {__( + 'Adding more weight to an item will mean it will have more presence during searches. Add more weight to the items that are more important and need more prominence during searches. For example, adding more weight to the title attribute will cause search matches on the post title to apear mor prominently.', + 'elasticpress', + )} +

+

+ {__( + 'Important: If you enable or disable indexing for a field, you will need to refresh your index after saving your settings', + 'elasticpress', + )} +

+
+ {formData.map((postType) => ( +
+
+

{postType.label}

+
+ +
+ + {hasChanges && ( + + )} +
+ + {postType.indexable && ( + + )} +
+ ))} + +
+ + + + {hasChanges + ? __('Please re-sync your data after saving.', 'elasticpress') + : __('You have nothing to save.', 'elasticpress')} + +
+
+ ); +}; + +render(, document.getElementById('ep-weighting-screen')); diff --git a/assets/js/weighting/utils.js b/assets/js/weighting/utils.js new file mode 100644 index 0000000000..7b0847f5b3 --- /dev/null +++ b/assets/js/weighting/utils.js @@ -0,0 +1,7 @@ +/** + * Sort list by order. + * + * @param {Array} list items to bo sorted. + * @return {Array} sorted list by it's order. + */ +export const sortListByOrder = (list = []) => list.sort((a, b) => a.order - b.order); diff --git a/includes/classes/Feature/Search/Weighting.php b/includes/classes/Feature/Search/Weighting.php index 95902a8ea4..9b15194ec9 100644 --- a/includes/classes/Feature/Search/Weighting.php +++ b/includes/classes/Feature/Search/Weighting.php @@ -40,6 +40,32 @@ public function setup() { add_filter( 'ep_query_weighting_fields', [ $this, 'adjust_weight_for_cross_fields' ], 10, 5 ); } + /** + * Returns unique list of meta keys + * + * @param string $post_type Post type + * + * @return array + */ + public function get_meta_keys_for_post_type( $post_type ) { + + + $meta_keys = array(); + $posts = get_posts( array( 'post_type' => $post_type, 'limit' => -1 ) ); + + + foreach ( $posts as $post ) { + $post_meta_keys = get_post_custom_keys( $post->ID ); + var_dump($post_meta_keys); + $meta_keys = array_merge( $meta_keys, $post_meta_keys ); + } + + // Use array_unique to remove duplicate meta_keys that we received from all posts + // Use array_values to reset the index of the array + return array_values( array_unique( $meta_keys ) ); + + } + /** * Returns a grouping of all the fields that support weighting for the post type * @@ -211,58 +237,7 @@ public function add_weighting_submenu_page() { public function render_settings_page() { include EP_PATH . '/includes/partials/header.php'; ?>
- -

-

-

- -
- - - -
-

-
- -
-

-
- get_registered_feature( 'search' ); - - $post_types = $search->get_searchable_post_types(); - - $current_values = $this->get_weighting_configuration(); - - foreach ( $post_types as $post_type ) : - $fields = $this->get_weightable_fields_for_post_type( $post_type ); - $post_type_object = get_post_type_object( $post_type ); - ?> -
-

labels->menu_name ); ?>

- - render_settings_section( $post_type, $field_group, $current_values ); - endforeach; - ?> -
- -
+
-
+
+

+ id="" name="weighting[][][enabled]"> + +

+

- id="" name="weighting[][][enabled]"> - + id="" name="weighting[][][enabled]"> +

@@ -326,6 +306,8 @@ public function render_settings_section( $post_type, $field, $current_values ) { " name="weighting[][][weight]" >

+ +

Date: Wed, 9 Feb 2022 22:06:02 +0600 Subject: [PATCH 02/63] Fix weghting screen --- assets/css/dashboard.css | 8 ++++++-- assets/js/weighting/index.js | 37 +++++++++++++++++++++++++++++++++--- package-lock.json | 19 ++++++++---------- package.json | 1 + 4 files changed, 49 insertions(+), 16 deletions(-) diff --git a/assets/css/dashboard.css b/assets/css/dashboard.css index 72f24ca62d..1ac874893a 100644 --- a/assets/css/dashboard.css +++ b/assets/css/dashboard.css @@ -713,8 +713,12 @@ h2.ep-list-features { padding: 10px; position: sticky; - & button { - margin-right: 10px; + & .note { + margin-left: 10px; + } + + & .reset-all-changes-button { + margin-left: auto; } } diff --git a/assets/js/weighting/index.js b/assets/js/weighting/index.js index 9c4d94daef..57c1940568 100644 --- a/assets/js/weighting/index.js +++ b/assets/js/weighting/index.js @@ -114,6 +114,28 @@ const App = () => { setFormData(sortListByOrder(dummyData)); }; + /** + * Reset current form changes. + * + * @param {string} postType name of the post type. + */ + const resetCurrentFormChanges = (postType) => { + const savedFormData = getSavedFormData(postType); + const excludeOldCurrentFormData = formData.filter((item) => item.name !== postType); + + const newFormData = [...excludeOldCurrentFormData, savedFormData]; + + setFormData(sortListByOrder(newFormData)); + }; + + /** + * Check if current has changes. + * + * @param {string} postType name of the post type. + */ + const hasCurrentFormChanges = (postType) => + JSON.stringify(getCurrentFormData(postType)) !== JSON.stringify(getSavedFormData(postType)); + /** * Handle for submission. * @@ -166,11 +188,11 @@ const App = () => {
- {hasChanges && ( + {hasCurrentFormChanges(postType.name) && ( @@ -201,11 +223,20 @@ const App = () => { {isSaving ? __('Saving…', 'elasticpress') : __('Save changes', 'elasticpress')} - + {hasChanges ? __('Please re-sync your data after saving.', 'elasticpress') : __('You have nothing to save.', 'elasticpress')} + + ); diff --git a/package-lock.json b/package-lock.json index 42388a7cf2..66ae5bb6a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17087,22 +17087,19 @@ } }, "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", - "react-is": "^16.8.1" + "react-is": "^16.13.1" }, "dependencies": { - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" } } }, diff --git a/package.json b/package.json index 2dcb7ef8e0..2e9bb1cd8e 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "focus-trap-react": "^8.8.2", "jquery": "^3.6.0", "promise-polyfill": "^8.2.1", + "prop-types": "^15.8.1", "react": "^16.14.0", "react-beautiful-dnd": "^11.0.5", "react-dom": "^16.14.0", From d865cf059f4d62c9937f4c92592a2d9e12c7674d Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 4 Oct 2022 12:37:51 +1100 Subject: [PATCH 03/63] Refactor with core components. --- assets/css/dashboard.css | 292 --------- assets/css/weighting.css | 57 ++ .../components/common/undo-button.js | 16 + .../components/common/weight-control.js | 16 + assets/js/weighting/components/field-group.js | 181 ----- assets/js/weighting/components/field-item.js | 102 --- assets/js/weighting/components/meta-Item.js | 72 -- assets/js/weighting/components/post-type.js | 110 ++++ .../components/post-type/properties.js | 54 ++ .../components/post-type/property.js | 107 +++ assets/js/weighting/components/save.js | 31 + .../js/weighting/components/undo-changes.js | 20 - assets/js/weighting/dummyData.js | 6 +- assets/js/weighting/index.js | 226 ++----- assets/js/weighting/utils.js | 7 - includes/dashboard.php | 8 + package-lock.json | 616 ++++++++++++------ package.json | 7 +- 18 files changed, 853 insertions(+), 1075 deletions(-) create mode 100644 assets/css/weighting.css create mode 100644 assets/js/weighting/components/common/undo-button.js create mode 100644 assets/js/weighting/components/common/weight-control.js delete mode 100644 assets/js/weighting/components/field-group.js delete mode 100644 assets/js/weighting/components/field-item.js delete mode 100644 assets/js/weighting/components/meta-Item.js create mode 100644 assets/js/weighting/components/post-type.js create mode 100644 assets/js/weighting/components/post-type/properties.js create mode 100644 assets/js/weighting/components/post-type/property.js create mode 100644 assets/js/weighting/components/save.js delete mode 100644 assets/js/weighting/components/undo-changes.js delete mode 100644 assets/js/weighting/utils.js diff --git a/assets/css/dashboard.css b/assets/css/dashboard.css index 02310c9f49..0d8749570d 100644 --- a/assets/css/dashboard.css +++ b/assets/css/dashboard.css @@ -689,298 +689,6 @@ h2.ep-list-features { color: #d84440; } -/** - * Weighting - */ - -.weighting-settings { - box-sizing: border-box; - max-width: 650px; - - & .postbox { - - & .components-range-control__number { - display: none; - } - - & .postbox__header { - align-items: center; - border-bottom: 1px solid #c3c4c7; - display: flex; - } - - & .undo-changes { - background-color: transparent; - border: 0; - color: #1e8cbe; - cursor: pointer; - margin-left: auto; - padding: 0 10px; - - &:hover { - text-decoration: underline; - } - } - - & * { - box-sizing: border-box; - } - } - - & .add-meta-key-wrap { - align-items: center; - display: flex; - - & legend { - top: 0; - } - } - - & .add-meta-key { - width: 300px; - } - - & .submit__button { - align-items: center; - background: #fff; - bottom: 0; - display: flex; - padding: 10px; - position: sticky; - - & .note { - margin-left: 10px; - } - - & .reset-all-changes-button { - margin-left: auto; - } - } - -} - -.components-range-control__wrapper { - min-width: 100px; - - & span:last-child { - background-color: #1e8cbe; - pointer-events: none; - } -} - -div.components-base-control__field { - align-items: center; - display: flex; - height: 20px; - margin: 0; - - & .components-base-control__label { - margin: 0; - } -} - -.field-item .components-base-control__help { - margin-top: -10px; - padding: 0; -} - -.weighting-settings .postbox h2.hndle { - color: #444; - cursor: inherit; - width: 110px; -} - -.weighting-settings fieldset { - padding: 10px; -} - -.weighting-settings fieldset legend { - float: left; /* legend cannot use display */ - position: relative; - top: 5px; - width: 100px; -} - -.weighting-settings fieldset p { - display: inline-block; - float: left; - margin: 0; -} - -.weighting-settings .field-group { - margin: 10px 0 0; -} - -.weighting-settings .field-group h3 { - font-size: 1em; - margin: 10px; -} - -.weighting-settings .fields > fieldset:nth-of-type(odd) { - background: #f9f9f9; -} - -.field-item { - align-items: center; - display: flex; -} - -.remove-meta-item { - cursor: pointer; - - & path { - fill: #d84440; - } -} - -.field-item p { - display: inline-block; - padding: 0 10px; - - & a { - text-decoration: none; - } -} - -.weighting-settings .weighting { - align-items: center; - display: flex; -} - -.weighting-settings .weighting label { - min-width: 80px; -} - -.weighting-settings input[type="range"] { - -webkit-appearance: none; - background: transparent; - display: inline-block; - height: 1em; - margin: 0; - vertical-align: middle; - width: 120px; -} - -.weighting-settings input[type="range"]:focus { - outline: none; -} - -.weighting-settings input[type="range"]:disabled { - opacity: 0.5; - pointer-events: none; -} - -.weighting-settings input[type="range"]::-webkit-slider-runnable-track { - background: #ddd; - border: 0 solid #000; - border-radius: 1px; - box-shadow: 0 0 0 #000; - cursor: pointer; - height: 3px; - width: 100%; -} - -.weighting-settings input[type="range"]::-webkit-slider-thumb { - -webkit-appearance: none; - background: #1e8cbe; - border: 1px solid #1e8cbe; - border-radius: 25px; - box-shadow: 0 0 0 #000; - cursor: pointer; - height: 14px; - margin-top: -6px; - width: 14px; -} - -.weighting-settings input[type="range"]:focus::-webkit-slider-runnable-track { - background: #ddd; -} - -.weighting-settings input[type="range"]:focus::-webkit-slider-thumb { - background: #fff !important; -} - -.weighting-settings input[type="range"]::-moz-range-track { - background: #1e8cbe; - border: 0 solid #000; - border-radius: 1px; - box-shadow: 0 0 0 #000; - cursor: pointer; - height: 3px; - width: 100%; -} - -.weighting-settings input[type="range"]::-moz-range-thumb { - background: #1e8cbe; - border: 1px solid #1e8cbe; - border-radius: 25px; - box-shadow: 0 0 0 #000; - cursor: pointer; - height: 14px; - width: 14px; -} - -.weighting-settings input[type="range"]::-ms-track { - background: transparent; - border-color: transparent; - color: transparent; - cursor: pointer; - height: 3px; - width: 100%; -} - -.weighting-settings input[type="range"]::-ms-fill-lower { - background: #1e8cbe; - border: 0 solid #000; - border-radius: 2px; - box-shadow: 0 0 0 #000; -} - -.weighting-settings input[type="range"]::-ms-fill-upper { - background: #1e8cbe; - border: 0 solid #000; - border-radius: 2px; - box-shadow: 0 0 0 #000; -} - -.weighting-settings input[type="range"]::-ms-thumb { - background: #a1d0ff; - border: 1px solid #1e8cbe; - border-radius: 25px; - box-shadow: 0 0 0 #000; - cursor: pointer; - height: 14px; - margin-top: 1px; - width: 14px; -} - -.weighting-settings input[type="range"]:focus::-ms-fill-lower { - background: #1e8cbe; -} - -.weighting-settings input[type="range"]:focus::-ms-fill-upper { - background: #1e8cbe; -} - -.ep-feature-search .wp-color-result.button { - margin-bottom: 10px; -} - - -.ep-feature.ep-feature-search .settings .wp-picker-input-wrap > label { - margin-right: 10px; - margin-top: 0; -} - -.ep-feature.ep-feature-search .settings.wp-picker-input-wrap label { - align-items: center; - display: flex; -} - -.ep-feature.ep-feature-search .settings .wp-picker-open + .wp-picker-input-wrap { - display: flex; -} - /** * Animations */ diff --git a/assets/css/weighting.css b/assets/css/weighting.css new file mode 100644 index 0000000000..efff47d69e --- /dev/null +++ b/assets/css/weighting.css @@ -0,0 +1,57 @@ +#ep-weighting-screen { + + & .components-panel { + margin-bottom: 2rem; + max-width: 800px; + } +} + +.ep-weighting-property { + align-items: center; + display: grid; + grid-gap: 1em; + grid-template-columns: min(20%, 30ch) max-content max-content auto max-content; + width: 100%; + + & .components-base-control__field { + margin-bottom: 0; + } + + & .components-range-control { + + & .components-base-control__field { + align-items: center; + display: flex; + } + + & .components-base-control__label { + margin-bottom: 0; + } + } +} + +.ep-weighting-property fieldset { + display: contents; +} + +.ep-weighting-property__name { + + & h2 { + color: inherit; + font-size: 15px; + margin: 0; + } +} + +.ep-weighting-property__undo { + grid-column-start: 5; + justify-self: end; +} + + +.ep-weighting-undo { + + &[disabled] { + visibility: hidden; + } +} diff --git a/assets/js/weighting/components/common/undo-button.js b/assets/js/weighting/components/common/undo-button.js new file mode 100644 index 0000000000..04513b813b --- /dev/null +++ b/assets/js/weighting/components/common/undo-button.js @@ -0,0 +1,16 @@ +/** + * WordPress dependencies. + */ +import { Button } from '@wordpress/components'; +import { WPElement } from '@wordpress/element'; +import { undo } from '@wordpress/icons'; + +/** + * Undo button component. + * + * @param {object} props Component props. + * @returns {WPElement} Component element. + */ +export default (props) => { + return + + + + + + ); +}; diff --git a/assets/js/weighting/components/undo-changes.js b/assets/js/weighting/components/undo-changes.js deleted file mode 100644 index 689d88147a..0000000000 --- a/assets/js/weighting/components/undo-changes.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Wordpress Dependencies. - */ -import { __ } from '@wordpress/i18n'; -import PropTypes from 'prop-types'; - -const UndoChanges = ({ undoHandler, undoProps }) => { - return ( - - ); -}; - -UndoChanges.propTypes = { - undoHandler: PropTypes.func.isRequired, - undoProps: PropTypes.object.isRequired, -}; - -export default UndoChanges; diff --git a/assets/js/weighting/dummyData.js b/assets/js/weighting/dummyData.js index 98e5482731..19c44464b6 100644 --- a/assets/js/weighting/dummyData.js +++ b/assets/js/weighting/dummyData.js @@ -60,7 +60,7 @@ export const dummyData = [ }, ], - metas: [ + meta: [ // { // name: 'example_key', // searchable: false, @@ -139,7 +139,7 @@ export const dummyData = [ }, ], - metas: [], + meta: [], }, { label: 'Product', @@ -201,7 +201,7 @@ export const dummyData = [ weight: 1, }, ], - metas: [], + meta: [], }, ]; diff --git a/assets/js/weighting/index.js b/assets/js/weighting/index.js index 57c1940568..29d5dab8a6 100644 --- a/assets/js/weighting/index.js +++ b/assets/js/weighting/index.js @@ -1,160 +1,70 @@ /** * WordPress dependencies. */ -import { render, WPElement, useState, useEffect } from '@wordpress/element'; +import { render, WPElement, useMemo, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; +import { cloneDeep, isEqual } from 'lodash'; /** * Internal Dependencies. */ -import FieldGroup from './components/field-group'; import { dummyData } from './dummyData'; -import { sortListByOrder } from './utils'; +import PostType from './components/post-type'; +import Save from './components/save'; /** * component. * - * @return {WPElement} Element. + * @returns {WPElement} Element. */ const App = () => { - const [formData, setFormData] = useState([]); - const [savedData, setSavedData] = useState([]); - const [isSaving, setIsSaving] = useState(false); - - const hasChanges = JSON.stringify(savedData) !== JSON.stringify(formData); + const [data, setData] = useState(cloneDeep(dummyData)); + const [savedData, setSavedData] = useState(cloneDeep(dummyData)); + const [isBusy, setIsBusy] = useState(false); /** - * Fetch api response on component mount and set instead of dummy data. - */ - useEffect(() => { - setTimeout(() => { - setSavedData(dummyData); - setFormData(dummyData); - }, 200); - }, []); - - /** - * Get currently editing form type - * - * @param {string} postType type of the wp post. - * @return {Object} currently editing form. + * Is the current data different to the saved data. */ - const getCurrentFormData = (postType) => formData.find((item) => item.name === postType); + const isChanged = useMemo(() => !isEqual(data, savedData), [data, savedData]); /** - * Get currently editing form type + * Handle data change. * - * @param {string} postType type of the wp post. - * @return {Object} currently editing form. + * @param {Array} value Updated data. + * @returns {void} */ - const getSavedFormData = (postType) => savedData.find((item) => item.name === postType); - - const undoHandler = (props) => { - const { currentIndex, fieldType, postType, attribute } = props; - let currentFormData = getCurrentFormData(postType.name); - const savedFormData = getSavedFormData(postType.name); - - currentFormData = { - ...currentFormData, - [fieldType]: currentFormData[fieldType].map((item) => { - let newItem = item; - if (item.name === attribute.name) { - newItem = savedFormData[fieldType][currentIndex]; - } - return newItem; - }), - }; - - const excludeOldCurrentFormData = formData.filter((item) => item.name !== postType.name); - - const newFormData = [...excludeOldCurrentFormData, currentFormData]; - - setFormData(sortListByOrder(newFormData)); - }; - - /** - * Handle input changes. - * - * @param {Object} event react synthetic event - * @param {string} postType wp post type - * @param {string} type type of the field - * @param {string} attributeName field attribute name - */ - const onChangeHandler = (event, postType, type, attributeName) => { - let currentFormData = getCurrentFormData(postType); - - if (type === 'root') { - currentFormData = { ...currentFormData, indexable: !currentFormData.indexable }; - } else { - currentFormData = { - ...currentFormData, - [type]: currentFormData[type].map((item) => { - let newItem = item; - if (typeof event === 'number' && item.name === attributeName) { - newItem = { ...newItem, weight: event }; - } else if (item.name === attributeName) { - newItem = { ...newItem, [event.target.name]: !newItem[event.target.name] }; - } - return newItem; - }), - }; - } - - const excludeOldCurrentFormData = formData.filter((item) => item.name !== postType); - - const newFormData = [...excludeOldCurrentFormData, currentFormData]; - - setFormData(sortListByOrder(newFormData)); + const onChange = (value) => { + setData(value); }; /** - * Reset all changes of the form. - */ - const resetAllChanges = () => { - setFormData(sortListByOrder(dummyData)); - }; - - /** - * Reset current form changes. + * Handle for submission. * - * @param {string} postType name of the post type. + * @param {Event} event Submit event. + * @returns {void} */ - const resetCurrentFormChanges = (postType) => { - const savedFormData = getSavedFormData(postType); - const excludeOldCurrentFormData = formData.filter((item) => item.name !== postType); + const onSubmit = (event) => { + event.preventDefault(); - const newFormData = [...excludeOldCurrentFormData, savedFormData]; + const savedData = cloneDeep(data); - setFormData(sortListByOrder(newFormData)); + setIsBusy(true); + setSavedData(savedData); }; /** - * Check if current has changes. - * - * @param {string} postType name of the post type. - */ - const hasCurrentFormChanges = (postType) => - JSON.stringify(getCurrentFormData(postType)) !== JSON.stringify(getSavedFormData(postType)); - - /** - * Handle for submission. + * Handle resetting all settings. * - * @param {Object} event react synthetic event. + * @returns {void} */ - const handleSubmit = (event) => { - event.preventDefault(); - setIsSaving(true); + const onUndo = () => { + const data = cloneDeep(savedData); - // do api request to save formData - setTimeout(() => { - console.log({ formData }); - }, 500); - - setIsSaving(false); + setData(data); }; return ( -
+

{__('Manage Search Fields & Weighting', 'elasticpress')}

@@ -170,74 +80,24 @@ const App = () => { )}

- {formData.map((postType) => ( -
-
-

{postType.label}

-
- -
- {hasCurrentFormChanges(postType.name) && ( - - )} -
+ {data.map((value, index) => ( + { + const newValue = [...data]; - {postType.indexable && ( - - )} -
- ))} - -
- + newValue.splice(index, 1, value); - - {hasChanges - ? __('Please re-sync your data after saving.', 'elasticpress') - : __('You have nothing to save.', 'elasticpress')} - + onChange(newValue); + }} + originalValue={savedData[index]} + value={value} + /> + ))} - -
+ ); }; diff --git a/assets/js/weighting/utils.js b/assets/js/weighting/utils.js deleted file mode 100644 index 7b0847f5b3..0000000000 --- a/assets/js/weighting/utils.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Sort list by order. - * - * @param {Array} list items to bo sorted. - * @return {Array} sorted list by it's order. - */ -export const sortListByOrder = (list = []) => list.sort((a, b) => a.order - b.order); diff --git a/includes/dashboard.php b/includes/dashboard.php index 7d1a14d851..cf1a4b2089 100644 --- a/includes/dashboard.php +++ b/includes/dashboard.php @@ -491,6 +491,14 @@ function action_admin_enqueue_dashboard_scripts() { } if ( in_array( Screen::factory()->get_current_screen(), [ 'weighting', 'install' ], true ) ) { + wp_enqueue_style( 'wp-edit-post' ); + wp_enqueue_style( + 'ep_weighting_styles', + EP_URL . 'dist/css/weighting-styles.min.css', + Utils\get_asset_info( 'weighting-styles', 'dependencies' ), + Utils\get_asset_info( 'weighting-styles', 'version' ) + ); + wp_enqueue_script( 'ep_weighting_script', EP_URL . 'dist/js/weighting-script.min.js', diff --git a/package-lock.json b/package-lock.json index 5dce9906eb..60b033c52c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,11 +13,9 @@ "@wordpress/icons": "^6.1.1", "chart.js": "^2.9.4", "focus-trap-react": "^8.8.2", - "jquery": "^3.6.0", - "promise-polyfill": "^8.2.1", "prop-types": "^15.8.1", - "react": "^16.14.0", "react-beautiful-dnd": "^11.0.5", + "react-select": "^5.4.0", "react-slider": "^1.3.1", "uuid": "^8.3.2" }, @@ -29,6 +27,7 @@ "cypress-file-upload": "^5.0.8", "eslint-plugin-cypress": "^2.12.1", "jsdoc": "^3.6.10", + "lodash": "^4.17.21", "postcss-preset-env": "^7.0.1", "terser-webpack-plugin": "^5.3.0", "wp-hookdoc": "^0.2.0" @@ -149,7 +148,6 @@ }, "node_modules/@ampproject/remapping": { "version": "2.1.2", - "dev": true, "license": "Apache-2.0", "dependencies": { "@jridgewell/trace-mapping": "^0.3.0" @@ -160,7 +158,6 @@ }, "node_modules/@babel/code-frame": { "version": "7.16.7", - "dev": true, "license": "MIT", "dependencies": { "@babel/highlight": "^7.16.7" @@ -171,7 +168,6 @@ }, "node_modules/@babel/compat-data": { "version": "7.17.0", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -179,7 +175,6 @@ }, "node_modules/@babel/core": { "version": "7.17.5", - "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.1.0", @@ -225,7 +220,6 @@ }, "node_modules/@babel/generator": { "version": "7.17.3", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.17.0", @@ -261,7 +255,6 @@ }, "node_modules/@babel/helper-compilation-targets": { "version": "7.16.7", - "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.16.4", @@ -331,7 +324,6 @@ }, "node_modules/@babel/helper-environment-visitor": { "version": "7.16.7", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.16.7" @@ -353,7 +345,6 @@ }, "node_modules/@babel/helper-function-name": { "version": "7.16.7", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-get-function-arity": "^7.16.7", @@ -366,7 +357,6 @@ }, "node_modules/@babel/helper-get-function-arity": { "version": "7.16.7", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.16.7" @@ -377,7 +367,6 @@ }, "node_modules/@babel/helper-hoist-variables": { "version": "7.16.7", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.16.7" @@ -399,7 +388,6 @@ }, "node_modules/@babel/helper-module-imports": { "version": "7.16.7", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.16.7" @@ -410,7 +398,6 @@ }, "node_modules/@babel/helper-module-transforms": { "version": "7.17.6", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-environment-visitor": "^7.16.7", @@ -438,9 +425,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.16.7", - "dev": true, - "license": "MIT", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", + "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==", "engines": { "node": ">=6.9.0" } @@ -475,7 +462,6 @@ }, "node_modules/@babel/helper-simple-access": { "version": "7.16.7", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.16.7" @@ -497,7 +483,6 @@ }, "node_modules/@babel/helper-split-export-declaration": { "version": "7.16.7", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.16.7" @@ -508,7 +493,6 @@ }, "node_modules/@babel/helper-validator-identifier": { "version": "7.16.7", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -516,7 +500,6 @@ }, "node_modules/@babel/helper-validator-option": { "version": "7.16.7", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -538,7 +521,6 @@ }, "node_modules/@babel/helpers": { "version": "7.17.2", - "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.16.7", @@ -551,7 +533,6 @@ }, "node_modules/@babel/highlight": { "version": "7.16.10", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", @@ -564,7 +545,6 @@ }, "node_modules/@babel/parser": { "version": "7.17.3", - "dev": true, "license": "MIT", "bin": { "parser": "bin/babel-parser.js" @@ -928,11 +908,11 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.16.7", - "dev": true, - "license": "MIT", + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", "dependencies": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" @@ -1788,8 +1768,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.17.2", - "license": "MIT", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.0.tgz", + "integrity": "sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==", "dependencies": { "regenerator-runtime": "^0.13.4" }, @@ -1827,7 +1808,6 @@ }, "node_modules/@babel/template": { "version": "7.16.7", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.16.7", @@ -1840,7 +1820,6 @@ }, "node_modules/@babel/traverse": { "version": "7.17.3", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.16.7", @@ -1860,7 +1839,6 @@ }, "node_modules/@babel/types": { "version": "7.17.0", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", @@ -2035,6 +2013,117 @@ "ms": "^2.1.1" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.10.2", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.2.tgz", + "integrity": "sha512-xNQ57njWTFVfPAc3cjfuaPdsgLp5QOSuRsj9MA6ndEhH/AzuZM86qIQzt6rq+aGBwj3n5/TkLmU5lhAfdRmogA==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/plugin-syntax-jsx": "^7.17.12", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.0", + "@emotion/memoize": "^0.8.0", + "@emotion/serialize": "^1.1.0", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.0.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.10.3", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.3.tgz", + "integrity": "sha512-Psmp/7ovAa8appWh3g51goxu/z3iVms7JXOreq136D8Bbn6dYraPnmL6mdM8GThEx9vwSn92Fz+mGSjBzN8UPQ==", + "dependencies": { + "@emotion/memoize": "^0.8.0", + "@emotion/sheet": "^1.2.0", + "@emotion/utils": "^1.2.0", + "@emotion/weak-memoize": "^0.3.0", + "stylis": "4.0.13" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz", + "integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==" + }, + "node_modules/@emotion/memoize": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", + "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" + }, + "node_modules/@emotion/react": { + "version": "11.10.4", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.4.tgz", + "integrity": "sha512-j0AkMpr6BL8gldJZ6XQsQ8DnS9TxEQu1R+OGmDZiWjBAJtCcbt0tS3I/YffoqHXxH6MjgI7KdMbYKw3MEiU9eA==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.10.0", + "@emotion/cache": "^11.10.0", + "@emotion/serialize": "^1.1.0", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", + "@emotion/utils": "^1.2.0", + "@emotion/weak-memoize": "^0.3.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.0.tgz", + "integrity": "sha512-F1ZZZW51T/fx+wKbVlwsfchr5q97iW8brAnXmsskz4d0hVB4O3M/SiA3SaeH06x02lSNzkkQv+n3AX3kCXKSFA==", + "dependencies": { + "@emotion/hash": "^0.9.0", + "@emotion/memoize": "^0.8.0", + "@emotion/unitless": "^0.8.0", + "@emotion/utils": "^1.2.0", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.0.tgz", + "integrity": "sha512-OiTkRgpxescko+M51tZsMq7Puu/KP55wMT8BgpcXVG2hqXc0Vo0mfymJ/Uj24Hp0i083ji/o0aLddh08UEjq8w==" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", + "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", + "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz", + "integrity": "sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz", + "integrity": "sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==" + }, "node_modules/@es-joy/jsdoccomment": { "version": "0.20.1", "dev": true, @@ -2777,7 +2866,6 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.0.5", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -2804,14 +2892,12 @@ }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.11", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.14", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -3517,7 +3603,6 @@ }, "node_modules/@types/parse-json": { "version": "4.0.0", - "dev": true, "license": "MIT" }, "node_modules/@types/prettier": { @@ -3565,6 +3650,14 @@ "redux": "^4.0.0" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -4537,7 +4630,6 @@ }, "node_modules/ansi-styles": { "version": "3.2.1", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^1.9.0" @@ -4960,6 +5052,20 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.3.1", "dev": true, @@ -5218,7 +5324,6 @@ }, "node_modules/browserslist": { "version": "4.19.3", - "dev": true, "license": "MIT", "dependencies": { "caniuse-lite": "^1.0.30001312", @@ -5344,7 +5449,6 @@ }, "node_modules/callsites": { "version": "3.1.0", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -5417,7 +5521,6 @@ "version": "1.0.30001375", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001375.tgz", "integrity": "sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==", - "dev": true, "funding": [ { "type": "opencollective", @@ -5447,7 +5550,6 @@ }, "node_modules/chalk": { "version": "2.4.2", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", @@ -5460,7 +5562,6 @@ }, "node_modules/chalk/node_modules/escape-string-regexp": { "version": "1.0.5", - "dev": true, "license": "MIT", "engines": { "node": ">=0.8.0" @@ -5948,7 +6049,6 @@ }, "node_modules/convert-source-map": { "version": "1.8.0", - "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.1.1" @@ -6132,7 +6232,6 @@ }, "node_modules/cosmiconfig": { "version": "7.0.1", - "dev": true, "license": "MIT", "dependencies": { "@types/parse-json": "^4.0.0", @@ -6678,7 +6777,6 @@ }, "node_modules/debug": { "version": "4.3.3", - "dev": true, "license": "MIT", "dependencies": { "ms": "2.1.2" @@ -6997,6 +7095,15 @@ "utila": "~0.4" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/dom-serializer": { "version": "1.3.2", "dev": true, @@ -7105,7 +7212,6 @@ }, "node_modules/electron-to-chromium": { "version": "1.4.75", - "dev": true, "license": "ISC" }, "node_modules/emittery": { @@ -7191,7 +7297,6 @@ }, "node_modules/error-ex": { "version": "1.3.2", - "dev": true, "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" @@ -7261,7 +7366,6 @@ }, "node_modules/escalade": { "version": "3.1.1", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -7274,7 +7378,6 @@ }, "node_modules/escape-string-regexp": { "version": "4.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -8571,6 +8674,11 @@ "dev": true, "license": "MIT" }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "node_modules/find-up": { "version": "5.0.0", "dev": true, @@ -8716,7 +8824,6 @@ }, "node_modules/function-bind": { "version": "1.1.1", - "dev": true, "license": "MIT" }, "node_modules/functional-red-black-tree": { @@ -8726,7 +8833,6 @@ }, "node_modules/gensync": { "version": "1.0.0-beta.2", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -8908,7 +9014,6 @@ }, "node_modules/globals": { "version": "11.12.0", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -9011,7 +9116,6 @@ }, "node_modules/has": { "version": "1.0.3", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.1" @@ -9030,7 +9134,6 @@ }, "node_modules/has-flag": { "version": "3.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -9476,7 +9579,6 @@ }, "node_modules/import-fresh": { "version": "3.3.0", - "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -9491,7 +9593,6 @@ }, "node_modules/import-fresh/node_modules/resolve-from": { "version": "4.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -9708,7 +9809,6 @@ }, "node_modules/is-arrayish": { "version": "0.2.1", - "dev": true, "license": "MIT" }, "node_modules/is-bigint": { @@ -9794,7 +9894,6 @@ }, "node_modules/is-core-module": { "version": "2.8.1", - "dev": true, "license": "MIT", "dependencies": { "has": "^1.0.3" @@ -11955,11 +12054,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/jquery": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz", - "integrity": "sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw==" - }, "node_modules/js-tokens": { "version": "4.0.0", "license": "MIT" @@ -12126,7 +12220,6 @@ }, "node_modules/jsesc": { "version": "2.5.2", - "dev": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -12148,7 +12241,6 @@ }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", - "dev": true, "license": "MIT" }, "node_modules/json-schema": { @@ -12178,7 +12270,6 @@ }, "node_modules/json5": { "version": "2.2.0", - "dev": true, "license": "MIT", "dependencies": { "minimist": "^1.2.5" @@ -12325,7 +12416,6 @@ }, "node_modules/lines-and-columns": { "version": "1.2.4", - "dev": true, "license": "MIT" }, "node_modules/linkify-it": { @@ -12407,7 +12497,8 @@ }, "node_modules/lodash": { "version": "4.17.21", - "license": "MIT" + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.debounce": { "version": "4.0.8", @@ -13069,8 +13160,7 @@ "node_modules/minimist": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, "node_modules/minimist-options": { "version": "4.1.0", @@ -13114,7 +13204,6 @@ }, "node_modules/ms": { "version": "2.1.2", - "dev": true, "license": "MIT" }, "node_modules/multicast-dns": { @@ -13193,7 +13282,6 @@ }, "node_modules/node-releases": { "version": "2.0.2", - "dev": true, "license": "MIT" }, "node_modules/normalize-package-data": { @@ -13724,7 +13812,6 @@ }, "node_modules/parent-module": { "version": "1.0.1", - "dev": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" @@ -13752,7 +13839,6 @@ }, "node_modules/parse-json": { "version": "5.2.0", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", @@ -13815,7 +13901,6 @@ }, "node_modules/path-parse": { "version": "1.0.7", - "dev": true, "license": "MIT" }, "node_modules/path-to-regexp": { @@ -13825,7 +13910,6 @@ }, "node_modules/path-type": { "version": "4.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -13843,7 +13927,6 @@ }, "node_modules/picocolors": { "version": "1.0.0", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -15383,11 +15466,6 @@ "dev": true, "license": "MIT" }, - "node_modules/promise-polyfill": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.2.3.tgz", - "integrity": "sha512-Og0+jCRQetV84U8wVjMNccfGCnMQ9mGs9Hv78QFe+pSDD3gWTpz0y+1QCuxy5d/vBFuZ3iwP2eycAkvqIMPmWg==" - }, "node_modules/prompts": { "version": "2.4.2", "dev": true, @@ -15551,6 +15629,7 @@ "node_modules/react": { "version": "16.14.0", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -15626,6 +15705,24 @@ "node": ">=0.10.0" } }, + "node_modules/react-select": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.4.0.tgz", + "integrity": "sha512-CjE9RFLUvChd5SdlfG4vqxZd55AZJRrLrHzkQyTYeHlpOztqcgnyftYAolJ0SGsBev6zAs6qFrjm6KU3eo2hzg==", + "dependencies": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^5.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-slider": { "version": "1.3.1", "license": "MIT", @@ -15634,6 +15731,21 @@ "react": "^16 || ^17" } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "dev": true, @@ -15994,7 +16106,6 @@ }, "node_modules/resolve": { "version": "1.22.0", - "dev": true, "license": "MIT", "dependencies": { "is-core-module": "^2.8.1", @@ -16157,7 +16268,6 @@ }, "node_modules/safe-buffer": { "version": "5.1.2", - "dev": true, "license": "MIT" }, "node_modules/safer-buffer": { @@ -16273,7 +16383,6 @@ }, "node_modules/semver": { "version": "6.3.0", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -16537,7 +16646,6 @@ }, "node_modules/source-map": { "version": "0.5.7", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -17219,6 +17327,11 @@ "node": ">=8" } }, + "node_modules/stylis": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz", + "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==" + }, "node_modules/sugarss": { "version": "3.0.3", "dev": true, @@ -17236,7 +17349,6 @@ }, "node_modules/supports-color": { "version": "5.5.0", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^3.0.0" @@ -17278,7 +17390,6 @@ }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -17587,7 +17698,6 @@ }, "node_modules/to-fast-properties": { "version": "2.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -18841,7 +18951,6 @@ }, "node_modules/yaml": { "version": "1.10.2", - "dev": true, "license": "ISC", "engines": { "node": ">= 6" @@ -18980,25 +19089,21 @@ }, "@ampproject/remapping": { "version": "2.1.2", - "dev": true, "requires": { "@jridgewell/trace-mapping": "^0.3.0" } }, "@babel/code-frame": { "version": "7.16.7", - "dev": true, "requires": { "@babel/highlight": "^7.16.7" } }, "@babel/compat-data": { - "version": "7.17.0", - "dev": true + "version": "7.17.0" }, "@babel/core": { "version": "7.17.5", - "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", @@ -19028,7 +19133,6 @@ }, "@babel/generator": { "version": "7.17.3", - "dev": true, "requires": { "@babel/types": "^7.17.0", "jsesc": "^2.5.1", @@ -19052,7 +19156,6 @@ }, "@babel/helper-compilation-targets": { "version": "7.16.7", - "dev": true, "requires": { "@babel/compat-data": "^7.16.4", "@babel/helper-validator-option": "^7.16.7", @@ -19097,7 +19200,6 @@ }, "@babel/helper-environment-visitor": { "version": "7.16.7", - "dev": true, "requires": { "@babel/types": "^7.16.7" } @@ -19111,7 +19213,6 @@ }, "@babel/helper-function-name": { "version": "7.16.7", - "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.16.7", "@babel/template": "^7.16.7", @@ -19120,14 +19221,12 @@ }, "@babel/helper-get-function-arity": { "version": "7.16.7", - "dev": true, "requires": { "@babel/types": "^7.16.7" } }, "@babel/helper-hoist-variables": { "version": "7.16.7", - "dev": true, "requires": { "@babel/types": "^7.16.7" } @@ -19141,14 +19240,12 @@ }, "@babel/helper-module-imports": { "version": "7.16.7", - "dev": true, "requires": { "@babel/types": "^7.16.7" } }, "@babel/helper-module-transforms": { "version": "7.17.6", - "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-module-imports": "^7.16.7", @@ -19168,8 +19265,9 @@ } }, "@babel/helper-plugin-utils": { - "version": "7.16.7", - "dev": true + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", + "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==" }, "@babel/helper-remap-async-to-generator": { "version": "7.16.8", @@ -19193,7 +19291,6 @@ }, "@babel/helper-simple-access": { "version": "7.16.7", - "dev": true, "requires": { "@babel/types": "^7.16.7" } @@ -19207,18 +19304,15 @@ }, "@babel/helper-split-export-declaration": { "version": "7.16.7", - "dev": true, "requires": { "@babel/types": "^7.16.7" } }, "@babel/helper-validator-identifier": { - "version": "7.16.7", - "dev": true + "version": "7.16.7" }, "@babel/helper-validator-option": { - "version": "7.16.7", - "dev": true + "version": "7.16.7" }, "@babel/helper-wrap-function": { "version": "7.16.8", @@ -19232,7 +19326,6 @@ }, "@babel/helpers": { "version": "7.17.2", - "dev": true, "requires": { "@babel/template": "^7.16.7", "@babel/traverse": "^7.17.0", @@ -19241,7 +19334,6 @@ }, "@babel/highlight": { "version": "7.16.10", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", "chalk": "^2.0.0", @@ -19249,8 +19341,7 @@ } }, "@babel/parser": { - "version": "7.17.3", - "dev": true + "version": "7.17.3" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.16.7", @@ -19453,10 +19544,11 @@ } }, "@babel/plugin-syntax-jsx": { - "version": "7.16.7", - "dev": true, + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", "requires": { - "@babel/helper-plugin-utils": "^7.16.7" + "@babel/helper-plugin-utils": "^7.18.6" } }, "@babel/plugin-syntax-logical-assignment-operators": { @@ -19960,7 +20052,9 @@ } }, "@babel/runtime": { - "version": "7.17.2", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.0.tgz", + "integrity": "sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -19987,7 +20081,6 @@ }, "@babel/template": { "version": "7.16.7", - "dev": true, "requires": { "@babel/code-frame": "^7.16.7", "@babel/parser": "^7.16.7", @@ -19996,7 +20089,6 @@ }, "@babel/traverse": { "version": "7.17.3", - "dev": true, "requires": { "@babel/code-frame": "^7.16.7", "@babel/generator": "^7.17.3", @@ -20012,7 +20104,6 @@ }, "@babel/types": { "version": "7.17.0", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", "to-fast-properties": "^2.0.0" @@ -20122,6 +20213,100 @@ } } }, + "@emotion/babel-plugin": { + "version": "11.10.2", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.2.tgz", + "integrity": "sha512-xNQ57njWTFVfPAc3cjfuaPdsgLp5QOSuRsj9MA6ndEhH/AzuZM86qIQzt6rq+aGBwj3n5/TkLmU5lhAfdRmogA==", + "requires": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/plugin-syntax-jsx": "^7.17.12", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.0", + "@emotion/memoize": "^0.8.0", + "@emotion/serialize": "^1.1.0", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.0.13" + } + }, + "@emotion/cache": { + "version": "11.10.3", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.3.tgz", + "integrity": "sha512-Psmp/7ovAa8appWh3g51goxu/z3iVms7JXOreq136D8Bbn6dYraPnmL6mdM8GThEx9vwSn92Fz+mGSjBzN8UPQ==", + "requires": { + "@emotion/memoize": "^0.8.0", + "@emotion/sheet": "^1.2.0", + "@emotion/utils": "^1.2.0", + "@emotion/weak-memoize": "^0.3.0", + "stylis": "4.0.13" + } + }, + "@emotion/hash": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz", + "integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==" + }, + "@emotion/memoize": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", + "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" + }, + "@emotion/react": { + "version": "11.10.4", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.4.tgz", + "integrity": "sha512-j0AkMpr6BL8gldJZ6XQsQ8DnS9TxEQu1R+OGmDZiWjBAJtCcbt0tS3I/YffoqHXxH6MjgI7KdMbYKw3MEiU9eA==", + "requires": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.10.0", + "@emotion/cache": "^11.10.0", + "@emotion/serialize": "^1.1.0", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", + "@emotion/utils": "^1.2.0", + "@emotion/weak-memoize": "^0.3.0", + "hoist-non-react-statics": "^3.3.1" + } + }, + "@emotion/serialize": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.0.tgz", + "integrity": "sha512-F1ZZZW51T/fx+wKbVlwsfchr5q97iW8brAnXmsskz4d0hVB4O3M/SiA3SaeH06x02lSNzkkQv+n3AX3kCXKSFA==", + "requires": { + "@emotion/hash": "^0.9.0", + "@emotion/memoize": "^0.8.0", + "@emotion/unitless": "^0.8.0", + "@emotion/utils": "^1.2.0", + "csstype": "^3.0.2" + } + }, + "@emotion/sheet": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.0.tgz", + "integrity": "sha512-OiTkRgpxescko+M51tZsMq7Puu/KP55wMT8BgpcXVG2hqXc0Vo0mfymJ/Uj24Hp0i083ji/o0aLddh08UEjq8w==" + }, + "@emotion/unitless": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", + "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" + }, + "@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", + "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==", + "requires": {} + }, + "@emotion/utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz", + "integrity": "sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==" + }, + "@emotion/weak-memoize": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz", + "integrity": "sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==" + }, "@es-joy/jsdoccomment": { "version": "0.20.1", "dev": true, @@ -20607,8 +20792,7 @@ } }, "@jridgewell/resolve-uri": { - "version": "3.0.5", - "dev": true + "version": "3.0.5" }, "@jridgewell/set-array": { "version": "1.1.2", @@ -20627,14 +20811,12 @@ } }, "@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "dev": true + "version": "1.4.11" }, "@jridgewell/trace-mapping": { "version": "0.3.14", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", - "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -21090,8 +21272,7 @@ "dev": true }, "@types/parse-json": { - "version": "4.0.0", - "dev": true + "version": "4.0.0" }, "@types/prettier": { "version": "2.4.4", @@ -21131,6 +21312,14 @@ "redux": "^4.0.0" } }, + "@types/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==", + "requires": { + "@types/react": "*" + } + }, "@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -21784,7 +21973,6 @@ }, "ansi-styles": { "version": "3.2.1", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -22040,6 +22228,16 @@ "@types/babel__traverse": "^7.0.6" } }, + "babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "requires": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + } + }, "babel-plugin-polyfill-corejs2": { "version": "0.3.1", "dev": true, @@ -22214,7 +22412,6 @@ }, "browserslist": { "version": "4.19.3", - "dev": true, "requires": { "caniuse-lite": "^1.0.30001312", "electron-to-chromium": "^1.4.71", @@ -22288,8 +22485,7 @@ } }, "callsites": { - "version": "3.1.0", - "dev": true + "version": "3.1.0" }, "camel-case": { "version": "4.1.2", @@ -22335,8 +22531,7 @@ "caniuse-lite": { "version": "1.0.30001375", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001375.tgz", - "integrity": "sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==", - "dev": true + "integrity": "sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==" }, "caseless": { "version": "0.12.0", @@ -22351,7 +22546,6 @@ }, "chalk": { "version": "2.4.2", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -22359,8 +22553,7 @@ }, "dependencies": { "escape-string-regexp": { - "version": "1.0.5", - "dev": true + "version": "1.0.5" } } }, @@ -22675,7 +22868,6 @@ }, "convert-source-map": { "version": "1.8.0", - "dev": true, "requires": { "safe-buffer": "~5.1.1" } @@ -22784,7 +22976,6 @@ }, "cosmiconfig": { "version": "7.0.1", - "dev": true, "requires": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -23137,7 +23328,6 @@ }, "debug": { "version": "4.3.3", - "dev": true, "requires": { "ms": "2.1.2" } @@ -23343,6 +23533,15 @@ "utila": "~0.4" } }, + "dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "requires": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "dom-serializer": { "version": "1.3.2", "dev": true, @@ -23416,8 +23615,7 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.75", - "dev": true + "version": "1.4.75" }, "emittery": { "version": "0.8.1", @@ -23467,7 +23665,6 @@ }, "error-ex": { "version": "1.3.2", - "dev": true, "requires": { "is-arrayish": "^0.2.1" } @@ -23519,16 +23716,14 @@ } }, "escalade": { - "version": "3.1.1", - "dev": true + "version": "3.1.1" }, "escape-html": { "version": "1.0.3", "dev": true }, "escape-string-regexp": { - "version": "4.0.0", - "dev": true + "version": "4.0.0" }, "escodegen": { "version": "2.0.0", @@ -24373,6 +24568,11 @@ "version": "0.3.1", "dev": true }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "find-up": { "version": "5.0.0", "dev": true, @@ -24453,16 +24653,14 @@ "dev": true }, "function-bind": { - "version": "1.1.1", - "dev": true + "version": "1.1.1" }, "functional-red-black-tree": { "version": "1.0.1", "dev": true }, "gensync": { - "version": "1.0.0-beta.2", - "dev": true + "version": "1.0.0-beta.2" }, "get-caller-file": { "version": "2.0.5", @@ -24574,8 +24772,7 @@ } }, "globals": { - "version": "11.12.0", - "dev": true + "version": "11.12.0" }, "globby": { "version": "11.1.0", @@ -24640,7 +24837,6 @@ }, "has": { "version": "1.0.3", - "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -24650,8 +24846,7 @@ "dev": true }, "has-flag": { - "version": "3.0.0", - "dev": true + "version": "3.0.0" }, "has-symbols": { "version": "1.0.3", @@ -24921,15 +25116,13 @@ }, "import-fresh": { "version": "3.3.0", - "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" }, "dependencies": { "resolve-from": { - "version": "4.0.0", - "dev": true + "version": "4.0.0" } } }, @@ -25061,8 +25254,7 @@ } }, "is-arrayish": { - "version": "0.2.1", - "dev": true + "version": "0.2.1" }, "is-bigint": { "version": "1.0.4", @@ -25103,7 +25295,6 @@ }, "is-core-module": { "version": "2.8.1", - "dev": true, "requires": { "has": "^1.0.3" } @@ -26461,11 +26652,6 @@ } } }, - "jquery": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz", - "integrity": "sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw==" - }, "js-tokens": { "version": "4.0.0" }, @@ -26583,8 +26769,7 @@ } }, "jsesc": { - "version": "2.5.2", - "dev": true + "version": "2.5.2" }, "json-buffer": { "version": "3.0.1", @@ -26597,8 +26782,7 @@ "dev": true }, "json-parse-even-better-errors": { - "version": "2.3.1", - "dev": true + "version": "2.3.1" }, "json-schema": { "version": "0.4.0", @@ -26622,7 +26806,6 @@ }, "json5": { "version": "2.2.0", - "dev": true, "requires": { "minimist": "^1.2.5" } @@ -26715,8 +26898,7 @@ "dev": true }, "lines-and-columns": { - "version": "1.2.4", - "dev": true + "version": "1.2.4" }, "linkify-it": { "version": "3.0.3", @@ -26769,7 +26951,9 @@ } }, "lodash": { - "version": "4.17.21" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash.debounce": { "version": "4.0.8", @@ -27176,8 +27360,7 @@ "minimist": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, "minimist-options": { "version": "4.1.0", @@ -27205,8 +27388,7 @@ "dev": true }, "ms": { - "version": "2.1.2", - "dev": true + "version": "2.1.2" }, "multicast-dns": { "version": "6.2.3", @@ -27259,8 +27441,7 @@ "dev": true }, "node-releases": { - "version": "2.0.2", - "dev": true + "version": "2.0.2" }, "normalize-package-data": { "version": "2.5.0", @@ -27591,7 +27772,6 @@ }, "parent-module": { "version": "1.0.1", - "dev": true, "requires": { "callsites": "^3.0.0" } @@ -27610,7 +27790,6 @@ }, "parse-json": { "version": "5.2.0", - "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -27647,16 +27826,14 @@ "dev": true }, "path-parse": { - "version": "1.0.7", - "dev": true + "version": "1.0.7" }, "path-to-regexp": { "version": "0.1.7", "dev": true }, "path-type": { - "version": "4.0.0", - "dev": true + "version": "4.0.0" }, "pend": { "version": "1.2.0", @@ -27667,8 +27844,7 @@ "dev": true }, "picocolors": { - "version": "1.0.0", - "dev": true + "version": "1.0.0" }, "picomatch": { "version": "2.3.1", @@ -28561,11 +28737,6 @@ "version": "2.0.1", "dev": true }, - "promise-polyfill": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.2.3.tgz", - "integrity": "sha512-Og0+jCRQetV84U8wVjMNccfGCnMQ9mGs9Hv78QFe+pSDD3gWTpz0y+1QCuxy5d/vBFuZ3iwP2eycAkvqIMPmWg==" - }, "prompts": { "version": "2.4.2", "dev": true, @@ -28672,6 +28843,7 @@ }, "react": { "version": "16.14.0", + "peer": true, "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -28719,10 +28891,35 @@ "version": "0.11.0", "dev": true }, + "react-select": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.4.0.tgz", + "integrity": "sha512-CjE9RFLUvChd5SdlfG4vqxZd55AZJRrLrHzkQyTYeHlpOztqcgnyftYAolJ0SGsBev6zAs6qFrjm6KU3eo2hzg==", + "requires": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^5.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0" + } + }, "react-slider": { "version": "1.3.1", "requires": {} }, + "react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "requires": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + } + }, "read-cache": { "version": "1.0.0", "dev": true, @@ -28964,7 +29161,6 @@ }, "resolve": { "version": "1.22.0", - "dev": true, "requires": { "is-core-module": "^2.8.1", "path-parse": "^1.0.7", @@ -29060,8 +29256,7 @@ } }, "safe-buffer": { - "version": "5.1.2", - "dev": true + "version": "5.1.2" }, "safer-buffer": { "version": "2.1.2", @@ -29120,8 +29315,7 @@ } }, "semver": { - "version": "6.3.0", - "dev": true + "version": "6.3.0" }, "send": { "version": "0.17.2", @@ -29320,8 +29514,7 @@ } }, "source-map": { - "version": "0.5.7", - "dev": true + "version": "0.5.7" }, "source-map-js": { "version": "1.0.2", @@ -29783,6 +29976,11 @@ } } }, + "stylis": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz", + "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==" + }, "sugarss": { "version": "3.0.3", "dev": true, @@ -29792,7 +29990,6 @@ }, "supports-color": { "version": "5.5.0", - "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -29819,8 +30016,7 @@ } }, "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "dev": true + "version": "1.0.0" }, "svg-parser": { "version": "2.0.4", @@ -30016,8 +30212,7 @@ "dev": true }, "to-fast-properties": { - "version": "2.0.0", - "dev": true + "version": "2.0.0" }, "to-regex-range": { "version": "5.0.1", @@ -30796,8 +30991,7 @@ "dev": true }, "yaml": { - "version": "1.10.2", - "dev": true + "version": "1.10.2" }, "yargs": { "version": "17.3.1", diff --git a/package.json b/package.json index 012eb4d4ce..600f7fa068 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "cypress-file-upload": "^5.0.8", "eslint-plugin-cypress": "^2.12.1", "jsdoc": "^3.6.10", + "lodash": "^4.17.21", "postcss-preset-env": "^7.0.1", "terser-webpack-plugin": "^5.3.0", "wp-hookdoc": "^0.2.0" @@ -76,7 +77,8 @@ "facets-block-styles.min": "./assets/css/facets-block.css", "related-posts-block-styles.min": "./assets/css/related-posts-block.css", "sync-styles.min": "./assets/css/sync.css", - "synonyms-styles.min": "./assets/css/synonyms.css" + "synonyms-styles.min": "./assets/css/synonyms.css", + "weighting-styles.min": "./assets/css/weighting.css" }, "wpDependencyExternals": true }, @@ -85,10 +87,7 @@ "@wordpress/icons": "^6.1.1", "chart.js": "^2.9.4", "focus-trap-react": "^8.8.2", - "jquery": "^3.6.0", - "promise-polyfill": "^8.2.1", "prop-types": "^15.8.1", - "react": "^16.14.0", "react-beautiful-dnd": "^11.0.5", "react-slider": "^1.3.1", "uuid": "^8.3.2" From b68c382e1e697c40d54304e677567d964a8f10cd Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Mon, 10 Oct 2022 01:18:30 +1100 Subject: [PATCH 04/63] WIP populate weighting screen with real data. --- assets/css/weighting.css | 8 + assets/js/weighting/components/post-type.js | 74 +-- .../weighting/components/post-type/group.js | 121 ++++ .../components/post-type/properties.js | 54 -- .../components/post-type/property.js | 40 +- assets/js/weighting/config.js | 6 + assets/js/weighting/index.js | 30 +- assets/js/weighting/utilities.js | 0 includes/classes/Feature/Search/Weighting.php | 109 +++- includes/dashboard.php | 11 + package-lock.json | 554 ++++++------------ 11 files changed, 455 insertions(+), 552 deletions(-) create mode 100644 assets/js/weighting/components/post-type/group.js delete mode 100644 assets/js/weighting/components/post-type/properties.js create mode 100644 assets/js/weighting/config.js create mode 100644 assets/js/weighting/utilities.js diff --git a/assets/css/weighting.css b/assets/css/weighting.css index efff47d69e..d77d3c45e4 100644 --- a/assets/css/weighting.css +++ b/assets/css/weighting.css @@ -55,3 +55,11 @@ visibility: hidden; } } + +.ep-weighting-add-meta { + justify-content: start; + + & .components-select-control__input.components-select-control__input.components-select-control__input { + min-height: 36px; + } +} diff --git a/assets/js/weighting/components/post-type.js b/assets/js/weighting/components/post-type.js index bb06b751e7..7d151b5ce7 100644 --- a/assets/js/weighting/components/post-type.js +++ b/assets/js/weighting/components/post-type.js @@ -9,50 +9,31 @@ import { isEqual } from 'lodash'; /** * Internal dependencies. */ -import Properties from './post-type/properties'; +import Group from './post-type/group'; import UndoButton from './common/undo-button'; /** * Post type weighting settings component. * * @param {object} props Components props. + * @param {object[]} props.groups Field groups. * @param {string} props.label Post type label. * @param {Function} props.onChange Data change handler. - * @param {object} props.originalValue Saved post type settings. - * @param {object} props.value Current post type settings. + * @param {object} props.originalValues Saved post type settings. + * @param {object} props.values Current post type settings. * @returns {WPElement} Component element. */ -export default ({ label, onChange, originalValue, value }) => { - const isChanged = useMemo(() => !isEqual(originalValue, value), [originalValue, value]); +export default ({ groups, label, onChange, originalValues, values }) => { + const isChanged = useMemo(() => !isEqual(originalValues, values), [originalValues, values]); /** - * Handle a change to post type indexing. + * Handle a change to the post type's setttings. * - * @param {boolean} indexable Is post type indexable. + * @param {Array} values Post type settings. * @returns {void} */ - const onChangeIndexable = (indexable) => { - onChange({ ...value, indexable }); - }; - - /** - * Handle a change to the post type's attribute setttings. - * - * @param {Array} attributes Attribute settings. - * @returns {void} - */ - const onChangeAttributes = (attributes) => { - onChange({ ...value, attributes }); - }; - - /** - * Handle a change to the post type's taxonomy setttings. - * - * @param {Array} taxonomies Taxonomy settings. - * @returns {void} - */ - const onChangeTaxonomies = (taxonomies) => { - onChange({ ...value, taxonomies }); + const onChangeGroup = (values) => { + onChange(values); }; /** @@ -61,7 +42,7 @@ export default ({ label, onChange, originalValue, value }) => { * @returns {void} */ const onReset = () => { - onChange({ ...originalValue }); + onChange(originalValues); }; return ( @@ -72,11 +53,7 @@ export default ({ label, onChange, originalValue, value }) => {

{label}

- +
{isChanged ? ( @@ -89,22 +66,19 @@ export default ({ label, onChange, originalValue, value }) => {
- {value.indexable ? ( - <> - - g.children.length !== 0) + .map(([key, { label, children }]) => ( + - - ) : null} + ))} ); }; diff --git a/assets/js/weighting/components/post-type/group.js b/assets/js/weighting/components/post-type/group.js new file mode 100644 index 0000000000..3f6a52eeaf --- /dev/null +++ b/assets/js/weighting/components/post-type/group.js @@ -0,0 +1,121 @@ +/** + * WordPress dependencies. + */ +import { Button, PanelBody, PanelRow, SelectControl } from '@wordpress/components'; +import { useMemo, useState, WPElement } from '@wordpress/element'; + +/** + * Internal dependencies. + */ +import Property from './property'; + +/** + * Post type propertes component. + * + * @param {object} props Component props. + * @param {boolean} props.isEditable Whether to display as an editable list. + * @param {string} props.label Properties label. + * @param {Function} props.onChange Change handler. + * @param {object[]} props.originalValues Saved property values. + * @param {object[]} props.properties Post type properties. + * @param {object[]} props.values Current property values. + * @returns {WPElement} Component element. + */ +export default ({ isEditable, label, onChange, originalValues, properties, values }) => { + const [toAdd, setToAdd] = useState(''); + + /** + * Handle changes to a property. + * + * @param {object} value New property data. + * @param {number} key Property key. + * @returns {void} + */ + const onChangeProperty = (value, key) => { + const newValues = { ...values, [key]: value }; + + onChange(newValues); + }; + + /** + * Handle selecting a new property to enable. + * + * @param {string} key Key of property to enable. + * @returns {void} + */ + const onChangeToAdd = (key) => { + setToAdd(key); + }; + + /** + * Handle clicking to add a new property. + * + * @returns {void} + */ + const onClickAdd = () => { + const value = { ...values[toAdd], enabled: true }; + const newValues = { ...values, [toAdd]: value }; + + onChange(newValues); + setToAdd(''); + }; + + /** + * Options for properties that can be added to the list of weightable + * properties. + */ + const availableProperties = useMemo(() => { + return isEditable + ? Object.values(properties).map((p) => ({ + label: p.label, + value: p.key, + disabled: values[p.key].enabled, + })) + : null; + }, [isEditable, properties, values]); + + /** + * Properties that can be weighted. + * + * If the component is set to be editable this will only be properties that + * are indexed, otherwise it will be all properties. + */ + const weightableProperties = useMemo(() => { + return Object.values(properties).filter((p) => (isEditable ? values[p.key].enabled : true)); + }, [isEditable, properties, values]); + + return ( + + {weightableProperties.map(({ key, label }) => ( + + { + onChangeProperty(value, key); + }} + /> + + ))} + {availableProperties ? ( + + +   + + + ) : null} + + ); +}; diff --git a/assets/js/weighting/components/post-type/properties.js b/assets/js/weighting/components/post-type/properties.js deleted file mode 100644 index dc0b75aac1..0000000000 --- a/assets/js/weighting/components/post-type/properties.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * WordPress dependencies. - */ -import { PanelBody, PanelRow } from '@wordpress/components'; -import { WPElement } from '@wordpress/element'; - -/** - * Internal dependencies. - */ -import Property from './property'; - -/** - * Post type propertes component. - * - * @param {object} props Component props. - * @param {string} props.label Properties label. - * @param {Function} props.onChange Change handler. - * @param {object[]} props.originalValue Saved property values. - * @param {object[]} props.value Current property values. - * @returns {WPElement} Component element. - */ -export default ({ label, onChange, originalValue, value }) => { - /** - * Handle changes to a property. - * - * @param {object} property New property data. - * @param {number} index Index of the changed property. - * @returns {void} - */ - const onChangeProperty = (property, index) => { - const newValue = [...value]; - - newValue[index] = { ...value[index], ...property }; - - onChange(newValue); - }; - - return ( - - {value.map((value, index) => ( - - { - onChangeProperty(value, index); - }} - /> - - ))} - - ); -}; diff --git a/assets/js/weighting/components/post-type/property.js b/assets/js/weighting/components/post-type/property.js index b4715610fa..6d2e126255 100644 --- a/assets/js/weighting/components/post-type/property.js +++ b/assets/js/weighting/components/post-type/property.js @@ -23,7 +23,7 @@ import WeightControl from '../common/weight-control'; * @returns {WPElement} Component element. */ export default ({ label, onChange, originalValue, value }) => { - const { indexable, searchable, weight } = value; + const { enabled = true, weight = 99 } = value; /** * Is the current value different to the original. @@ -33,21 +33,11 @@ export default ({ label, onChange, originalValue, value }) => { /** * Handle change of indexable. * - * @param {boolean} indexable New indexable value. + * @param {boolean} enabled New indexable value. * @returns {void} */ - const onChangeIndex = (indexable) => { - onChange({ ...value, indexable, searchable: false }); - }; - - /** - * Handle change of searchable. - * - * @param {boolean} searchable New searchable value. - * @returns {void} - */ - const onChangeSearchable = (searchable) => { - onChange({ ...value, indexable: true, searchable }); + const onChangeEnabled = (enabled) => { + onChange({ ...value, enabled }); }; /** @@ -65,8 +55,8 @@ export default ({ label, onChange, originalValue, value }) => { * * @returns {void} */ - const onUndo = () => { - onChange({ ...originalValue }); + const onReset = () => { + onChange(originalValue); }; return ( @@ -76,29 +66,21 @@ export default ({ label, onChange, originalValue, value }) => {
- +
- +
diff --git a/assets/js/weighting/config.js b/assets/js/weighting/config.js new file mode 100644 index 0000000000..c8b1826783 --- /dev/null +++ b/assets/js/weighting/config.js @@ -0,0 +1,6 @@ +/** + * Window dependencies. + */ +const { weightableFields, weightingConfiguration } = window.epWeighting; + +export { weightableFields, weightingConfiguration }; diff --git a/assets/js/weighting/index.js b/assets/js/weighting/index.js index 29d5dab8a6..1f387798cc 100644 --- a/assets/js/weighting/index.js +++ b/assets/js/weighting/index.js @@ -8,7 +8,7 @@ import { cloneDeep, isEqual } from 'lodash'; /** * Internal Dependencies. */ -import { dummyData } from './dummyData'; +import { weightableFields, weightingConfiguration } from './config'; import PostType from './components/post-type'; import Save from './components/save'; @@ -18,8 +18,8 @@ import Save from './components/save'; * @returns {WPElement} Element. */ const App = () => { - const [data, setData] = useState(cloneDeep(dummyData)); - const [savedData, setSavedData] = useState(cloneDeep(dummyData)); + const [data, setData] = useState(cloneDeep(weightingConfiguration)); + const [savedData, setSavedData] = useState(cloneDeep(weightingConfiguration)); const [isBusy, setIsBusy] = useState(false); /** @@ -31,10 +31,13 @@ const App = () => { * Handle data change. * * @param {Array} value Updated data. + * @param {string} postType Updated post type. * @returns {void} */ - const onChange = (value) => { - setData(value); + const onChangePostType = (value, postType) => { + const newData = { ...data, [postType]: value }; + + setData(newData); }; /** @@ -81,19 +84,16 @@ const App = () => {

- {data.map((value, index) => ( + {Object.entries(weightableFields).map(([key, groups]) => ( { - const newValue = [...data]; - - newValue.splice(index, 1, value); - - onChange(newValue); + onChangePostType(value, key); }} - originalValue={savedData[index]} - value={value} + originalValues={savedData[key]} + values={data[key]} /> ))} diff --git a/assets/js/weighting/utilities.js b/assets/js/weighting/utilities.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/includes/classes/Feature/Search/Weighting.php b/includes/classes/Feature/Search/Weighting.php index 783118c615..b9d86d09c0 100644 --- a/includes/classes/Feature/Search/Weighting.php +++ b/includes/classes/Feature/Search/Weighting.php @@ -8,6 +8,7 @@ namespace ElasticPress\Feature\Search; use ElasticPress\Features; +use ElasticPress\Indexables; use ElasticPress\Indexable\Post\Post; use ElasticPress\Utils as Utils; @@ -40,32 +41,6 @@ public function setup() { add_filter( 'ep_query_weighting_fields', [ $this, 'adjust_weight_for_cross_fields' ], 10, 5 ); } - /** - * Returns unique list of meta keys - * - * @param string $post_type Post type - * - * @return array - */ - public function get_meta_keys_for_post_type( $post_type ) { - - - $meta_keys = array(); - $posts = get_posts( array( 'post_type' => $post_type, 'limit' => -1 ) ); - - - foreach ( $posts as $post ) { - $post_meta_keys = get_post_custom_keys( $post->ID ); - var_dump($post_meta_keys); - $meta_keys = array_merge( $meta_keys, $post_meta_keys ); - } - - // Use array_unique to remove duplicate meta_keys that we received from all posts - // Use array_values to reset the index of the array - return array_values( array_unique( $meta_keys ) ); - - } - /** * Returns a grouping of all the fields that support weighting for the post type * @@ -125,6 +100,29 @@ public function get_weightable_fields_for_post_type( $post_type ) { } } + /** + * TODO: Meta keys per post type? + */ + $fields['meta'] = [ + 'label' => 'Metadata', + 'children' => [], + ]; + + try { + $meta_keys = Indexables::factory()->get( 'post' )->get_distinct_meta_field_keys(); + } catch ( \Throwable $th ) { + $meta_keys = []; + } + + foreach ( $meta_keys as $meta_key ) { + $key = "meta.$meta_key.value"; + + $fields['meta']['children'][$key] = [ + 'key' => $key, + 'label' => $meta_key, + ]; + } + /** * Filter weighting fields for a post type * @@ -136,6 +134,23 @@ public function get_weightable_fields_for_post_type( $post_type ) { return apply_filters( 'ep_weighting_fields_for_post_type', $fields, $post_type ); } + /** + * Get weightable fields for all searchable post types. + * + * @since 4.4.0 + * @return array + */ + public function get_weightable_fields() { + $weightable = array(); + $post_types = Features::factory()->get_registered_feature( 'search' )->get_searchable_post_types(); + + foreach ( $post_types as $post_type ) { + $weightable[ $post_type ] = $this->get_weightable_fields_for_post_type( $post_type ); + } + + return $weightable; + } + /** * Returns default settings for any post type * @@ -190,6 +205,26 @@ public function get_post_type_default_settings( $post_type ) { } } + /** + * TODO: Meta keys per post type? + */ + $indexable = Indexables::factory()->get( 'post' ); + + try { + $meta_keys = $indexable->get_distinct_meta_field_keys(); + } catch ( \Throwable $th ) { + $meta_keys = []; + } + + foreach ( $meta_keys as $meta_key ) { + $key = "meta.$meta_key.value"; + + $post_type_defaults[ $key ] = [ + 'enabled' => false, + 'weight' => 1, + ]; + } + /** * Filter weighting defaults for post type * @@ -217,6 +252,28 @@ public function get_weighting_configuration() { return apply_filters( 'ep_weighting_configuration', get_option( 'elasticpress_weighting', [] ) ); } + /** + * Returns the current weighting configuration with defaults for any + * missing properties. + * + * @return array Current weighting configuration with defaults. + * @since 4.4.0 + */ + public function get_weighting_configuration_with_defaults() { + $search = Features::factory()->get_registered_feature( 'search' ); + $post_types = $search->get_searchable_post_types(); + $weighting = $this->get_weighting_configuration(); + + foreach ( $post_types as $post_type ) { + $config = isset( $weighting[ $post_type ] ) ? $weighting[ $post_type ] : array(); + $default = $this->get_post_type_default_settings( $post_type ); + + $weighting[ $post_type ] = wp_parse_args( $config, $default ); + } + + return $weighting; + } + /** * Adds the submenu page for controlling weighting */ diff --git a/includes/dashboard.php b/includes/dashboard.php index cf1a4b2089..18f21c1bdb 100644 --- a/includes/dashboard.php +++ b/includes/dashboard.php @@ -506,6 +506,17 @@ function action_admin_enqueue_dashboard_scripts() { Utils\get_asset_info( 'weighting-script', 'version' ), true ); + + $weighting = Features::factory()->get_registered_feature( 'search' )->weighting; + + wp_localize_script( + 'ep_weighting_script', + 'epWeighting', + array( + 'weightableFields' => $weighting->get_weightable_fields(), + 'weightingConfiguration' => $weighting->get_weighting_configuration_with_defaults(), + ) + ); } if ( in_array( Screen::factory()->get_current_screen(), [ 'dashboard', 'install' ], true ) ) { diff --git a/package-lock.json b/package-lock.json index 60b033c52c..6b2d7f3f20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,6 @@ "focus-trap-react": "^8.8.2", "prop-types": "^15.8.1", "react-beautiful-dnd": "^11.0.5", - "react-select": "^5.4.0", "react-slider": "^1.3.1", "uuid": "^8.3.2" }, @@ -148,6 +147,7 @@ }, "node_modules/@ampproject/remapping": { "version": "2.1.2", + "dev": true, "license": "Apache-2.0", "dependencies": { "@jridgewell/trace-mapping": "^0.3.0" @@ -158,6 +158,7 @@ }, "node_modules/@babel/code-frame": { "version": "7.16.7", + "dev": true, "license": "MIT", "dependencies": { "@babel/highlight": "^7.16.7" @@ -168,6 +169,7 @@ }, "node_modules/@babel/compat-data": { "version": "7.17.0", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -175,6 +177,7 @@ }, "node_modules/@babel/core": { "version": "7.17.5", + "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.1.0", @@ -220,6 +223,7 @@ }, "node_modules/@babel/generator": { "version": "7.17.3", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.17.0", @@ -255,6 +259,7 @@ }, "node_modules/@babel/helper-compilation-targets": { "version": "7.16.7", + "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.16.4", @@ -324,6 +329,7 @@ }, "node_modules/@babel/helper-environment-visitor": { "version": "7.16.7", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.16.7" @@ -345,6 +351,7 @@ }, "node_modules/@babel/helper-function-name": { "version": "7.16.7", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-get-function-arity": "^7.16.7", @@ -357,6 +364,7 @@ }, "node_modules/@babel/helper-get-function-arity": { "version": "7.16.7", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.16.7" @@ -367,6 +375,7 @@ }, "node_modules/@babel/helper-hoist-variables": { "version": "7.16.7", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.16.7" @@ -388,6 +397,7 @@ }, "node_modules/@babel/helper-module-imports": { "version": "7.16.7", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.16.7" @@ -398,6 +408,7 @@ }, "node_modules/@babel/helper-module-transforms": { "version": "7.17.6", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-environment-visitor": "^7.16.7", @@ -428,6 +439,7 @@ "version": "7.19.0", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -462,6 +474,7 @@ }, "node_modules/@babel/helper-simple-access": { "version": "7.16.7", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.16.7" @@ -483,6 +496,7 @@ }, "node_modules/@babel/helper-split-export-declaration": { "version": "7.16.7", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.16.7" @@ -493,6 +507,7 @@ }, "node_modules/@babel/helper-validator-identifier": { "version": "7.16.7", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -500,6 +515,7 @@ }, "node_modules/@babel/helper-validator-option": { "version": "7.16.7", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -521,6 +537,7 @@ }, "node_modules/@babel/helpers": { "version": "7.17.2", + "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.16.7", @@ -533,6 +550,7 @@ }, "node_modules/@babel/highlight": { "version": "7.16.10", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", @@ -545,6 +563,7 @@ }, "node_modules/@babel/parser": { "version": "7.17.3", + "dev": true, "license": "MIT", "bin": { "parser": "bin/babel-parser.js" @@ -911,6 +930,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.18.6" }, @@ -1808,6 +1828,7 @@ }, "node_modules/@babel/template": { "version": "7.16.7", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.16.7", @@ -1820,6 +1841,7 @@ }, "node_modules/@babel/traverse": { "version": "7.17.3", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.16.7", @@ -1839,6 +1861,7 @@ }, "node_modules/@babel/types": { "version": "7.17.0", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", @@ -2013,117 +2036,6 @@ "ms": "^2.1.1" } }, - "node_modules/@emotion/babel-plugin": { - "version": "11.10.2", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.2.tgz", - "integrity": "sha512-xNQ57njWTFVfPAc3cjfuaPdsgLp5QOSuRsj9MA6ndEhH/AzuZM86qIQzt6rq+aGBwj3n5/TkLmU5lhAfdRmogA==", - "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/plugin-syntax-jsx": "^7.17.12", - "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.0", - "@emotion/memoize": "^0.8.0", - "@emotion/serialize": "^1.1.0", - "babel-plugin-macros": "^3.1.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.0.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@emotion/cache": { - "version": "11.10.3", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.3.tgz", - "integrity": "sha512-Psmp/7ovAa8appWh3g51goxu/z3iVms7JXOreq136D8Bbn6dYraPnmL6mdM8GThEx9vwSn92Fz+mGSjBzN8UPQ==", - "dependencies": { - "@emotion/memoize": "^0.8.0", - "@emotion/sheet": "^1.2.0", - "@emotion/utils": "^1.2.0", - "@emotion/weak-memoize": "^0.3.0", - "stylis": "4.0.13" - } - }, - "node_modules/@emotion/hash": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz", - "integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==" - }, - "node_modules/@emotion/memoize": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", - "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" - }, - "node_modules/@emotion/react": { - "version": "11.10.4", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.4.tgz", - "integrity": "sha512-j0AkMpr6BL8gldJZ6XQsQ8DnS9TxEQu1R+OGmDZiWjBAJtCcbt0tS3I/YffoqHXxH6MjgI7KdMbYKw3MEiU9eA==", - "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.10.0", - "@emotion/cache": "^11.10.0", - "@emotion/serialize": "^1.1.0", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@emotion/utils": "^1.2.0", - "@emotion/weak-memoize": "^0.3.0", - "hoist-non-react-statics": "^3.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@types/react": { - "optional": true - } - } - }, - "node_modules/@emotion/serialize": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.0.tgz", - "integrity": "sha512-F1ZZZW51T/fx+wKbVlwsfchr5q97iW8brAnXmsskz4d0hVB4O3M/SiA3SaeH06x02lSNzkkQv+n3AX3kCXKSFA==", - "dependencies": { - "@emotion/hash": "^0.9.0", - "@emotion/memoize": "^0.8.0", - "@emotion/unitless": "^0.8.0", - "@emotion/utils": "^1.2.0", - "csstype": "^3.0.2" - } - }, - "node_modules/@emotion/sheet": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.0.tgz", - "integrity": "sha512-OiTkRgpxescko+M51tZsMq7Puu/KP55wMT8BgpcXVG2hqXc0Vo0mfymJ/Uj24Hp0i083ji/o0aLddh08UEjq8w==" - }, - "node_modules/@emotion/unitless": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", - "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" - }, - "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", - "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==", - "peerDependencies": { - "react": ">=16.8.0" - } - }, - "node_modules/@emotion/utils": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz", - "integrity": "sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==" - }, - "node_modules/@emotion/weak-memoize": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz", - "integrity": "sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==" - }, "node_modules/@es-joy/jsdoccomment": { "version": "0.20.1", "dev": true, @@ -2866,6 +2778,7 @@ }, "node_modules/@jridgewell/resolve-uri": { "version": "3.0.5", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -2892,12 +2805,14 @@ }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.11", + "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.14", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -3603,6 +3518,7 @@ }, "node_modules/@types/parse-json": { "version": "4.0.0", + "dev": true, "license": "MIT" }, "node_modules/@types/prettier": { @@ -3650,14 +3566,6 @@ "redux": "^4.0.0" } }, - "node_modules/@types/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==", - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -4630,6 +4538,7 @@ }, "node_modules/ansi-styles": { "version": "3.2.1", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^1.9.0" @@ -5052,20 +4961,6 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "dependencies": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - }, - "engines": { - "node": ">=10", - "npm": ">=6" - } - }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.3.1", "dev": true, @@ -5324,6 +5219,7 @@ }, "node_modules/browserslist": { "version": "4.19.3", + "dev": true, "license": "MIT", "dependencies": { "caniuse-lite": "^1.0.30001312", @@ -5449,6 +5345,7 @@ }, "node_modules/callsites": { "version": "3.1.0", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -5521,6 +5418,7 @@ "version": "1.0.30001375", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001375.tgz", "integrity": "sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==", + "dev": true, "funding": [ { "type": "opencollective", @@ -5550,6 +5448,7 @@ }, "node_modules/chalk": { "version": "2.4.2", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", @@ -5562,6 +5461,7 @@ }, "node_modules/chalk/node_modules/escape-string-regexp": { "version": "1.0.5", + "dev": true, "license": "MIT", "engines": { "node": ">=0.8.0" @@ -6049,6 +5949,7 @@ }, "node_modules/convert-source-map": { "version": "1.8.0", + "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.1.1" @@ -6232,6 +6133,7 @@ }, "node_modules/cosmiconfig": { "version": "7.0.1", + "dev": true, "license": "MIT", "dependencies": { "@types/parse-json": "^4.0.0", @@ -6777,6 +6679,7 @@ }, "node_modules/debug": { "version": "4.3.3", + "dev": true, "license": "MIT", "dependencies": { "ms": "2.1.2" @@ -7095,15 +6998,6 @@ "utila": "~0.4" } }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, "node_modules/dom-serializer": { "version": "1.3.2", "dev": true, @@ -7212,6 +7106,7 @@ }, "node_modules/electron-to-chromium": { "version": "1.4.75", + "dev": true, "license": "ISC" }, "node_modules/emittery": { @@ -7297,6 +7192,7 @@ }, "node_modules/error-ex": { "version": "1.3.2", + "dev": true, "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" @@ -7366,6 +7262,7 @@ }, "node_modules/escalade": { "version": "3.1.1", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -7378,6 +7275,7 @@ }, "node_modules/escape-string-regexp": { "version": "4.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -8674,11 +8572,6 @@ "dev": true, "license": "MIT" }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" - }, "node_modules/find-up": { "version": "5.0.0", "dev": true, @@ -8824,6 +8717,7 @@ }, "node_modules/function-bind": { "version": "1.1.1", + "dev": true, "license": "MIT" }, "node_modules/functional-red-black-tree": { @@ -8833,6 +8727,7 @@ }, "node_modules/gensync": { "version": "1.0.0-beta.2", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -9014,6 +8909,7 @@ }, "node_modules/globals": { "version": "11.12.0", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -9116,6 +9012,7 @@ }, "node_modules/has": { "version": "1.0.3", + "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.1" @@ -9134,6 +9031,7 @@ }, "node_modules/has-flag": { "version": "3.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -9579,6 +9477,7 @@ }, "node_modules/import-fresh": { "version": "3.3.0", + "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -9593,6 +9492,7 @@ }, "node_modules/import-fresh/node_modules/resolve-from": { "version": "4.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -9809,6 +9709,7 @@ }, "node_modules/is-arrayish": { "version": "0.2.1", + "dev": true, "license": "MIT" }, "node_modules/is-bigint": { @@ -9894,6 +9795,7 @@ }, "node_modules/is-core-module": { "version": "2.8.1", + "dev": true, "license": "MIT", "dependencies": { "has": "^1.0.3" @@ -12220,6 +12122,7 @@ }, "node_modules/jsesc": { "version": "2.5.2", + "dev": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -12241,6 +12144,7 @@ }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", + "dev": true, "license": "MIT" }, "node_modules/json-schema": { @@ -12270,6 +12174,7 @@ }, "node_modules/json5": { "version": "2.2.0", + "dev": true, "license": "MIT", "dependencies": { "minimist": "^1.2.5" @@ -12416,6 +12321,7 @@ }, "node_modules/lines-and-columns": { "version": "1.2.4", + "dev": true, "license": "MIT" }, "node_modules/linkify-it": { @@ -13160,7 +13066,8 @@ "node_modules/minimist": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true }, "node_modules/minimist-options": { "version": "4.1.0", @@ -13204,6 +13111,7 @@ }, "node_modules/ms": { "version": "2.1.2", + "dev": true, "license": "MIT" }, "node_modules/multicast-dns": { @@ -13282,6 +13190,7 @@ }, "node_modules/node-releases": { "version": "2.0.2", + "dev": true, "license": "MIT" }, "node_modules/normalize-package-data": { @@ -13812,6 +13721,7 @@ }, "node_modules/parent-module": { "version": "1.0.1", + "dev": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" @@ -13839,6 +13749,7 @@ }, "node_modules/parse-json": { "version": "5.2.0", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", @@ -13901,6 +13812,7 @@ }, "node_modules/path-parse": { "version": "1.0.7", + "dev": true, "license": "MIT" }, "node_modules/path-to-regexp": { @@ -13910,6 +13822,7 @@ }, "node_modules/path-type": { "version": "4.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -13927,6 +13840,7 @@ }, "node_modules/picocolors": { "version": "1.0.0", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -15705,24 +15619,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-select": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.4.0.tgz", - "integrity": "sha512-CjE9RFLUvChd5SdlfG4vqxZd55AZJRrLrHzkQyTYeHlpOztqcgnyftYAolJ0SGsBev6zAs6qFrjm6KU3eo2hzg==", - "dependencies": { - "@babel/runtime": "^7.12.0", - "@emotion/cache": "^11.4.0", - "@emotion/react": "^11.8.1", - "@types/react-transition-group": "^4.4.0", - "memoize-one": "^5.0.0", - "prop-types": "^15.6.0", - "react-transition-group": "^4.3.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/react-slider": { "version": "1.3.1", "license": "MIT", @@ -15731,21 +15627,6 @@ "react": "^16 || ^17" } }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, "node_modules/read-cache": { "version": "1.0.0", "dev": true, @@ -16106,6 +15987,7 @@ }, "node_modules/resolve": { "version": "1.22.0", + "dev": true, "license": "MIT", "dependencies": { "is-core-module": "^2.8.1", @@ -16268,6 +16150,7 @@ }, "node_modules/safe-buffer": { "version": "5.1.2", + "dev": true, "license": "MIT" }, "node_modules/safer-buffer": { @@ -16383,6 +16266,7 @@ }, "node_modules/semver": { "version": "6.3.0", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -16646,6 +16530,7 @@ }, "node_modules/source-map": { "version": "0.5.7", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -17327,11 +17212,6 @@ "node": ">=8" } }, - "node_modules/stylis": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz", - "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==" - }, "node_modules/sugarss": { "version": "3.0.3", "dev": true, @@ -17349,6 +17229,7 @@ }, "node_modules/supports-color": { "version": "5.5.0", + "dev": true, "license": "MIT", "dependencies": { "has-flag": "^3.0.0" @@ -17390,6 +17271,7 @@ }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -17698,6 +17580,7 @@ }, "node_modules/to-fast-properties": { "version": "2.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -18951,6 +18834,7 @@ }, "node_modules/yaml": { "version": "1.10.2", + "dev": true, "license": "ISC", "engines": { "node": ">= 6" @@ -19089,21 +18973,25 @@ }, "@ampproject/remapping": { "version": "2.1.2", + "dev": true, "requires": { "@jridgewell/trace-mapping": "^0.3.0" } }, "@babel/code-frame": { "version": "7.16.7", + "dev": true, "requires": { "@babel/highlight": "^7.16.7" } }, "@babel/compat-data": { - "version": "7.17.0" + "version": "7.17.0", + "dev": true }, "@babel/core": { "version": "7.17.5", + "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", @@ -19133,6 +19021,7 @@ }, "@babel/generator": { "version": "7.17.3", + "dev": true, "requires": { "@babel/types": "^7.17.0", "jsesc": "^2.5.1", @@ -19156,6 +19045,7 @@ }, "@babel/helper-compilation-targets": { "version": "7.16.7", + "dev": true, "requires": { "@babel/compat-data": "^7.16.4", "@babel/helper-validator-option": "^7.16.7", @@ -19200,6 +19090,7 @@ }, "@babel/helper-environment-visitor": { "version": "7.16.7", + "dev": true, "requires": { "@babel/types": "^7.16.7" } @@ -19213,6 +19104,7 @@ }, "@babel/helper-function-name": { "version": "7.16.7", + "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.16.7", "@babel/template": "^7.16.7", @@ -19221,12 +19113,14 @@ }, "@babel/helper-get-function-arity": { "version": "7.16.7", + "dev": true, "requires": { "@babel/types": "^7.16.7" } }, "@babel/helper-hoist-variables": { "version": "7.16.7", + "dev": true, "requires": { "@babel/types": "^7.16.7" } @@ -19240,12 +19134,14 @@ }, "@babel/helper-module-imports": { "version": "7.16.7", + "dev": true, "requires": { "@babel/types": "^7.16.7" } }, "@babel/helper-module-transforms": { "version": "7.17.6", + "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-module-imports": "^7.16.7", @@ -19267,7 +19163,8 @@ "@babel/helper-plugin-utils": { "version": "7.19.0", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", - "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==" + "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==", + "dev": true }, "@babel/helper-remap-async-to-generator": { "version": "7.16.8", @@ -19291,6 +19188,7 @@ }, "@babel/helper-simple-access": { "version": "7.16.7", + "dev": true, "requires": { "@babel/types": "^7.16.7" } @@ -19304,15 +19202,18 @@ }, "@babel/helper-split-export-declaration": { "version": "7.16.7", + "dev": true, "requires": { "@babel/types": "^7.16.7" } }, "@babel/helper-validator-identifier": { - "version": "7.16.7" + "version": "7.16.7", + "dev": true }, "@babel/helper-validator-option": { - "version": "7.16.7" + "version": "7.16.7", + "dev": true }, "@babel/helper-wrap-function": { "version": "7.16.8", @@ -19326,6 +19227,7 @@ }, "@babel/helpers": { "version": "7.17.2", + "dev": true, "requires": { "@babel/template": "^7.16.7", "@babel/traverse": "^7.17.0", @@ -19334,6 +19236,7 @@ }, "@babel/highlight": { "version": "7.16.10", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", "chalk": "^2.0.0", @@ -19341,7 +19244,8 @@ } }, "@babel/parser": { - "version": "7.17.3" + "version": "7.17.3", + "dev": true }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.16.7", @@ -19547,6 +19451,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.18.6" } @@ -20081,6 +19986,7 @@ }, "@babel/template": { "version": "7.16.7", + "dev": true, "requires": { "@babel/code-frame": "^7.16.7", "@babel/parser": "^7.16.7", @@ -20089,6 +19995,7 @@ }, "@babel/traverse": { "version": "7.17.3", + "dev": true, "requires": { "@babel/code-frame": "^7.16.7", "@babel/generator": "^7.17.3", @@ -20104,6 +20011,7 @@ }, "@babel/types": { "version": "7.17.0", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", "to-fast-properties": "^2.0.0" @@ -20213,100 +20121,6 @@ } } }, - "@emotion/babel-plugin": { - "version": "11.10.2", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.2.tgz", - "integrity": "sha512-xNQ57njWTFVfPAc3cjfuaPdsgLp5QOSuRsj9MA6ndEhH/AzuZM86qIQzt6rq+aGBwj3n5/TkLmU5lhAfdRmogA==", - "requires": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/plugin-syntax-jsx": "^7.17.12", - "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.0", - "@emotion/memoize": "^0.8.0", - "@emotion/serialize": "^1.1.0", - "babel-plugin-macros": "^3.1.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.0.13" - } - }, - "@emotion/cache": { - "version": "11.10.3", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.3.tgz", - "integrity": "sha512-Psmp/7ovAa8appWh3g51goxu/z3iVms7JXOreq136D8Bbn6dYraPnmL6mdM8GThEx9vwSn92Fz+mGSjBzN8UPQ==", - "requires": { - "@emotion/memoize": "^0.8.0", - "@emotion/sheet": "^1.2.0", - "@emotion/utils": "^1.2.0", - "@emotion/weak-memoize": "^0.3.0", - "stylis": "4.0.13" - } - }, - "@emotion/hash": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz", - "integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==" - }, - "@emotion/memoize": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", - "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" - }, - "@emotion/react": { - "version": "11.10.4", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.4.tgz", - "integrity": "sha512-j0AkMpr6BL8gldJZ6XQsQ8DnS9TxEQu1R+OGmDZiWjBAJtCcbt0tS3I/YffoqHXxH6MjgI7KdMbYKw3MEiU9eA==", - "requires": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.10.0", - "@emotion/cache": "^11.10.0", - "@emotion/serialize": "^1.1.0", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@emotion/utils": "^1.2.0", - "@emotion/weak-memoize": "^0.3.0", - "hoist-non-react-statics": "^3.3.1" - } - }, - "@emotion/serialize": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.0.tgz", - "integrity": "sha512-F1ZZZW51T/fx+wKbVlwsfchr5q97iW8brAnXmsskz4d0hVB4O3M/SiA3SaeH06x02lSNzkkQv+n3AX3kCXKSFA==", - "requires": { - "@emotion/hash": "^0.9.0", - "@emotion/memoize": "^0.8.0", - "@emotion/unitless": "^0.8.0", - "@emotion/utils": "^1.2.0", - "csstype": "^3.0.2" - } - }, - "@emotion/sheet": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.0.tgz", - "integrity": "sha512-OiTkRgpxescko+M51tZsMq7Puu/KP55wMT8BgpcXVG2hqXc0Vo0mfymJ/Uj24Hp0i083ji/o0aLddh08UEjq8w==" - }, - "@emotion/unitless": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", - "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" - }, - "@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", - "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==", - "requires": {} - }, - "@emotion/utils": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz", - "integrity": "sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==" - }, - "@emotion/weak-memoize": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz", - "integrity": "sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==" - }, "@es-joy/jsdoccomment": { "version": "0.20.1", "dev": true, @@ -20792,7 +20606,8 @@ } }, "@jridgewell/resolve-uri": { - "version": "3.0.5" + "version": "3.0.5", + "dev": true }, "@jridgewell/set-array": { "version": "1.1.2", @@ -20811,12 +20626,14 @@ } }, "@jridgewell/sourcemap-codec": { - "version": "1.4.11" + "version": "1.4.11", + "dev": true }, "@jridgewell/trace-mapping": { "version": "0.3.14", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "dev": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -21272,7 +21089,8 @@ "dev": true }, "@types/parse-json": { - "version": "4.0.0" + "version": "4.0.0", + "dev": true }, "@types/prettier": { "version": "2.4.4", @@ -21312,14 +21130,6 @@ "redux": "^4.0.0" } }, - "@types/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==", - "requires": { - "@types/react": "*" - } - }, "@types/responselike": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", @@ -21973,6 +21783,7 @@ }, "ansi-styles": { "version": "3.2.1", + "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -22228,16 +22039,6 @@ "@types/babel__traverse": "^7.0.6" } }, - "babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "requires": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - } - }, "babel-plugin-polyfill-corejs2": { "version": "0.3.1", "dev": true, @@ -22412,6 +22213,7 @@ }, "browserslist": { "version": "4.19.3", + "dev": true, "requires": { "caniuse-lite": "^1.0.30001312", "electron-to-chromium": "^1.4.71", @@ -22485,7 +22287,8 @@ } }, "callsites": { - "version": "3.1.0" + "version": "3.1.0", + "dev": true }, "camel-case": { "version": "4.1.2", @@ -22531,7 +22334,8 @@ "caniuse-lite": { "version": "1.0.30001375", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001375.tgz", - "integrity": "sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==" + "integrity": "sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==", + "dev": true }, "caseless": { "version": "0.12.0", @@ -22546,6 +22350,7 @@ }, "chalk": { "version": "2.4.2", + "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -22553,7 +22358,8 @@ }, "dependencies": { "escape-string-regexp": { - "version": "1.0.5" + "version": "1.0.5", + "dev": true } } }, @@ -22868,6 +22674,7 @@ }, "convert-source-map": { "version": "1.8.0", + "dev": true, "requires": { "safe-buffer": "~5.1.1" } @@ -22976,6 +22783,7 @@ }, "cosmiconfig": { "version": "7.0.1", + "dev": true, "requires": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -23328,6 +23136,7 @@ }, "debug": { "version": "4.3.3", + "dev": true, "requires": { "ms": "2.1.2" } @@ -23533,15 +23342,6 @@ "utila": "~0.4" } }, - "dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "requires": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, "dom-serializer": { "version": "1.3.2", "dev": true, @@ -23615,7 +23415,8 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.75" + "version": "1.4.75", + "dev": true }, "emittery": { "version": "0.8.1", @@ -23665,6 +23466,7 @@ }, "error-ex": { "version": "1.3.2", + "dev": true, "requires": { "is-arrayish": "^0.2.1" } @@ -23716,14 +23518,16 @@ } }, "escalade": { - "version": "3.1.1" + "version": "3.1.1", + "dev": true }, "escape-html": { "version": "1.0.3", "dev": true }, "escape-string-regexp": { - "version": "4.0.0" + "version": "4.0.0", + "dev": true }, "escodegen": { "version": "2.0.0", @@ -24568,11 +24372,6 @@ "version": "0.3.1", "dev": true }, - "find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" - }, "find-up": { "version": "5.0.0", "dev": true, @@ -24653,14 +24452,16 @@ "dev": true }, "function-bind": { - "version": "1.1.1" + "version": "1.1.1", + "dev": true }, "functional-red-black-tree": { "version": "1.0.1", "dev": true }, "gensync": { - "version": "1.0.0-beta.2" + "version": "1.0.0-beta.2", + "dev": true }, "get-caller-file": { "version": "2.0.5", @@ -24772,7 +24573,8 @@ } }, "globals": { - "version": "11.12.0" + "version": "11.12.0", + "dev": true }, "globby": { "version": "11.1.0", @@ -24837,6 +24639,7 @@ }, "has": { "version": "1.0.3", + "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -24846,7 +24649,8 @@ "dev": true }, "has-flag": { - "version": "3.0.0" + "version": "3.0.0", + "dev": true }, "has-symbols": { "version": "1.0.3", @@ -25116,13 +24920,15 @@ }, "import-fresh": { "version": "3.3.0", + "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" }, "dependencies": { "resolve-from": { - "version": "4.0.0" + "version": "4.0.0", + "dev": true } } }, @@ -25254,7 +25060,8 @@ } }, "is-arrayish": { - "version": "0.2.1" + "version": "0.2.1", + "dev": true }, "is-bigint": { "version": "1.0.4", @@ -25295,6 +25102,7 @@ }, "is-core-module": { "version": "2.8.1", + "dev": true, "requires": { "has": "^1.0.3" } @@ -26769,7 +26577,8 @@ } }, "jsesc": { - "version": "2.5.2" + "version": "2.5.2", + "dev": true }, "json-buffer": { "version": "3.0.1", @@ -26782,7 +26591,8 @@ "dev": true }, "json-parse-even-better-errors": { - "version": "2.3.1" + "version": "2.3.1", + "dev": true }, "json-schema": { "version": "0.4.0", @@ -26806,6 +26616,7 @@ }, "json5": { "version": "2.2.0", + "dev": true, "requires": { "minimist": "^1.2.5" } @@ -26898,7 +26709,8 @@ "dev": true }, "lines-and-columns": { - "version": "1.2.4" + "version": "1.2.4", + "dev": true }, "linkify-it": { "version": "3.0.3", @@ -27360,7 +27172,8 @@ "minimist": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true }, "minimist-options": { "version": "4.1.0", @@ -27388,7 +27201,8 @@ "dev": true }, "ms": { - "version": "2.1.2" + "version": "2.1.2", + "dev": true }, "multicast-dns": { "version": "6.2.3", @@ -27441,7 +27255,8 @@ "dev": true }, "node-releases": { - "version": "2.0.2" + "version": "2.0.2", + "dev": true }, "normalize-package-data": { "version": "2.5.0", @@ -27772,6 +27587,7 @@ }, "parent-module": { "version": "1.0.1", + "dev": true, "requires": { "callsites": "^3.0.0" } @@ -27790,6 +27606,7 @@ }, "parse-json": { "version": "5.2.0", + "dev": true, "requires": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -27826,14 +27643,16 @@ "dev": true }, "path-parse": { - "version": "1.0.7" + "version": "1.0.7", + "dev": true }, "path-to-regexp": { "version": "0.1.7", "dev": true }, "path-type": { - "version": "4.0.0" + "version": "4.0.0", + "dev": true }, "pend": { "version": "1.2.0", @@ -27844,7 +27663,8 @@ "dev": true }, "picocolors": { - "version": "1.0.0" + "version": "1.0.0", + "dev": true }, "picomatch": { "version": "2.3.1", @@ -28891,35 +28711,10 @@ "version": "0.11.0", "dev": true }, - "react-select": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.4.0.tgz", - "integrity": "sha512-CjE9RFLUvChd5SdlfG4vqxZd55AZJRrLrHzkQyTYeHlpOztqcgnyftYAolJ0SGsBev6zAs6qFrjm6KU3eo2hzg==", - "requires": { - "@babel/runtime": "^7.12.0", - "@emotion/cache": "^11.4.0", - "@emotion/react": "^11.8.1", - "@types/react-transition-group": "^4.4.0", - "memoize-one": "^5.0.0", - "prop-types": "^15.6.0", - "react-transition-group": "^4.3.0" - } - }, "react-slider": { "version": "1.3.1", "requires": {} }, - "react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "requires": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - } - }, "read-cache": { "version": "1.0.0", "dev": true, @@ -29161,6 +28956,7 @@ }, "resolve": { "version": "1.22.0", + "dev": true, "requires": { "is-core-module": "^2.8.1", "path-parse": "^1.0.7", @@ -29256,7 +29052,8 @@ } }, "safe-buffer": { - "version": "5.1.2" + "version": "5.1.2", + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -29315,7 +29112,8 @@ } }, "semver": { - "version": "6.3.0" + "version": "6.3.0", + "dev": true }, "send": { "version": "0.17.2", @@ -29514,7 +29312,8 @@ } }, "source-map": { - "version": "0.5.7" + "version": "0.5.7", + "dev": true }, "source-map-js": { "version": "1.0.2", @@ -29976,11 +29775,6 @@ } } }, - "stylis": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz", - "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==" - }, "sugarss": { "version": "3.0.3", "dev": true, @@ -29990,6 +29784,7 @@ }, "supports-color": { "version": "5.5.0", + "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -30016,7 +29811,8 @@ } }, "supports-preserve-symlinks-flag": { - "version": "1.0.0" + "version": "1.0.0", + "dev": true }, "svg-parser": { "version": "2.0.4", @@ -30212,7 +30008,8 @@ "dev": true }, "to-fast-properties": { - "version": "2.0.0" + "version": "2.0.0", + "dev": true }, "to-regex-range": { "version": "5.0.1", @@ -30991,7 +30788,8 @@ "dev": true }, "yaml": { - "version": "1.10.2" + "version": "1.10.2", + "dev": true }, "yargs": { "version": "17.3.1", From 8dc36c81984dd67ca78732283a4d6d0ad41dd769 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Fri, 14 Oct 2022 00:08:15 +1100 Subject: [PATCH 05/63] WIP: Progress on dashboard, handle updated data format. --- assets/css/weighting.css | 20 +- .../components/{save.js => actions.js} | 13 + .../components/common/delete-button.js | 19 ++ .../components/common/undo-button.js | 3 + .../components/common/weight-control.js | 3 + assets/js/weighting/components/post-type.js | 101 ++++++-- .../weighting/components/post-type/field.js | 123 ++++++++++ .../weighting/components/post-type/fields.js | 155 ++++++++++++ .../weighting/components/post-type/group.js | 121 ---------- .../components/post-type/property.js | 89 ------- assets/js/weighting/config.js | 6 - assets/js/weighting/dummyData.js | 224 ------------------ assets/js/weighting/index.js | 79 +++--- assets/js/weighting/utilities.js | 0 .../classes/Feature/Documents/Documents.php | 5 +- includes/classes/Feature/Search/Weighting.php | 70 +++--- .../Feature/SearchOrdering/SearchOrdering.php | 7 +- .../Feature/WooCommerce/WooCommerce.php | 12 +- includes/dashboard.php | 8 +- 19 files changed, 506 insertions(+), 552 deletions(-) rename assets/js/weighting/components/{save.js => actions.js} (66%) create mode 100644 assets/js/weighting/components/common/delete-button.js create mode 100644 assets/js/weighting/components/post-type/field.js create mode 100644 assets/js/weighting/components/post-type/fields.js delete mode 100644 assets/js/weighting/components/post-type/group.js delete mode 100644 assets/js/weighting/components/post-type/property.js delete mode 100644 assets/js/weighting/config.js delete mode 100644 assets/js/weighting/dummyData.js delete mode 100644 assets/js/weighting/utilities.js diff --git a/assets/css/weighting.css b/assets/css/weighting.css index d77d3c45e4..d93ad04a2e 100644 --- a/assets/css/weighting.css +++ b/assets/css/weighting.css @@ -1,12 +1,12 @@ #ep-weighting-screen { & .components-panel { - margin-bottom: 2rem; + margin-bottom: 1rem; max-width: 800px; } } -.ep-weighting-property { +.ep-weighting-field { align-items: center; display: grid; grid-gap: 1em; @@ -28,27 +28,35 @@ margin-bottom: 0; } } + + @media ( max-width: 600px ) { + + & .components-range-control__wrapper { + display: none; + } + } } -.ep-weighting-property fieldset { +.ep-weighting-field fieldset { display: contents; } -.ep-weighting-property__name { +.ep-weighting-field__name { & h2 { color: inherit; - font-size: 15px; + font-size: 14px; margin: 0; } } -.ep-weighting-property__undo { +.ep-weighting-field__undo { grid-column-start: 5; justify-self: end; } +.ep-weighting-delete, .ep-weighting-undo { &[disabled] { diff --git a/assets/js/weighting/components/save.js b/assets/js/weighting/components/actions.js similarity index 66% rename from assets/js/weighting/components/save.js rename to assets/js/weighting/components/actions.js index 65cf401154..a79ceef680 100644 --- a/assets/js/weighting/components/save.js +++ b/assets/js/weighting/components/actions.js @@ -2,10 +2,23 @@ * WordPress dependencies. */ import { Button, Panel, PanelBody, PanelRow } from '@wordpress/components'; +import { WPElement } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { undo } from '@wordpress/icons'; +/** + * Actions component. + * + * @param {object} props Component props. + * @param {boolean} props.isBusy Is the app busy? + * @param {boolean} props.isChanged Are there changes? + * @param {Function} props.onReset Reset handler. + * @returns {WPElement} Component element. + */ export default ({ isBusy, isChanged, onReset }) => { + /** + * Render. + */ return ( diff --git a/assets/js/weighting/components/common/delete-button.js b/assets/js/weighting/components/common/delete-button.js new file mode 100644 index 0000000000..64856aae83 --- /dev/null +++ b/assets/js/weighting/components/common/delete-button.js @@ -0,0 +1,19 @@ +/** + * WordPress dependencies. + */ +import { Button } from '@wordpress/components'; +import { WPElement } from '@wordpress/element'; +import { close } from '@wordpress/icons'; + +/** + * Undo button component. + * + * @param {object} props Component props. + * @returns {WPElement} Component element. + */ +export default (props) => { + /** + * Render. + */ + return + + ) : null} + + ); +}; diff --git a/assets/js/weighting/components/post-type/group.js b/assets/js/weighting/components/post-type/group.js deleted file mode 100644 index 3f6a52eeaf..0000000000 --- a/assets/js/weighting/components/post-type/group.js +++ /dev/null @@ -1,121 +0,0 @@ -/** - * WordPress dependencies. - */ -import { Button, PanelBody, PanelRow, SelectControl } from '@wordpress/components'; -import { useMemo, useState, WPElement } from '@wordpress/element'; - -/** - * Internal dependencies. - */ -import Property from './property'; - -/** - * Post type propertes component. - * - * @param {object} props Component props. - * @param {boolean} props.isEditable Whether to display as an editable list. - * @param {string} props.label Properties label. - * @param {Function} props.onChange Change handler. - * @param {object[]} props.originalValues Saved property values. - * @param {object[]} props.properties Post type properties. - * @param {object[]} props.values Current property values. - * @returns {WPElement} Component element. - */ -export default ({ isEditable, label, onChange, originalValues, properties, values }) => { - const [toAdd, setToAdd] = useState(''); - - /** - * Handle changes to a property. - * - * @param {object} value New property data. - * @param {number} key Property key. - * @returns {void} - */ - const onChangeProperty = (value, key) => { - const newValues = { ...values, [key]: value }; - - onChange(newValues); - }; - - /** - * Handle selecting a new property to enable. - * - * @param {string} key Key of property to enable. - * @returns {void} - */ - const onChangeToAdd = (key) => { - setToAdd(key); - }; - - /** - * Handle clicking to add a new property. - * - * @returns {void} - */ - const onClickAdd = () => { - const value = { ...values[toAdd], enabled: true }; - const newValues = { ...values, [toAdd]: value }; - - onChange(newValues); - setToAdd(''); - }; - - /** - * Options for properties that can be added to the list of weightable - * properties. - */ - const availableProperties = useMemo(() => { - return isEditable - ? Object.values(properties).map((p) => ({ - label: p.label, - value: p.key, - disabled: values[p.key].enabled, - })) - : null; - }, [isEditable, properties, values]); - - /** - * Properties that can be weighted. - * - * If the component is set to be editable this will only be properties that - * are indexed, otherwise it will be all properties. - */ - const weightableProperties = useMemo(() => { - return Object.values(properties).filter((p) => (isEditable ? values[p.key].enabled : true)); - }, [isEditable, properties, values]); - - return ( - - {weightableProperties.map(({ key, label }) => ( - - { - onChangeProperty(value, key); - }} - /> - - ))} - {availableProperties ? ( - - -   - - - ) : null} - - ); -}; diff --git a/assets/js/weighting/components/post-type/property.js b/assets/js/weighting/components/post-type/property.js deleted file mode 100644 index 6d2e126255..0000000000 --- a/assets/js/weighting/components/post-type/property.js +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Wordpress Dependencies. - */ -import { CheckboxControl } from '@wordpress/components'; -import { useMemo, WPElement } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; -import { isEqual } from 'lodash'; - -/** - * Internal dependencies. - */ -import UndoButton from '../common/undo-button'; -import WeightControl from '../common/weight-control'; - -/** - * Property settings component. - * - * @param {object} props Component props. - * @param {string} props.label Property label. - * @param {Function} props.onChange Change handler. - * @param {object} props.originalValue Original value. - * @param {object} props.value Values. - * @returns {WPElement} Component element. - */ -export default ({ label, onChange, originalValue, value }) => { - const { enabled = true, weight = 99 } = value; - - /** - * Is the current value different to the original. - */ - const isChanged = useMemo(() => !isEqual(originalValue, value), [originalValue, value]); - - /** - * Handle change of indexable. - * - * @param {boolean} enabled New indexable value. - * @returns {void} - */ - const onChangeEnabled = (enabled) => { - onChange({ ...value, enabled }); - }; - - /** - * Handle change of weighting. - * - * @param {number} weight New weight value. - * @returns {void} - */ - const onChangeWeight = (weight) => { - onChange({ ...value, weight }); - }; - - /** - * Handle clicking undo. - * - * @returns {void} - */ - const onReset = () => { - onChange(originalValue); - }; - - return ( -
-
- {label} -
- -
-
- -
-
- -
-
- -
-
-
- ); -}; diff --git a/assets/js/weighting/config.js b/assets/js/weighting/config.js deleted file mode 100644 index c8b1826783..0000000000 --- a/assets/js/weighting/config.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Window dependencies. - */ -const { weightableFields, weightingConfiguration } = window.epWeighting; - -export { weightableFields, weightingConfiguration }; diff --git a/assets/js/weighting/dummyData.js b/assets/js/weighting/dummyData.js deleted file mode 100644 index 19c44464b6..0000000000 --- a/assets/js/weighting/dummyData.js +++ /dev/null @@ -1,224 +0,0 @@ -export const dummyData = [ - { - label: 'Posts', - name: 'post', - indexable: true, - order: 0, - - attributes: [ - { - label: 'Title', - name: 'post_title', - indexable: true, - searchable: true, - weight: 40, - }, - { - label: 'Content', - name: 'post_content', - indexable: false, - searchable: false, - weight: 1, - }, - { - label: 'Excerpt', - name: 'post_excerpt', - indexable: false, - searchable: false, - weight: 1, - }, - { - label: 'Author', - name: 'post_author', - indexable: false, - searchable: false, - weight: 1, - }, - ], - - taxonomies: [ - { - label: 'Categories', - name: 'post_categories', - indexable: false, - searchable: false, - weight: 1, - }, - { - label: 'Tags', - name: 'post_tags', - indexable: false, - searchable: false, - weight: 1, - }, - { - label: 'Formats', - name: 'post_formats', - indexable: false, - searchable: false, - weight: 1, - }, - ], - - meta: [ - // { - // name: 'example_key', - // searchable: false, - // weight: 10, - // }, - // { - // name: 'another_key', - // searchable: false, - // weight: 10, - // }, - // { - // name: 'one_more_key', - // searchable: false, - // weight: 10, - // }, - ], - }, - { - label: 'Pages', - name: 'page', - indexable: true, - order: 1, - - attributes: [ - { - label: 'Title', - name: 'post_title', - indexable: false, - searchable: false, - weight: 1, - }, - { - label: 'Content', - name: 'post_content', - indexable: false, - searchable: false, - weight: 1, - }, - { - label: 'Excerpt', - name: 'post_excerpt', - indexable: false, - searchable: false, - weight: 1, - }, - { - label: 'Author', - name: 'post_author', - indexable: false, - searchable: false, - weight: 1, - }, - ], - - taxonomies: [ - { - label: 'Categories', - name: 'post_categories', - indexable: false, - searchable: false, - weight: 1, - }, - { - label: 'Tags', - name: 'post_tags', - indexable: false, - searchable: false, - weight: 1, - }, - { - label: 'Formats', - name: 'post_formats', - indexable: false, - searchable: false, - weight: 1, - }, - ], - - meta: [], - }, - { - label: 'Product', - name: 'product', - indexable: true, - order: 2, - - attributes: [ - { - label: 'Title', - name: 'post_title', - indexable: false, - searchable: false, - weight: 1, - }, - { - label: 'Content', - name: 'post_content', - indexable: false, - searchable: false, - weight: 1, - }, - { - label: 'Excerpt', - name: 'post_excerpt', - indexable: false, - searchable: false, - weight: 1, - }, - { - label: 'Author', - name: 'post_author', - indexable: false, - searchable: false, - weight: 1, - }, - ], - - taxonomies: [ - { - label: 'Categories', - name: 'post_categories', - indexable: false, - searchable: false, - weight: 1, - }, - { - label: 'Tags', - name: 'post_tags', - indexable: false, - searchable: false, - weight: 1, - }, - { - label: 'Formats', - name: 'post_formats', - indexable: false, - searchable: false, - weight: 1, - }, - ], - meta: [], - }, -]; - -export const dummyMetaKeys = [ - { - name: 'example_key', - searchable: false, - weight: 10, - }, - { - name: 'another_key', - searchable: false, - weight: 10, - }, - { - name: 'one_more_key', - searchable: false, - weight: 10, - }, -]; diff --git a/assets/js/weighting/index.js b/assets/js/weighting/index.js index 1f387798cc..eb7df0cc0f 100644 --- a/assets/js/weighting/index.js +++ b/assets/js/weighting/index.js @@ -8,36 +8,40 @@ import { cloneDeep, isEqual } from 'lodash'; /** * Internal Dependencies. */ -import { weightableFields, weightingConfiguration } from './config'; +import Actions from './components/actions'; import PostType from './components/post-type'; -import Save from './components/save'; /** - * component. + * Window dependencies. + */ + +/** + * Weighting settings app. * + * @param {object} props Component props. + * @param {object} props.weightableFields Weightable fields, indexed by post type. + * @param {object} props.weightingConfiguration Weighting configuration, indexed by post type. * @returns {WPElement} Element. */ -const App = () => { - const [data, setData] = useState(cloneDeep(weightingConfiguration)); - const [savedData, setSavedData] = useState(cloneDeep(weightingConfiguration)); +const App = ({ weightableFields, weightingConfiguration }) => { + const [currentData, setCurrentData] = useState({ ...weightingConfiguration }); + const [savedData, setSavedData] = useState({ ...weightingConfiguration }); const [isBusy, setIsBusy] = useState(false); /** * Is the current data different to the saved data. */ - const isChanged = useMemo(() => !isEqual(data, savedData), [data, savedData]); + const isChanged = useMemo(() => !isEqual(currentData, savedData), [currentData, savedData]); /** * Handle data change. * - * @param {Array} value Updated data. * @param {string} postType Updated post type. + * @param {Array} values Updated data. * @returns {void} */ - const onChangePostType = (value, postType) => { - const newData = { ...data, [postType]: value }; - - setData(newData); + const onChangePostType = (postType, values) => { + setCurrentData({ ...currentData, [postType]: values }); }; /** @@ -49,10 +53,14 @@ const App = () => { const onSubmit = (event) => { event.preventDefault(); - const savedData = cloneDeep(data); + const savedData = cloneDeep(currentData); setIsBusy(true); - setSavedData(savedData); + + setTimeout(() => { + setSavedData(savedData); + setIsBusy(false); + }, 1000); }; /** @@ -60,12 +68,13 @@ const App = () => { * * @returns {void} */ - const onUndo = () => { - const data = cloneDeep(savedData); - - setData(data); + const onReset = () => { + setCurrentData({ ...savedData }); }; + /** + * Render. + */ return (

{__('Manage Search Fields & Weighting', 'elasticpress')}

@@ -83,23 +92,35 @@ const App = () => { )}

- - {Object.entries(weightableFields).map(([key, groups]) => ( + {Object.entries(weightableFields).map(([postType, { groups, label }]) => ( { - onChangePostType(value, key); + key={postType} + label={label} + onChange={(values) => { + onChangePostType(postType, values); }} - originalValues={savedData[key]} - values={data[key]} + originalValues={savedData[postType]} + values={currentData[postType]} /> ))} - - + ); }; -render(, document.getElementById('ep-weighting-screen')); +/** + * Initialize. + * + * @returns {void} + */ +const init = () => { + const { weightableFields, weightingConfiguration } = window.epWeighting; + + render( + , + document.getElementById('ep-weighting-screen'), + ); +}; + +init(); diff --git a/assets/js/weighting/utilities.js b/assets/js/weighting/utilities.js deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/includes/classes/Feature/Documents/Documents.php b/includes/classes/Feature/Documents/Documents.php index 78569c3345..d4e3a75d77 100644 --- a/includes/classes/Feature/Documents/Documents.php +++ b/includes/classes/Feature/Documents/Documents.php @@ -510,8 +510,9 @@ public function filter_weightable_fields_for_post_type( $fields, $post_type ) { public function filter_attachment_post_type_weights( $weights, $post_type ) { if ( 'attachment' === $post_type ) { $weights['attachments.attachment.content'] = [ - 'enabled' => true, - 'weight' => 0, + 'indexable' => true, + 'searchable' => true, + 'weight' => 0, ]; } diff --git a/includes/classes/Feature/Search/Weighting.php b/includes/classes/Feature/Search/Weighting.php index b9d86d09c0..c9cd5b42ea 100644 --- a/includes/classes/Feature/Search/Weighting.php +++ b/includes/classes/Feature/Search/Weighting.php @@ -145,7 +145,13 @@ public function get_weightable_fields() { $post_types = Features::factory()->get_registered_feature( 'search' )->get_searchable_post_types(); foreach ( $post_types as $post_type ) { - $weightable[ $post_type ] = $this->get_weightable_fields_for_post_type( $post_type ); + $post_type_object = get_post_type_object( $post_type ); + $post_type_labels = get_post_type_labels( $post_type_object ); + + $weightable[ $post_type ] = [ + 'label' => $post_type_labels->menu_name, + 'groups' => $this->get_weightable_fields_for_post_type( $post_type ), + ]; } return $weightable; @@ -163,20 +169,24 @@ public function get_weightable_fields() { public function get_post_type_default_settings( $post_type ) { $post_type_defaults = [ 'post_title' => [ - 'enabled' => true, - 'weight' => 1, + 'indexable' => true, + 'searchable' => true, + 'weight' => 1, ], 'post_content' => [ - 'enabled' => true, - 'weight' => 1, + 'indexable' => true, + 'searchable' => true, + 'weight' => 1, ], 'post_excerpt' => [ - 'enabled' => true, - 'weight' => 1, + 'indexable' => true, + 'searchable' => true, + 'weight' => 1, ], 'author_name' => [ - 'enabled' => true, - 'weight' => 1, + 'indexable' => true, + 'searchable' => true, + 'weight' => 1, ], ]; @@ -199,32 +209,13 @@ public function get_post_type_default_settings( $post_type ) { foreach ( $enabled_by_default as $default_tax ) { if ( in_array( $default_tax, $post_type_taxonomies, true ) ) { $post_type_defaults[ 'terms.' . $default_tax . '.name' ] = [ - 'enabled' => true, - 'weight' => 1, + 'indexable' => true, + 'searchable' => true, + 'weight' => 1, ]; } } - /** - * TODO: Meta keys per post type? - */ - $indexable = Indexables::factory()->get( 'post' ); - - try { - $meta_keys = $indexable->get_distinct_meta_field_keys(); - } catch ( \Throwable $th ) { - $meta_keys = []; - } - - foreach ( $meta_keys as $meta_key ) { - $key = "meta.$meta_key.value"; - - $post_type_defaults[ $key ] = [ - 'enabled' => false, - 'weight' => 1, - ]; - } - /** * Filter weighting defaults for post type * @@ -253,22 +244,21 @@ public function get_weighting_configuration() { } /** - * Returns the current weighting configuration with defaults for any - * missing properties. + * Returns the default weighting configuration. * - * @return array Current weighting configuration with defaults. + * @return array Default weighting configuration. * @since 4.4.0 */ - public function get_weighting_configuration_with_defaults() { + public function get_default_weighting_configuration() { $search = Features::factory()->get_registered_feature( 'search' ); $post_types = $search->get_searchable_post_types(); - $weighting = $this->get_weighting_configuration(); + $weighting = []; foreach ( $post_types as $post_type ) { - $config = isset( $weighting[ $post_type ] ) ? $weighting[ $post_type ] : array(); - $default = $this->get_post_type_default_settings( $post_type ); - - $weighting[ $post_type ] = wp_parse_args( $config, $default ); + $weighting[ $post_type ] = [ + 'indexable' => true, + 'fields' => $this->get_post_type_default_settings( $post_type ), + ]; } return $weighting; diff --git a/includes/classes/Feature/SearchOrdering/SearchOrdering.php b/includes/classes/Feature/SearchOrdering/SearchOrdering.php index 78ed5ce75b..46078db1bf 100644 --- a/includes/classes/Feature/SearchOrdering/SearchOrdering.php +++ b/includes/classes/Feature/SearchOrdering/SearchOrdering.php @@ -601,9 +601,10 @@ public function filter_weighting_configuration( $weighting_configuration, $args */ public function filter_default_post_type_weights( $post_type_defaults, $post_type ) { $post_type_defaults['terms.ep_custom_result.name'] = [ - 'enabled' => true, - 'weight' => 9999, - 'fuzziness' => false, + 'fuzziness' => false, + 'indexable' => true, + 'searchable' => true, + 'weight' => 9999, ]; return $post_type_defaults; diff --git a/includes/classes/Feature/WooCommerce/WooCommerce.php b/includes/classes/Feature/WooCommerce/WooCommerce.php index 02de2d950d..09e44ee584 100644 --- a/includes/classes/Feature/WooCommerce/WooCommerce.php +++ b/includes/classes/Feature/WooCommerce/WooCommerce.php @@ -769,13 +769,15 @@ public function add_product_default_post_type_weights( $defaults, $post_type ) { } $defaults['meta._sku.value'] = array( - 'enabled' => true, - 'weight' => 1, + 'indexable' => true, + 'searchable' => true, + 'weight' => 1, ); $defaults['meta._variations_skus.value'] = array( - 'enabled' => true, - 'weight' => 1, + 'indexable' => true, + 'searchable' => true, + 'weight' => 1, ); } return $defaults; @@ -820,7 +822,7 @@ public function setup() { add_action( 'parse_query', [ $this, 'search_order' ], 11 ); add_filter( 'ep_term_suggest_post_type', [ $this, 'suggest_wc_add_post_type' ] ); add_filter( 'ep_facet_include_taxonomies', [ $this, 'add_product_attributes' ] ); - add_filter( 'ep_weighting_fields_for_post_type', [ $this, 'add_product_attributes_to_weighting' ], 10, 2 ); + // add_filter( 'ep_weighting_fields_for_post_type', [ $this, 'add_product_attributes_to_weighting' ], 10, 2 ); add_filter( 'ep_weighting_default_post_type_weights', [ $this, 'add_product_default_post_type_weights' ], 10, 2 ); add_filter( 'ep_prepare_meta_data', [ $this, 'add_variations_skus_meta' ], 10, 2 ); add_filter( 'request', [ $this, 'admin_product_list_request_query' ], 9 ); diff --git a/includes/dashboard.php b/includes/dashboard.php index 18f21c1bdb..e8c0feb5c1 100644 --- a/includes/dashboard.php +++ b/includes/dashboard.php @@ -509,12 +509,16 @@ function action_admin_enqueue_dashboard_scripts() { $weighting = Features::factory()->get_registered_feature( 'search' )->weighting; + $weightable_fields = $weighting->get_weightable_fields(); + $weighting_configuration = $weighting->get_weighting_configuration(); + $weighting_default = $weighting->get_default_weighting_configuration(); + wp_localize_script( 'ep_weighting_script', 'epWeighting', array( - 'weightableFields' => $weighting->get_weightable_fields(), - 'weightingConfiguration' => $weighting->get_weighting_configuration_with_defaults(), + 'weightableFields' => $weightable_fields, + 'weightingConfiguration' => $weighting_configuration ? $weighting_configuration : $weighting_default, ) ); } From d1c4cd5362bb4f34099d691f4adfdcaac087f83f Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Thu, 20 Oct 2022 01:05:24 +1100 Subject: [PATCH 06/63] WIP: Implement manual management of meta. --- assets/css/weighting.css | 23 +- .../components/common/weight-control.js | 19 -- assets/js/weighting/components/post-type.js | 135 ------------ .../weighting/components/post-type/fields.js | 155 -------------- assets/js/weighting/components/weighting.js | 111 ++++++++++ .../components/{ => weighting}/actions.js | 0 .../components/weighting/post-type.js | 128 ++++++++++++ .../{ => weighting}/post-type/field.js | 44 ++-- .../components/weighting/post-type/fields.js | 197 ++++++++++++++++++ .../components/weighting/post-type/group.js | 70 +++++++ assets/js/weighting/index.js | 110 +--------- .../classes/Feature/Documents/Documents.php | 5 +- includes/classes/Feature/Search/Weighting.php | 67 +++--- .../Feature/SearchOrdering/SearchOrdering.php | 7 +- .../Feature/WooCommerce/WooCommerce.php | 20 +- includes/classes/Upgrades.php | 29 +++ includes/dashboard.php | 5 +- 17 files changed, 630 insertions(+), 495 deletions(-) delete mode 100644 assets/js/weighting/components/common/weight-control.js delete mode 100644 assets/js/weighting/components/post-type.js delete mode 100644 assets/js/weighting/components/post-type/fields.js create mode 100644 assets/js/weighting/components/weighting.js rename assets/js/weighting/components/{ => weighting}/actions.js (100%) create mode 100644 assets/js/weighting/components/weighting/post-type.js rename assets/js/weighting/components/{ => weighting}/post-type/field.js (66%) create mode 100644 assets/js/weighting/components/weighting/post-type/fields.js create mode 100644 assets/js/weighting/components/weighting/post-type/group.js diff --git a/assets/css/weighting.css b/assets/css/weighting.css index d93ad04a2e..26e983ef70 100644 --- a/assets/css/weighting.css +++ b/assets/css/weighting.css @@ -1,16 +1,20 @@ #ep-weighting-screen { + max-width: 800px; & .components-panel { margin-bottom: 1rem; - max-width: 800px; + } + + & .components-toggle-control__label { + max-width: none; } } .ep-weighting-field { align-items: center; display: grid; - grid-gap: 1em; - grid-template-columns: min(20%, 30ch) max-content max-content auto max-content; + grid-gap: 0.5em; + grid-template-columns: min(20%, 30ch) max-content auto max-content; width: 100%; & .components-base-control__field { @@ -30,6 +34,7 @@ } @media ( max-width: 600px ) { + grid-gap: 1em; & .components-range-control__wrapper { display: none; @@ -42,16 +47,17 @@ } .ep-weighting-field__name { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; & h2 { - color: inherit; - font-size: 14px; margin: 0; } } .ep-weighting-field__undo { - grid-column-start: 5; + grid-column-start: 4; justify-self: end; } @@ -66,8 +72,11 @@ .ep-weighting-add-meta { justify-content: start; +} + +.ep-weighting-add-meta__select { - & .components-select-control__input.components-select-control__input.components-select-control__input { + & select.components-select-control__input { min-height: 36px; } } diff --git a/assets/js/weighting/components/common/weight-control.js b/assets/js/weighting/components/common/weight-control.js deleted file mode 100644 index 1d63881aaf..0000000000 --- a/assets/js/weighting/components/common/weight-control.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * WordPress dependencies. - */ -import { RangeControl } from '@wordpress/components'; -import { WPElement } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; - -/** - * Weight control component. - * - * @param {object} props Component props. - * @returns {WPElement} Component element. - */ -export default (props) => { - /** - * Render. - */ - return ; -}; diff --git a/assets/js/weighting/components/post-type.js b/assets/js/weighting/components/post-type.js deleted file mode 100644 index 87ef4a3844..0000000000 --- a/assets/js/weighting/components/post-type.js +++ /dev/null @@ -1,135 +0,0 @@ -/** - * WordPress dependencies. - */ -import { CheckboxControl, Panel, PanelHeader } from '@wordpress/components'; -import { useMemo, WPElement } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; -import { isEqual } from 'lodash'; - -/** - * Internal dependencies. - */ -import Fields from './post-type/fields'; -import UndoButton from './common/undo-button'; - -/** - * Post type weighting settings component. - * - * @param {object} props Components props. - * @param {object[]} props.groups Field groups. - * @param {string} props.label Post type label. - * @param {Function} props.onChange Data change handler. - * @param {object} props.originalValues Saved post type settings. - * @param {object} props.values Current post type settings. - * @returns {WPElement} Component element. - */ -export default ({ groups, label, onChange, originalValues, values }) => { - const { fields, indexable = true, ...rest } = values; - const { - fields: originalFields, - indexable: originalIndexable = true, - ...originalRest - } = originalValues; - - /** - * The fields' values. - * - * `fields` and `indexable` are available on >4.4.0, while earlier versions - * will contain the fields data in `rest`. - */ - const fieldsValues = useMemo(() => fields || rest, [fields, rest]); - - /** - * The original fields' values. - * - * `fields` and `indexable` are available on >4.4.0, while earlier versions - * will contain the fields data in `rest`. - */ - const originalFieldsValues = useMemo( - () => originalFields || originalRest, - [originalFields, originalRest], - ); - - /** - * Have any values changed? - */ - const isChanged = useMemo( - () => !(indexable === originalIndexable && isEqual(fieldsValues, originalFieldsValues)), - [fieldsValues, indexable, originalIndexable, originalFieldsValues], - ); - - /** - * Handle change of indexable. - * - * @param {Array} indexable New indexable value. - * @returns {void}} - */ - const onChangeIndexable = (indexable) => { - onChange({ fields: fieldsValues, indexable }); - }; - - /** - * Handle a change to the post type's fields. - * - * @param {Array} fields New field values. - * @returns {void} - */ - const onChangeGroup = (fields) => { - onChange({ fields, indexable }); - }; - - /** - * Handle resetting all data for the post type. - * - * @returns {void} - */ - const onReset = () => { - onChange(originalValues); - }; - - /** - * Render. - */ - return ( - - -
-
-

{label}

-
-
- -
-
- {isChanged ? ( - - ) : null} -
-
-
- {indexable - ? Object.entries(groups) - .filter(([, g]) => g.children.length !== 0) - .map(([key, { label, children }]) => ( - - )) - : null} -
- ); -}; diff --git a/assets/js/weighting/components/post-type/fields.js b/assets/js/weighting/components/post-type/fields.js deleted file mode 100644 index 2d8d835336..0000000000 --- a/assets/js/weighting/components/post-type/fields.js +++ /dev/null @@ -1,155 +0,0 @@ -/** - * WordPress dependencies. - */ -import { Button, PanelBody, PanelRow, SelectControl } from '@wordpress/components'; -import { useMemo, useState, WPElement } from '@wordpress/element'; - -/** - * Internal dependencies. - */ -import Field from './field'; - -/** - * Post type propertes component. - * - * @param {object} props Component props. - * @param {object[]} props.fields Post type fields. - * @param {boolean} props.isEditable Whether to display as an editable list. - * @param {string} props.label Properties label. - * @param {Function} props.onChange Change handler. - * @param {object[]} props.originalValues Saved property values. - * @param {object[]} props.values Current property values. - * @returns {WPElement} Component element. - */ -export default ({ fields, isEditable, label, onChange, originalValues, values }) => { - const [toAdd, setToAdd] = useState(''); - - /** - * Handle changes to a property. - * - * @param {object} value New property data. - * @param {number} key Property key. - * @returns {void} - */ - const onChangeProperty = (value, key) => { - onChange({ ...values, [key]: value }); - }; - - /** - * Handle removing a property. - * - * @param {number} key Property key. - * @returns {void} - */ - const onDeleteProperty = (key) => { - const newValues = { ...values }; - - delete newValues[key]; - - onChange(newValues); - }; - - /** - * Handle selecting a new property to enable. - * - * @param {string} key Key of property to enable. - * @returns {void} - */ - const onChangeToAdd = (key) => { - setToAdd(key); - }; - - /** - * Handle clicking to add a new property. - * - * @returns {void} - */ - const onClickAdd = () => { - const value = { indexable: true, searchable: false, weight: 1 }; - const newValues = { ...values, [toAdd]: value }; - - onChange(newValues); - setToAdd(''); - }; - - /** - * Weightable fields that can be added to the group, if it is editable. - */ - const availableFields = useMemo(() => { - return isEditable - ? Object.values(fields).map((p) => ({ - label: p.label, - value: p.key, - disabled: values?.[p.key]?.enable || values?.[p.key]?.indexable, - })) - : null; - }, [isEditable, fields, values]); - - /** - * Fields that can be weighted. - * - * For editable groups fields are sorted to match the order of the saved - * configuration, to preserve the order in which the fields were added. - */ - const weightableFields = useMemo(() => { - const weightableFields = Object.values(fields); - - return isEditable - ? weightableFields.sort((a, b) => { - const { key: aKey } = a; - const { key: bKey } = b; - - const keys = Object.keys(values); - - return keys.indexOf(aKey) - keys.indexOf(bKey); - }) - : weightableFields; - }, [isEditable, fields, values]); - - /** - * Render. - */ - return ( - - {weightableFields - .filter((p) => values[p.key]) - .map(({ key, label }) => ( - - { - onChangeProperty(value, key); - }} - onDelete={ - isEditable - ? () => { - onDeleteProperty(key); - } - : null - } - /> - - ))} - {availableFields ? ( - - -   - - - ) : null} - - ); -}; diff --git a/assets/js/weighting/components/weighting.js b/assets/js/weighting/components/weighting.js new file mode 100644 index 0000000000..fae32fc43f --- /dev/null +++ b/assets/js/weighting/components/weighting.js @@ -0,0 +1,111 @@ +/** + * WordPress dependencies. + */ +import { WPElement, useMemo, useState } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { cloneDeep, isEqual } from 'lodash'; + +/** + * Internal Dependencies. + */ +import Actions from './weighting/actions'; +import PostType from './weighting/post-type'; + +/** + * Weighting settings app. + * + * @param {object} props Component props. + * @param {object} props.weightableFields Weightable fields, indexed by post type. + * @param {object} props.weightingConfiguration Weighting configuration, indexed by post type. + * @returns {WPElement} Element. + */ +export default ({ weightableFields, weightingConfiguration }) => { + const [currentData, setCurrentData] = useState({ ...weightingConfiguration }); + const [savedData, setSavedData] = useState({ ...weightingConfiguration }); + const [isBusy, setIsBusy] = useState(false); + + /** + * Is the current data different to the saved data. + */ + const isChanged = useMemo(() => !isEqual(currentData, savedData), [currentData, savedData]); + + /** + * Handle data change. + * + * @param {string} postType Updated post type. + * @param {Array} values Updated data. + * @returns {void} + */ + const onChangePostType = (postType, values) => { + setCurrentData({ ...currentData, [postType]: values }); + }; + + /** + * Handle resetting all settings. + * + * @returns {void} + */ + const onReset = () => { + setCurrentData({ ...savedData }); + }; + + /** + * Handle for submission. + * + * @param {Event} event Submit event. + * @returns {void} + */ + const onSubmit = (event) => { + event.preventDefault(); + + const savedData = cloneDeep(currentData); + + setIsBusy(true); + + setTimeout(() => { + setSavedData(savedData); + setIsBusy(false); + }, 1000); + }; + + /** + * Render. + */ + return ( +
+

{__('Manage Search Fields & Weighting', 'elasticpress')}

+
+

+ {__( + 'Adding more weight to an item will mean it will have more presence during searches. Add more weight to the items that are more important and need more prominence during searches. For example, adding more weight to the title attribute will cause search matches on the post title to apear mor prominently.', + 'elasticpress', + )} +

+

+ {__( + 'Important: If you enable or disable indexing for a field, you will need to refresh your index after saving your settings', + 'elasticpress', + )} +

+
+ {Object.entries(weightableFields).map(([postType, { groups, label }]) => { + const originalValues = savedData[postType] || {}; + const values = currentData[postType] || {}; + + return ( + { + onChangePostType(postType, values); + }} + originalValues={originalValues} + values={values} + /> + ); + })} + + + ); +}; diff --git a/assets/js/weighting/components/actions.js b/assets/js/weighting/components/weighting/actions.js similarity index 100% rename from assets/js/weighting/components/actions.js rename to assets/js/weighting/components/weighting/actions.js diff --git a/assets/js/weighting/components/weighting/post-type.js b/assets/js/weighting/components/weighting/post-type.js new file mode 100644 index 0000000000..effee68336 --- /dev/null +++ b/assets/js/weighting/components/weighting/post-type.js @@ -0,0 +1,128 @@ +/** + * WordPress dependencies. + */ +import { Panel, PanelHeader } from '@wordpress/components'; +import { useMemo, WPElement } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { isEqual } from 'lodash'; + +/** + * Internal dependencies. + */ +import UndoButton from '../common/undo-button'; +import Group from './post-type/group'; + +/** + * Post type weighting settings component. + * + * @param {object} props Components props. + * @param {object[]} props.groups Field groups. + * @param {string} props.label Post type label. + * @param {Function} props.onChange Data change handler. + * @param {object} props.originalValues Saved post type settings. + * @param {object} props.values Current post type settings. + * @returns {WPElement} Component element. + */ +export default ({ groups, label, onChange, originalValues, values }) => { + /** + * Have any values changed? + */ + const isChanged = useMemo(() => !isEqual(originalValues, values), [originalValues, values]); + + /** + * The field groups to display. + * + * Filters out any groups without fields. + */ + const fieldGroups = useMemo( + () => + Object.entries(groups).reduce((previousValue, currentValue) => { + const [key, group] = currentValue; + + if (Object.keys(group.children).length > 0) { + return [...previousValue, { key, ...group }]; + } + + return previousValue; + }, []), + [groups], + ); + + /** + * Handle a change to the post type's fields. + * + * @param {Array} fields New field values. + * @returns {void} + */ + const onChangeFields = (fields) => { + onChange({ ...values, fields }); + }; + + /** + * Handle change in meta management. + * + * When disabling manual meta management remove weighting settings for + * metadata. + * + * @param {Array} manageMeta New manage meta value. + * @returns {void}} + */ + const onChangeManageMeta = (manageMeta) => { + if (manageMeta === false) { + const keys = Object.keys(groups.meta?.children || {}); + + for (const k of keys) { + delete values.fields[k]; + } + } + + onChange({ ...values, manage_meta: manageMeta }); + }; + + /** + * Handle resetting all data for the post type. + * + * @returns {void} + */ + const onReset = () => { + onChange(originalValues); + }; + + /** + * Render. + */ + return ( + + +
+
+

{label}

+
+
+ {isChanged ? ( + + ) : null} +
+
+
+ {fieldGroups.map(({ children, key, label }) => { + return ( + + ); + })} +
+ ); +}; diff --git a/assets/js/weighting/components/post-type/field.js b/assets/js/weighting/components/weighting/post-type/field.js similarity index 66% rename from assets/js/weighting/components/post-type/field.js rename to assets/js/weighting/components/weighting/post-type/field.js index b59af5c688..24bf53de18 100644 --- a/assets/js/weighting/components/post-type/field.js +++ b/assets/js/weighting/components/weighting/post-type/field.js @@ -1,7 +1,7 @@ /** * Wordpress Dependencies. */ -import { CheckboxControl } from '@wordpress/components'; +import { CheckboxControl, RangeControl } from '@wordpress/components'; import { useMemo, WPElement } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { isEqual } from 'lodash'; @@ -9,9 +9,8 @@ import { isEqual } from 'lodash'; /** * Internal dependencies. */ -import DeleteButton from '../common/delete-button'; -import UndoButton from '../common/undo-button'; -import WeightControl from '../common/weight-control'; +import DeleteButton from '../../common/delete-button'; +import UndoButton from '../../common/undo-button'; /** * Field settings component. @@ -25,7 +24,8 @@ import WeightControl from '../common/weight-control'; * @returns {WPElement} Component element. */ export default ({ label, onChange, onDelete, originalValue, value }) => { - const { enabled, indexable, searchable, weight } = value; + const { enabled = false, weight = 0 } = value; + /** * Is the current value different to the original. */ @@ -37,21 +37,11 @@ export default ({ label, onChange, onDelete, originalValue, value }) => { /** * Handle change of indexable. * - * @param {boolean} indexable New indexable value. + * @param {boolean} enabled New searchable value. * @returns {void} */ - const onChangeIndexable = (indexable) => { - onChange({ weight, searchable: false, indexable }); - }; - - /** - * Handle change of indexable. - * - * @param {boolean} searchable New searchable value. - * @returns {void} - */ - const onChangeSearchable = (searchable) => { - onChange({ weight, indexable: true, searchable }); + const onChangeSearchable = (enabled) => { + onChange({ weight, enabled }); }; /** @@ -61,7 +51,7 @@ export default ({ label, onChange, onDelete, originalValue, value }) => { * @returns {void} */ const onChangeWeight = (weight) => { - onChange({ indexable: true, searchable: true, weight }); + onChange({ enabled: true, weight }); }; /** @@ -84,23 +74,19 @@ export default ({ label, onChange, onDelete, originalValue, value }) => {
{label} -
- -
- diff --git a/assets/js/weighting/components/weighting/post-type/fields.js b/assets/js/weighting/components/weighting/post-type/fields.js new file mode 100644 index 0000000000..d20a85629c --- /dev/null +++ b/assets/js/weighting/components/weighting/post-type/fields.js @@ -0,0 +1,197 @@ +/** + * WordPress dependencies. + */ +import { Button, PanelRow, SelectControl, ToggleControl } from '@wordpress/components'; +import { useMemo, useState, WPElement } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies. + */ +import Field from './field'; + +/** + * Post type propertes component. + * + * @param {object} props Component props. + * @param {object[]} props.fields Post type fields. + * @param {boolean} props.isEditable Whether to display as an editable list. + * @param {Function} props.onChange Change handler. + * @param {object[]} props.originalValues Saved property values. + * @param {object[]} props.values Current property values. + * @returns {WPElement} Component element. + */ +export default ({ fields, isEditable, onChange, originalValues, values }) => { + const [toAdd, setToAdd] = useState(''); + const [showFeatureFields, setShowFeatureFields] = useState(false); + + /** + * Weightable fields that can be added to the group, if it is editable. + * + * Fields that are automatically indexed by features are excluded, while + * fields that have already been added are disabled. + */ + const availableFields = useMemo(() => { + return fields + .filter((f) => !f.used_by_feature) + .map((f) => ({ + label: f.label, + value: f.key, + disabled: Object.prototype.hasOwnProperty.call(values, f.key), + })); + }, [fields, values]); + + /** + * Fields that are automatically indexed by features. + */ + const featureFields = useMemo(() => { + return fields.filter((f) => f.used_by_feature); + }, [fields]); + + /** + * Fields that can be weighted. + * + * For editable groups fields are sorted to match the order of the saved + * configuration, to preserve the order in which the fields were added. + */ + const weightedFields = useMemo(() => { + return fields + .filter((f) => !f.used_by_feature) + .filter((f) => Object.prototype.hasOwnProperty.call(values, f.key)) + .sort((a, b) => { + const { key: aKey } = a; + const { key: bKey } = b; + + const keys = Object.keys(values); + + return keys.indexOf(aKey) - keys.indexOf(bKey); + }); + }, [fields, values]); + + /** + * Handle changes to a property. + * + * @param {object} value New property data. + * @param {number} key Property key. + * @returns {void} + */ + const onChangeProperty = (value, key) => { + onChange({ ...values, [key]: value }); + }; + + /** + * Handle selecting a new property to enable. + * + * @param {string} key Key of property to enable. + * @returns {void} + */ + const onChangeToAdd = (key) => { + setToAdd(key); + }; + + /** + * Handle a change to whether fields automatically indexed by features are + * shown. + * + * @param {boolean} showFeatureFields Whether to show fields indexed by features. + * @returns {void} + */ + const onChangeShowFeatureFields = (showFeatureFields) => { + setShowFeatureFields(showFeatureFields); + }; + + /** + * Handle clicking to add a new property. + * + * @returns {void} + */ + const onClickAdd = () => { + const newValues = { ...values, [toAdd]: { enabled: false, weight: 0 } }; + + onChange(newValues); + setToAdd(''); + }; + + /** + * Handle removing a field. + * + * @param {number} key field key. + * @returns {void} + */ + const onDeleteField = (key) => { + const newValues = { ...values }; + + delete newValues[key]; + + onChange(newValues); + }; + + /** + * Render. + */ + return ( + <> + {featureFields.length > 0 ? ( + + + + ) : null} + {featureFields + .filter((f) => showFeatureFields || values?.[f.key]?.enabled === true) + .map(({ key, label }) => ( + + { + onChangeProperty(value, key); + }} + /> + + ))} + {(isEditable ? weightedFields : fields).map(({ key, label }) => ( + + { + onChangeProperty(value, key); + }} + onDelete={ + isEditable + ? () => { + onDeleteField(key); + } + : null + } + /> + + ))} + {isEditable && availableFields ? ( + + +   + + + ) : null} + + ); +}; diff --git a/assets/js/weighting/components/weighting/post-type/group.js b/assets/js/weighting/components/weighting/post-type/group.js new file mode 100644 index 0000000000..1f2aa0e7b1 --- /dev/null +++ b/assets/js/weighting/components/weighting/post-type/group.js @@ -0,0 +1,70 @@ +/** + * WordPress dependencies. + */ +import { CheckboxControl, PanelBody, PanelRow } from '@wordpress/components'; +import { useMemo, WPElement } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies. + */ +import Fields from './fields'; + +/** + * Field group component. + * + * @param {object} props Component props. + * @param {object} props.fields Group fields. + * @param {string} props.label Group label. + * @param {boolean} props.manual Whether the fields are being managed manually. + * @param {Function} props.onChange Field change handler. + * @param {Function} props.onChangeManual Manual managment change handler. + * @param {object} props.originalValues Saved weighting values. + * @param {object} props.values Current weighting values. + * @returns {WPElement} Component element. + */ +export default ({ fields, label, manual, onChange, onChangeManual, originalValues, values }) => { + /** + * Whether to show the fields. + * + * Always show the fields unless the group has the option for manual + * management, in which case only show fields if manual management is + * enabled. + */ + const showFields = useMemo(() => (onChangeManual ? manual : true), [manual, onChangeManual]); + + /** + * Weightable fields. + */ + const weightableFields = useMemo(() => Object.values(fields), [fields]); + + /** + * Render. + */ + return ( + + {onChangeManual ? ( + + + + ) : null} + {showFields ? ( + + ) : null} + + ); +}; diff --git a/assets/js/weighting/index.js b/assets/js/weighting/index.js index eb7df0cc0f..2a4fd68d63 100644 --- a/assets/js/weighting/index.js +++ b/assets/js/weighting/index.js @@ -1,113 +1,12 @@ /** * WordPress dependencies. */ -import { render, WPElement, useMemo, useState } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; -import { cloneDeep, isEqual } from 'lodash'; +import { render } from '@wordpress/element'; /** * Internal Dependencies. */ -import Actions from './components/actions'; -import PostType from './components/post-type'; - -/** - * Window dependencies. - */ - -/** - * Weighting settings app. - * - * @param {object} props Component props. - * @param {object} props.weightableFields Weightable fields, indexed by post type. - * @param {object} props.weightingConfiguration Weighting configuration, indexed by post type. - * @returns {WPElement} Element. - */ -const App = ({ weightableFields, weightingConfiguration }) => { - const [currentData, setCurrentData] = useState({ ...weightingConfiguration }); - const [savedData, setSavedData] = useState({ ...weightingConfiguration }); - const [isBusy, setIsBusy] = useState(false); - - /** - * Is the current data different to the saved data. - */ - const isChanged = useMemo(() => !isEqual(currentData, savedData), [currentData, savedData]); - - /** - * Handle data change. - * - * @param {string} postType Updated post type. - * @param {Array} values Updated data. - * @returns {void} - */ - const onChangePostType = (postType, values) => { - setCurrentData({ ...currentData, [postType]: values }); - }; - - /** - * Handle for submission. - * - * @param {Event} event Submit event. - * @returns {void} - */ - const onSubmit = (event) => { - event.preventDefault(); - - const savedData = cloneDeep(currentData); - - setIsBusy(true); - - setTimeout(() => { - setSavedData(savedData); - setIsBusy(false); - }, 1000); - }; - - /** - * Handle resetting all settings. - * - * @returns {void} - */ - const onReset = () => { - setCurrentData({ ...savedData }); - }; - - /** - * Render. - */ - return ( -
-

{__('Manage Search Fields & Weighting', 'elasticpress')}

-
-

- {__( - 'Adding more weight to an item will mean it will have more presence during searches. Add more weight to the items that are more important and need more prominence during searches. For example, adding more weight to the title attribute will cause search matches on the post title to apear mor prominently.', - 'elasticpress', - )} -

-

- {__( - 'Important: If you enable or disable indexing for a field, you will need to refresh your index after saving your settings', - 'elasticpress', - )} -

-
- {Object.entries(weightableFields).map(([postType, { groups, label }]) => ( - { - onChangePostType(postType, values); - }} - originalValues={savedData[postType]} - values={currentData[postType]} - /> - ))} - - - ); -}; +import Weighting from './components/weighting'; /** * Initialize. @@ -118,7 +17,10 @@ const init = () => { const { weightableFields, weightingConfiguration } = window.epWeighting; render( - , + , document.getElementById('ep-weighting-screen'), ); }; diff --git a/includes/classes/Feature/Documents/Documents.php b/includes/classes/Feature/Documents/Documents.php index d4e3a75d77..78569c3345 100644 --- a/includes/classes/Feature/Documents/Documents.php +++ b/includes/classes/Feature/Documents/Documents.php @@ -510,9 +510,8 @@ public function filter_weightable_fields_for_post_type( $fields, $post_type ) { public function filter_attachment_post_type_weights( $weights, $post_type ) { if ( 'attachment' === $post_type ) { $weights['attachments.attachment.content'] = [ - 'indexable' => true, - 'searchable' => true, - 'weight' => 0, + 'enabled' => true, + 'weight' => 0, ]; } diff --git a/includes/classes/Feature/Search/Weighting.php b/includes/classes/Feature/Search/Weighting.php index c9cd5b42ea..e564553413 100644 --- a/includes/classes/Feature/Search/Weighting.php +++ b/includes/classes/Feature/Search/Weighting.php @@ -100,9 +100,6 @@ public function get_weightable_fields_for_post_type( $post_type ) { } } - /** - * TODO: Meta keys per post type? - */ $fields['meta'] = [ 'label' => 'Metadata', 'children' => [], @@ -114,12 +111,22 @@ public function get_weightable_fields_for_post_type( $post_type ) { $meta_keys = []; } + $allowed_protected_keys = apply_filters( 'ep_prepare_meta_allowed_protected_keys', [], $post ); + $excluded_public_keys = apply_filters( 'ep_prepare_meta_excluded_public_keys', [], $post ); + foreach ( $meta_keys as $meta_key ) { $key = "meta.$meta_key.value"; - $fields['meta']['children'][$key] = [ - 'key' => $key, - 'label' => $meta_key, + if ( in_array( $key, $excluded_public_keys, true ) ) { + continue; + } + + $used_by_feature = in_array( $meta_key, $allowed_protected_keys, true ); + + $fields['meta']['children'][ $key ] = [ + 'key' => $key, + 'label' => $meta_key, + 'used_by_feature' => $used_by_feature, ]; } @@ -169,24 +176,20 @@ public function get_weightable_fields() { public function get_post_type_default_settings( $post_type ) { $post_type_defaults = [ 'post_title' => [ - 'indexable' => true, - 'searchable' => true, - 'weight' => 1, + 'enabled' => true, + 'weight' => 1, ], 'post_content' => [ - 'indexable' => true, - 'searchable' => true, - 'weight' => 1, + 'enabled' => true, + 'weight' => 1, ], 'post_excerpt' => [ - 'indexable' => true, - 'searchable' => true, - 'weight' => 1, + 'enabled' => true, + 'weight' => 1, ], 'author_name' => [ - 'indexable' => true, - 'searchable' => true, - 'weight' => 1, + 'enabled' => true, + 'weight' => 1, ], ]; @@ -209,9 +212,8 @@ public function get_post_type_default_settings( $post_type ) { foreach ( $enabled_by_default as $default_tax ) { if ( in_array( $default_tax, $post_type_taxonomies, true ) ) { $post_type_defaults[ 'terms.' . $default_tax . '.name' ] = [ - 'indexable' => true, - 'searchable' => true, - 'weight' => 1, + 'enabled' => true, + 'weight' => 1, ]; } } @@ -244,21 +246,28 @@ public function get_weighting_configuration() { } /** - * Returns the default weighting configuration. + * Returns the current weighting configuration with defaults for any + * missing fields. * - * @return array Default weighting configuration. + * @return array Current weighting configuration with defaults. * @since 4.4.0 */ - public function get_default_weighting_configuration() { + public function get_weighting_configuration_with_defaults() { $search = Features::factory()->get_registered_feature( 'search' ); $post_types = $search->get_searchable_post_types(); - $weighting = []; + $weighting = $this->get_weighting_configuration(); foreach ( $post_types as $post_type ) { - $weighting[ $post_type ] = [ - 'indexable' => true, - 'fields' => $this->get_post_type_default_settings( $post_type ), - ]; + $defaults = $this->get_post_type_default_settings( $post_type ); + + if ( isset( $weighting[ $post_type ] ) ) { + $weighting[ $post_type ]['fields'] = wp_parse_args( $weighting[ $post_type ]['fields'], $defaults ); + } else { + $weighting[ $post_type ] = [ + 'fields' => $defaults, + 'manage_meta' => false, + ]; + } } return $weighting; diff --git a/includes/classes/Feature/SearchOrdering/SearchOrdering.php b/includes/classes/Feature/SearchOrdering/SearchOrdering.php index 46078db1bf..9293c21436 100644 --- a/includes/classes/Feature/SearchOrdering/SearchOrdering.php +++ b/includes/classes/Feature/SearchOrdering/SearchOrdering.php @@ -601,10 +601,9 @@ public function filter_weighting_configuration( $weighting_configuration, $args */ public function filter_default_post_type_weights( $post_type_defaults, $post_type ) { $post_type_defaults['terms.ep_custom_result.name'] = [ - 'fuzziness' => false, - 'indexable' => true, - 'searchable' => true, - 'weight' => 9999, + 'enabled' => true, + 'fuzziness' => false, + 'weight' => 9999, ]; return $post_type_defaults; diff --git a/includes/classes/Feature/WooCommerce/WooCommerce.php b/includes/classes/Feature/WooCommerce/WooCommerce.php index 09e44ee584..24476ba05f 100644 --- a/includes/classes/Feature/WooCommerce/WooCommerce.php +++ b/includes/classes/Feature/WooCommerce/WooCommerce.php @@ -736,6 +736,14 @@ public function add_product_attributes_to_weighting( $fields, $post_type ) { unset( $fields['attributes']['children']['author_name'] ); } + if ( ! empty( $fields['meta']['children']['meta._sku.value'] ) ) { + unset( $fields['meta']['children']['meta._sku.value'] ); + } + + if ( ! empty( $fields['meta']['children']['meta._variations_skus.value'] ) ) { + unset( $fields['meta']['children']['meta._variations_skus.value'] ); + } + $sku_key = 'meta._sku.value'; $fields['attributes']['children'][ $sku_key ] = array( @@ -769,15 +777,13 @@ public function add_product_default_post_type_weights( $defaults, $post_type ) { } $defaults['meta._sku.value'] = array( - 'indexable' => true, - 'searchable' => true, - 'weight' => 1, + 'enabled' => true, + 'weight' => 1, ); $defaults['meta._variations_skus.value'] = array( - 'indexable' => true, - 'searchable' => true, - 'weight' => 1, + 'enabled' => true, + 'weight' => 1, ); } return $defaults; @@ -822,7 +828,7 @@ public function setup() { add_action( 'parse_query', [ $this, 'search_order' ], 11 ); add_filter( 'ep_term_suggest_post_type', [ $this, 'suggest_wc_add_post_type' ] ); add_filter( 'ep_facet_include_taxonomies', [ $this, 'add_product_attributes' ] ); - // add_filter( 'ep_weighting_fields_for_post_type', [ $this, 'add_product_attributes_to_weighting' ], 10, 2 ); + add_filter( 'ep_weighting_fields_for_post_type', [ $this, 'add_product_attributes_to_weighting' ], 10, 2 ); add_filter( 'ep_weighting_default_post_type_weights', [ $this, 'add_product_default_post_type_weights' ], 10, 2 ); add_filter( 'ep_prepare_meta_data', [ $this, 'add_variations_skus_meta' ], 10, 2 ); add_filter( 'request', [ $this, 'admin_product_list_request_query' ], 9 ); diff --git a/includes/classes/Upgrades.php b/includes/classes/Upgrades.php index 51cbaebc15..37c13180ca 100644 --- a/includes/classes/Upgrades.php +++ b/includes/classes/Upgrades.php @@ -48,6 +48,7 @@ public function setup() { '3.5.3' => [ 'upgrade_3_5_3', 'init' ], '3.6.6' => [ 'upgrade_3_6_6', 'init' ], '4.2.2' => [ 'upgrade_4_2_2', 'init' ], + '4.4.0' => [ 'upgrade_4_4_0', 'init' ], ]; array_walk( $routines, [ $this, 'run_upgrade_routine' ] ); @@ -185,6 +186,34 @@ public function upgrade_4_2_2() { } } + /** + * Upgrade routine of v4.4.0. + * + * Migrates weighting settings into a `fields` property of the weighting + * configuration and adds the `manage_meta` setting. + * + * @since 4.4.0 + * @return void + */ + public function upgrade_4_4_0() { + $weighting_configuration = get_option( 'elasticpress_weighting', [] ); + + if ( empty( $weighting_configuration ) ) { + return; + } + + foreach ( $weighting_configuration as $post_type => $config ) { + if ( ! isset( $config['fields'] ) ) { + $weighting_configuration[ $post_type ] = [ + 'fields' => $config, + 'manage_meta' => false, + ]; + } + } + + update_option( 'elasticpress_weighting', $weighting_configuration ); + } + /** * Adjust the upgrade sync notice to warn users about Instant Results. * diff --git a/includes/dashboard.php b/includes/dashboard.php index e8c0feb5c1..279cf3fa1e 100644 --- a/includes/dashboard.php +++ b/includes/dashboard.php @@ -510,15 +510,14 @@ function action_admin_enqueue_dashboard_scripts() { $weighting = Features::factory()->get_registered_feature( 'search' )->weighting; $weightable_fields = $weighting->get_weightable_fields(); - $weighting_configuration = $weighting->get_weighting_configuration(); - $weighting_default = $weighting->get_default_weighting_configuration(); + $weighting_configuration = $weighting->get_weighting_configuration_with_defaults(); wp_localize_script( 'ep_weighting_script', 'epWeighting', array( 'weightableFields' => $weightable_fields, - 'weightingConfiguration' => $weighting_configuration ? $weighting_configuration : $weighting_default, + 'weightingConfiguration' => $weighting_configuration, ) ); } From 0e48d7b78f6c800f5d9b803d2774372898a06654 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Thu, 20 Oct 2022 01:18:17 +1100 Subject: [PATCH 07/63] Revert unnecessary changes. --- includes/classes/Feature/Search/Weighting.php | 19 ++++++------------- .../Feature/SearchOrdering/SearchOrdering.php | 2 +- package.json | 1 - 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/includes/classes/Feature/Search/Weighting.php b/includes/classes/Feature/Search/Weighting.php index e564553413..99fd91f8fe 100644 --- a/includes/classes/Feature/Search/Weighting.php +++ b/includes/classes/Feature/Search/Weighting.php @@ -105,15 +105,15 @@ public function get_weightable_fields_for_post_type( $post_type ) { 'children' => [], ]; + $allowed_protected_keys = apply_filters( 'ep_prepare_meta_allowed_protected_keys', [], $post ); + $excluded_public_keys = apply_filters( 'ep_prepare_meta_excluded_public_keys', [], $post ); + try { $meta_keys = Indexables::factory()->get( 'post' )->get_distinct_meta_field_keys(); } catch ( \Throwable $th ) { $meta_keys = []; } - $allowed_protected_keys = apply_filters( 'ep_prepare_meta_allowed_protected_keys', [], $post ); - $excluded_public_keys = apply_filters( 'ep_prepare_meta_excluded_public_keys', [], $post ); - foreach ( $meta_keys as $meta_key ) { $key = "meta.$meta_key.value"; @@ -340,17 +340,12 @@ public function render_settings_section( $post_type, $field, $current_values ) { $weight = 0; } ?> -
+
-

- id="" name="weighting[][][enabled]"> - -

-

- id="" name="weighting[][][enabled]"> - + id="" name="weighting[][][enabled]"> +

@@ -362,8 +357,6 @@ public function render_settings_section( $post_type, $field, $current_values ) { " name="weighting[][][weight]" >

- -

true, - 'fuzziness' => false, 'weight' => 9999, + 'fuzziness' => false, ]; return $post_type_defaults; diff --git a/package.json b/package.json index 600f7fa068..01f4e2e849 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,6 @@ "@wordpress/icons": "^6.1.1", "chart.js": "^2.9.4", "focus-trap-react": "^8.8.2", - "prop-types": "^15.8.1", "react-beautiful-dnd": "^11.0.5", "react-slider": "^1.3.1", "uuid": "^8.3.2" From 13672ac940d4cc0baa65bd1de6115be585338b2a Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Thu, 20 Oct 2022 02:09:09 +1100 Subject: [PATCH 08/63] Support saving settings. --- assets/js/weighting/components/weighting.js | 26 ++- assets/js/weighting/index.js | 3 +- includes/classes/Feature/Search/Weighting.php | 164 ++++-------------- includes/dashboard.php | 2 + 4 files changed, 57 insertions(+), 138 deletions(-) diff --git a/assets/js/weighting/components/weighting.js b/assets/js/weighting/components/weighting.js index fae32fc43f..032dcccb3b 100644 --- a/assets/js/weighting/components/weighting.js +++ b/assets/js/weighting/components/weighting.js @@ -1,6 +1,7 @@ /** * WordPress dependencies. */ +import apiFetch from '@wordpress/api-fetch'; import { WPElement, useMemo, useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { cloneDeep, isEqual } from 'lodash'; @@ -15,11 +16,12 @@ import PostType from './weighting/post-type'; * Weighting settings app. * * @param {object} props Component props. + * @param {string} props.apiUrl API URL. * @param {object} props.weightableFields Weightable fields, indexed by post type. * @param {object} props.weightingConfiguration Weighting configuration, indexed by post type. * @returns {WPElement} Element. */ -export default ({ weightableFields, weightingConfiguration }) => { +export default ({ apiUrl, weightableFields, weightingConfiguration }) => { const [currentData, setCurrentData] = useState({ ...weightingConfiguration }); const [savedData, setSavedData] = useState({ ...weightingConfiguration }); const [isBusy, setIsBusy] = useState(false); @@ -55,17 +57,27 @@ export default ({ weightableFields, weightingConfiguration }) => { * @param {Event} event Submit event. * @returns {void} */ - const onSubmit = (event) => { + const onSubmit = async (event) => { event.preventDefault(); - const savedData = cloneDeep(currentData); + try { + setIsBusy(true); - setIsBusy(true); + const response = await apiFetch({ + body: JSON.stringify(currentData), + method: 'POST', + url: apiUrl, + }); - setTimeout(() => { - setSavedData(savedData); + const newCurrentdData = cloneDeep(response.data); + const newSavedData = cloneDeep(response.data); + + setCurrentData(newCurrentdData); + setSavedData(newSavedData); + setIsBusy(false); + } catch { setIsBusy(false); - }, 1000); + } }; /** diff --git a/assets/js/weighting/index.js b/assets/js/weighting/index.js index 2a4fd68d63..2e8b9fec4b 100644 --- a/assets/js/weighting/index.js +++ b/assets/js/weighting/index.js @@ -14,10 +14,11 @@ import Weighting from './components/weighting'; * @returns {void} */ const init = () => { - const { weightableFields, weightingConfiguration } = window.epWeighting; + const { apiUrl, weightableFields, weightingConfiguration } = window.epWeighting; render( , diff --git a/includes/classes/Feature/Search/Weighting.php b/includes/classes/Feature/Search/Weighting.php index 99fd91f8fe..520d816a12 100644 --- a/includes/classes/Feature/Search/Weighting.php +++ b/includes/classes/Feature/Search/Weighting.php @@ -37,6 +37,7 @@ public function setup() { add_action( 'admin_menu', [ $this, 'add_weighting_submenu_page' ], 15 ); add_action( 'admin_post_ep-weighting', [ $this, 'handle_save' ] ); + add_action( 'rest_api_init', [ $this, 'register_rest_routes' ] ); add_filter( 'ep_formatted_args', [ $this, 'do_weighting' ], 20, 2 ); // After date decay, etc are injected add_filter( 'ep_query_weighting_fields', [ $this, 'adjust_weight_for_cross_fields' ], 10, 5 ); } @@ -299,99 +300,45 @@ public function render_settings_page() { } /** - * Recursively renders each settings section and its children + * Register REST routes. * - * @param string $post_type Current post type we're rendering - * @param array $field Current field to render - * @param array $current_values Current stored weighting values + * @return void + * @since 4.4.0 */ - public function render_settings_section( $post_type, $field, $current_values ) { - if ( isset( $field['children'] ) && ! empty( $field['children'] ) ) : - ?> -
-

-
- render_settings_section( $post_type, $child, $current_values ); - } - ?> -
-
- get_post_type_default_settings( $post_type ); - - $weight = isset( $post_type_settings[ $key ] ) && isset( $post_type_settings[ $key ]['weight'] ) ? (int) $post_type_settings[ $key ]['weight'] : 0; - - $range_disabled = ''; - - $enabled = ( - isset( $post_type_settings ) && - isset( $post_type_settings[ $key ] ) && - isset( $post_type_settings[ $key ]['enabled'] ) - ) - ? boolval( $post_type_settings[ $key ]['enabled'] ) : false; - - if ( ! $enabled ) { - $range_disabled = 'disabled="disabled" '; - $weight = 0; - } - ?> -
- - -

- id="" name="weighting[][][enabled]"> - -

- -

- - " name="weighting[][][weight]" > -

-
- [ $this, 'handle_save' ], + 'methods' => 'POST', + 'permission_callback' => function() { + return current_user_can( 'manage_options' ); + }, + ] + ); } /** - * Handles processing the new weighting values and saving them to the elasticpress.io service + * Handles processing the new weighting values and saving them to the + * elasticpress.io service. + * + * @param \WP_Rest_Request $request REST API request. + * @return void */ - public function handle_save() { - if ( ! isset( $_POST['ep-weighting-nonce'] ) || ! wp_verify_nonce( $_POST['ep-weighting-nonce'], 'save-weighting' ) ) { - return; - } + public function handle_save( $request ) { + $json = $request->get_body(); - if ( ! current_user_can( 'manage_options' ) ) { - return; + try { + $settings = json_decode( $json, true ); + $settings = $this->save_weighting_configuration( $settings ); + + wp_send_json_success( $settings ); + exit; + } catch ( \Exception $e ) { + wp_send_json_error( $e->getMessage() ); + exit; } - - $this->save_weighting_configuration( $_POST ); - - $redirect_url = admin_url( 'admin.php?page=elasticpress-weighting' ); - $redirect_url = add_query_arg( 'settings-updated', true, $redirect_url ); - - $this->redirect( $redirect_url ); - } - - /** - * We need this method to test handle_save properly. - * - * @param string $redirect_url Redirect URL. - */ - protected function redirect( $redirect_url ) { - // @codeCoverageIgnoreStart - wp_safe_redirect( $redirect_url ); - exit(); - // @codeCoverageIgnoreEnd } /** @@ -403,50 +350,7 @@ protected function redirect( $redirect_url ) { * @since 3.4.1 */ public function save_weighting_configuration( $settings ) { - $new_config = array(); - $previous_config_formatted = array(); - $current_config = $this->get_weighting_configuration(); - - foreach ( $current_config as $post_type => $post_type_weighting ) { - // This also ensures the string is safe, since this would return false otherwise - if ( ! post_type_exists( $post_type ) ) { - continue; - } - - // We need a way to know if fields have been explicitly set before, let's compare a previous state against $_POST['weighting'] - foreach ( $post_type_weighting as $weighting_field => $weighting_values ) { - $previous_config_formatted[ $post_type ][ sanitize_text_field( $weighting_field ) ] = [ - 'weight' => isset( $settings['weighting'][ $post_type ][ $weighting_field ]['weight'] ) ? intval( $settings['weighting'][ $post_type ][ $weighting_field ]['weight'] ) : 0, - 'enabled' => isset( $settings['weighting'][ $post_type ][ $weighting_field ]['enabled'] ) && 'on' === $settings['weighting'][ $post_type ][ $weighting_field ]['enabled'] ? true : false, - ]; - } - } - - $search = Features::factory()->get_registered_feature( 'search' ); - $post_types = $search->get_searchable_post_types(); - - foreach ( $post_types as $post_type ) { - // This also ensures the string is safe, since this would return false otherwise - if ( ! post_type_exists( $post_type ) ) { - continue; - } - - /** override default post_type settings while saving */ - $new_config[ $post_type ] = array(); - - if ( isset( $settings['weighting'][ $post_type ] ) ) { - foreach ( $settings['weighting'][ $post_type ] as $weighting_field => $weighting_values ) { - $new_config[ $post_type ][ sanitize_text_field( $weighting_field ) ] = [ - 'weight' => isset( $weighting_values['weight'] ) ? intval( $weighting_values['weight'] ) : 0, - 'enabled' => isset( $weighting_values['enabled'] ) && 'on' === $weighting_values['enabled'] ? true : false, - ]; - } - } - } - - $final_config = array_replace_recursive( $previous_config_formatted, $new_config ); - - update_option( 'elasticpress_weighting', $final_config ); + update_option( 'elasticpress_weighting', $settings ); /** * Fires right after the weighting configuration is saved. @@ -456,7 +360,7 @@ public function save_weighting_configuration( $settings ) { */ do_action( 'ep_saved_weighting_configuration' ); - return $final_config; + return $settings; } /** diff --git a/includes/dashboard.php b/includes/dashboard.php index 279cf3fa1e..b7d15449bf 100644 --- a/includes/dashboard.php +++ b/includes/dashboard.php @@ -509,6 +509,7 @@ function action_admin_enqueue_dashboard_scripts() { $weighting = Features::factory()->get_registered_feature( 'search' )->weighting; + $api_url = esc_url_raw( rest_url( 'elasticpress/v1/weighting' ) ); $weightable_fields = $weighting->get_weightable_fields(); $weighting_configuration = $weighting->get_weighting_configuration_with_defaults(); @@ -516,6 +517,7 @@ function action_admin_enqueue_dashboard_scripts() { 'ep_weighting_script', 'epWeighting', array( + 'apiUrl' => $api_url, 'weightableFields' => $weightable_fields, 'weightingConfiguration' => $weighting_configuration, ) From 74813a681fbc23fb6392c1f75631b17039209623 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Thu, 20 Oct 2022 02:12:03 +1100 Subject: [PATCH 09/63] Rename manage meta setting. --- assets/js/weighting/components/weighting/post-type.js | 10 +++++----- includes/classes/Feature/Search/Weighting.php | 4 ++-- includes/classes/Upgrades.php | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/assets/js/weighting/components/weighting/post-type.js b/assets/js/weighting/components/weighting/post-type.js index effee68336..5819249f5f 100644 --- a/assets/js/weighting/components/weighting/post-type.js +++ b/assets/js/weighting/components/weighting/post-type.js @@ -64,11 +64,11 @@ export default ({ groups, label, onChange, originalValues, values }) => { * When disabling manual meta management remove weighting settings for * metadata. * - * @param {Array} manageMeta New manage meta value. + * @param {Array} managingMeta New manage meta value. * @returns {void}} */ - const onChangeManageMeta = (manageMeta) => { - if (manageMeta === false) { + const onChangeManageMeta = (managingMeta) => { + if (managingMeta === false) { const keys = Object.keys(groups.meta?.children || {}); for (const k of keys) { @@ -76,7 +76,7 @@ export default ({ groups, label, onChange, originalValues, values }) => { } } - onChange({ ...values, manage_meta: manageMeta }); + onChange({ ...values, managing_meta: managingMeta }); }; /** @@ -115,7 +115,7 @@ export default ({ groups, label, onChange, originalValues, values }) => { fields={Object.values(children)} key={key} label={label} - manual={key === 'meta' ? values.manage_meta : null} + manual={key === 'meta' ? values.managing_meta : null} onChange={onChangeFields} onChangeManual={key === 'meta' ? onChangeManageMeta : null} originalValues={originalValues.fields} diff --git a/includes/classes/Feature/Search/Weighting.php b/includes/classes/Feature/Search/Weighting.php index 520d816a12..972f60ca75 100644 --- a/includes/classes/Feature/Search/Weighting.php +++ b/includes/classes/Feature/Search/Weighting.php @@ -265,8 +265,8 @@ public function get_weighting_configuration_with_defaults() { $weighting[ $post_type ]['fields'] = wp_parse_args( $weighting[ $post_type ]['fields'], $defaults ); } else { $weighting[ $post_type ] = [ - 'fields' => $defaults, - 'manage_meta' => false, + 'fields' => $defaults, + 'managing_meta' => false, ]; } } diff --git a/includes/classes/Upgrades.php b/includes/classes/Upgrades.php index 37c13180ca..3519caeba2 100644 --- a/includes/classes/Upgrades.php +++ b/includes/classes/Upgrades.php @@ -190,7 +190,7 @@ public function upgrade_4_2_2() { * Upgrade routine of v4.4.0. * * Migrates weighting settings into a `fields` property of the weighting - * configuration and adds the `manage_meta` setting. + * configuration and adds the `managing_meta` setting. * * @since 4.4.0 * @return void @@ -205,8 +205,8 @@ public function upgrade_4_4_0() { foreach ( $weighting_configuration as $post_type => $config ) { if ( ! isset( $config['fields'] ) ) { $weighting_configuration[ $post_type ] = [ - 'fields' => $config, - 'manage_meta' => false, + 'fields' => $config, + 'managing_meta' => false, ]; } } From 207291e5aebb204c69169f50b035464ae58f0cf5 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Fri, 21 Oct 2022 03:09:37 +1100 Subject: [PATCH 10/63] WIP: Implement global meta management control. --- assets/css/weighting.css | 86 +------------------ assets/css/weighting/action.css | 6 ++ assets/css/weighting/add-new.css | 7 ++ assets/css/weighting/field.css | 50 +++++++++++ assets/css/weighting/screen.css | 15 ++++ .../components/common/delete-button.js | 8 +- .../components/common/undo-button.js | 4 +- assets/js/weighting/components/weighting.js | 80 +++++++++++++---- .../components/weighting/meta-mode.js | 36 ++++++++ .../components/weighting/post-type.js | 67 +++++---------- .../components/weighting/post-type/fields.js | 3 +- .../components/weighting/post-type/group.js | 70 --------------- assets/js/weighting/index.js | 3 +- includes/classes/Feature/Search/Weighting.php | 59 ++++++------- includes/classes/Upgrades.php | 29 ------- includes/dashboard.php | 2 + 16 files changed, 242 insertions(+), 283 deletions(-) create mode 100644 assets/css/weighting/action.css create mode 100644 assets/css/weighting/add-new.css create mode 100644 assets/css/weighting/field.css create mode 100644 assets/css/weighting/screen.css create mode 100644 assets/js/weighting/components/weighting/meta-mode.js delete mode 100644 assets/js/weighting/components/weighting/post-type/group.js diff --git a/assets/css/weighting.css b/assets/css/weighting.css index 26e983ef70..5478309da2 100644 --- a/assets/css/weighting.css +++ b/assets/css/weighting.css @@ -1,82 +1,4 @@ -#ep-weighting-screen { - max-width: 800px; - - & .components-panel { - margin-bottom: 1rem; - } - - & .components-toggle-control__label { - max-width: none; - } -} - -.ep-weighting-field { - align-items: center; - display: grid; - grid-gap: 0.5em; - grid-template-columns: min(20%, 30ch) max-content auto max-content; - width: 100%; - - & .components-base-control__field { - margin-bottom: 0; - } - - & .components-range-control { - - & .components-base-control__field { - align-items: center; - display: flex; - } - - & .components-base-control__label { - margin-bottom: 0; - } - } - - @media ( max-width: 600px ) { - grid-gap: 1em; - - & .components-range-control__wrapper { - display: none; - } - } -} - -.ep-weighting-field fieldset { - display: contents; -} - -.ep-weighting-field__name { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - - & h2 { - margin: 0; - } -} - -.ep-weighting-field__undo { - grid-column-start: 4; - justify-self: end; -} - - -.ep-weighting-delete, -.ep-weighting-undo { - - &[disabled] { - visibility: hidden; - } -} - -.ep-weighting-add-meta { - justify-content: start; -} - -.ep-weighting-add-meta__select { - - & select.components-select-control__input { - min-height: 36px; - } -} +@import "weighting/action.css"; +@import "weighting/add-new.css"; +@import "weighting/field.css"; +@import "weighting/screen.css"; diff --git a/assets/css/weighting/action.css b/assets/css/weighting/action.css new file mode 100644 index 0000000000..ba2084222e --- /dev/null +++ b/assets/css/weighting/action.css @@ -0,0 +1,6 @@ +.ep-weighting-action { + + &[disabled] { + visibility: hidden; + } +} diff --git a/assets/css/weighting/add-new.css b/assets/css/weighting/add-new.css new file mode 100644 index 0000000000..88e9c45482 --- /dev/null +++ b/assets/css/weighting/add-new.css @@ -0,0 +1,7 @@ +.ep-weighting-add-new { + justify-content: start; + + & .components-base-control .components-select-control .components-select-control__input { + min-height: 36px; + } +} diff --git a/assets/css/weighting/field.css b/assets/css/weighting/field.css new file mode 100644 index 0000000000..8891dc4afd --- /dev/null +++ b/assets/css/weighting/field.css @@ -0,0 +1,50 @@ +.ep-weighting-field { + align-items: center; + display: grid; + grid-gap: 0.5em; + grid-template-columns: min(20%, 30ch) max-content auto max-content; + width: 100%; + + & .components-base-control__field { + margin-bottom: 0; + } + + & .components-range-control { + + & .components-base-control__field { + align-items: center; + display: flex; + } + + & .components-base-control__label { + margin-bottom: 0; + } + } + + @media ( max-width: 600px ) { + grid-gap: 1em; + + & .components-range-control__wrapper { + display: none; + } + } +} + +.ep-weighting-field fieldset { + display: contents; +} + +.ep-weighting-field__name { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + & h2 { + margin: 0; + } +} + +.ep-weighting-field__undo { + grid-column-start: 4; + justify-self: end; +} diff --git a/assets/css/weighting/screen.css b/assets/css/weighting/screen.css new file mode 100644 index 0000000000..f2ccc02e63 --- /dev/null +++ b/assets/css/weighting/screen.css @@ -0,0 +1,15 @@ +.ep-weighting-screen { + max-width: 800px; + + & .components-panel { + margin-bottom: 1rem; + } + + & .components-toggle-control__label { + max-width: none; + } + + & .components-base-control__help { + margin-bottom: 0; + } +} diff --git a/assets/js/weighting/components/common/delete-button.js b/assets/js/weighting/components/common/delete-button.js index 64856aae83..6a3d37a118 100644 --- a/assets/js/weighting/components/common/delete-button.js +++ b/assets/js/weighting/components/common/delete-button.js @@ -15,5 +15,11 @@ export default (props) => { /** * Render. */ - return -
- - - - + <> + +   + + ); }; diff --git a/package-lock.json b/package-lock.json index 912c032a1e..5e2c1724df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,6 @@ "@wordpress/icons": "^6.1.1", "chart.js": "^2.9.4", "focus-trap-react": "^8.8.2", - "prop-types": "^15.8.1", "react-beautiful-dnd": "^11.0.5", "react-slider": "^1.3.1", "uuid": "^8.3.2" From 54de7aa303b17c8db46a2f000372df0801c566ec Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Mon, 31 Oct 2022 20:50:44 +1100 Subject: [PATCH 27/63] Update weighting tests, add test for meta. --- .../features/search/weighting.cy.js | 317 +++++++++++++++--- 1 file changed, 267 insertions(+), 50 deletions(-) diff --git a/tests/cypress/integration/features/search/weighting.cy.js b/tests/cypress/integration/features/search/weighting.cy.js index 8578b1bf77..0b0d0aa1b4 100644 --- a/tests/cypress/integration/features/search/weighting.cy.js +++ b/tests/cypress/integration/features/search/weighting.cy.js @@ -1,87 +1,304 @@ describe('Post Search Feature - Weighting Functionality', () => { - it("Can't find a post by title if title is not marked as searchable", () => { - cy.login(); + /** + * Delete test posts before running tests. + */ + before(() => { + cy.wpCli( + 'post list --meta_key="_weighting_tests" --meta_value="1" --ep_integrate=false --format=ids', + ).then((wpCliResponse) => { + if (wpCliResponse.stdout) { + cy.wpCli(`post delete ${wpCliResponse.stdout} --force`); + } + }); + }); + /** + * Reset weighting settings and log in before each test. + */ + beforeEach(() => { cy.updateWeighting(); + cy.login(); + }); - cy.publishPost({ - title: 'supercustomtitle', - }); + /** + * Test that the Searchable checkbox works as expected. + */ + it("Can't find a post by title if title is not marked as searchable", () => { + /** + * Create post with a unique title. + */ + cy.wpCliEval( + `wp_insert_post( + [ + 'post_title' => 'supercustomtitle', + 'post_content' => '', + 'post_status' => 'publish', + 'meta_input' => [ + '_weighting_tests' => 1, + ], + ] + );`, + ); + /** + * Sync. + */ + cy.wpCli('wp elasticpress sync --yes').its('stdout').should('contain', 'Success: Done!'); + cy.wait(500); // eslint-disable-line cypress/no-unnecessary-waiting + + /** + * Search for the new post. It should be returned. + */ cy.visit('/?s=supercustomtitle'); - cy.get('.hentry').should('contain.text', 'supercustomtitle'); + cy.get('.entry-title').should('contain.text', 'supercustomtitle'); + /** + * Make the title non-searchable for Posts. + */ cy.visitAdminPage('admin.php?page=elasticpress-weighting'); - - cy.contains('.ep-weighting-post-type', 'Posts') - .contains('.ep-weighting-field', 'Title') + cy.get('.components-panel__header') + .contains('Posts') + .closest('.components-panel') + .find('fieldset') + .contains('Title') + .closest('fieldset') .find('input[type="checkbox"]') - .as('postTitleCheckbox'); - - cy.get('@postTitleCheckbox').uncheck(); + .uncheck(); + /** + * Save weighting settings. + */ cy.intercept('/wp-json/elasticpress/v1/weighting*').as('apiRequest'); - cy.get('button.is-primary').click(); + cy.get('button').contains('Save Changes').click(); cy.wait('@apiRequest'); - cy.visit('/?s=supercustomtitle'); - cy.get('.hentry').should('not.exist'); + /** + * Sync. + */ + cy.wpCli('wp elasticpress sync --yes').its('stdout').should('contain', 'Success: Done!'); + cy.wait(500); // eslint-disable-line cypress/no-unnecessary-waiting - // Reset setting. - cy.visitAdminPage('admin.php?page=elasticpress-weighting'); - cy.get('@postTitleCheckbox').check(); - cy.get('button.is-primary').click(); - cy.wait('@apiRequest'); + /** + * Search for the post again. No results should be returned. + */ + cy.visit('/?s=supercustomtitle'); + cy.get('.entry-title').should('not.exist'); }); + /** + * Test that adjusting weighting influences search results as expected. + */ it('Can increase post_title weighting and influence search results', () => { cy.login(); - const postsData = [ - { - title: 'test weighting content', - content: 'findbyweighting findbyweighting findbyweighting', - }, - { - title: 'test weighting title findbyweighting', - content: 'Nothing here.', - }, - ]; - - postsData.forEach((postData) => { - cy.publishPost(postData); - }); + cy.wpCliEval( + `wp_insert_post( + [ + 'post_title' => 'test weighting content', + 'post_content' => 'findbyweighting findbyweighting findbyweighting', + 'post_status' => 'publish', + 'meta_input' => [ + '_weighting_tests' => 1, + ], + ] + ); + + wp_insert_post( + [ + 'post_title' => 'test weighting title findbyweighting', + 'post_content' => 'Nothing here.', + 'post_status' => 'publish', + 'meta_input' => [ + '_weighting_tests' => 1, + ], + ] + );`, + ); + /** + * Sync. + */ + cy.wpCli('wp elasticpress sync --yes').its('stdout').should('contain', 'Success: Done!'); + cy.wait(500); // eslint-disable-line cypress/no-unnecessary-waiting + + /** + * Search for the test posts. Both should be returned. + */ cy.visit('/?s=findbyweighting'); - cy.contains('.site-content article:nth-of-type(1) h2', 'test weighting content').should( - 'exist', + cy.get('.entry-title').contains('test weighting content').should('exist'); + cy.get('.entry-title').contains('test weighting title findbyweighting').should('exist'); + + /** + * Adjust the weighting of the title. + */ + cy.visitAdminPage('admin.php?page=elasticpress-weighting'); + cy.get('.components-panel__header') + .contains('Posts') + .closest('.components-panel') + .find('fieldset') + .contains('Title') + .closest('fieldset') + .find('input[type="number"]') + .clearThenType(20); + + /** + * Save weighting settings. + */ + cy.intercept('/wp-json/elasticpress/v1/weighting*').as('apiRequest'); + cy.get('button').contains('Save Changes').click(); + cy.wait('@apiRequest'); + + /** + * Sync. + */ + cy.wpCli('wp elasticpress sync --yes').its('stdout').should('contain', 'Success: Done!'); + cy.wait(500); // eslint-disable-line cypress/no-unnecessary-waiting + + /** + * Search for the posts again. The post with the search term in the + * title should be returned first. + */ + cy.visit('/?s=findbyweighting'); + cy.get('.entry-title') + .first() + .should('contain.text', 'test weighting title findbyweighting'); + cy.get('.entry-title').last().should('contain.text', 'test weighting content'); + }); + + /** + * Test that searching meta fields works as expected. + */ + it('Can add, weight and search meta fields', () => { + /** + * Create a post with a custom field with a specific value and a post + * with the valyue as content for weighting comparison purposes. + */ + cy.wpCliEval( + `wp_insert_post( + [ + 'post_title' => 'Test meta weighting, post meta', + 'post_content' => '', + 'post_status' => 'publish', + 'meta_input' => [ + '_weighting_tests' => 1, + '_my_custom_field' => 'abc123', + ], + ] + ); + + wp_insert_post( + [ + 'post_title' => 'Test meta weighting, post content', + 'post_content' => 'abc123', + 'post_status' => 'publish', + 'meta_input' => [ + '_weighting_tests' => 1, + ], + ] + );`, ); + /** + * Sync. + */ + cy.wpCli('wp elasticpress sync --yes').its('stdout').should('contain', 'Success: Done!'); + cy.wait(500); // eslint-disable-line cypress/no-unnecessary-waiting + + /** + * Only the post with the value in its content should appear. + */ + cy.visit('/?s=abc123'); + cy.get('.entry-title').contains('Test meta weighting, post content').should('exist'); + cy.get('.entry-title').contains('Test meta weighting, post meta').should('not.exist'); + + /** + * Turn on manual metadata management. + */ cy.visitAdminPage('admin.php?page=elasticpress-weighting'); + cy.get('.components-checkbox-control') + .contains('Customize meta sync and search (may require re-sync)') + .parent() + .find('input') + .check(); - cy.contains('.ep-weighting-post-type', 'Posts') - .contains('.ep-weighting-field', 'Title') + /** + * Add the custom field to the posts index. + */ + cy.get('.components-panel__header') + .contains('Posts') + .closest('.components-panel') + .as('panel') + .find('fieldset') + .contains('Content') + .closest('fieldset') .find('input[type="number"]') - .as('postTitleWeight'); - - cy.get('@postTitleWeight').clearThenType('20'); + .clearThenType(100); + cy.get('@panel') + .find('select') + .should('contain', '_my_custom_field') + .select('_my_custom_field'); + cy.get('@panel').find('button').contains('Add field').click(); + cy.get('@panel') + .find('fieldset') + .contains('_my_custom_field') + .closest('fieldset') + .find('input[type="checkbox"]') + .check(); + /** + * Save weighting settings. + */ cy.intercept('/wp-json/elasticpress/v1/weighting*').as('apiRequest'); - cy.get('button.is-primary').click(); + cy.get('button').contains('Save Changes').click(); cy.wait('@apiRequest'); - cy.visit('/?s=findbyweighting'); - cy.contains( - '.site-content article:nth-of-type(1) h2', - 'test weighting title findbyweighting', - ).should('exist'); + /** + * Sync. + */ + cy.wpCli('wp elasticpress sync --yes').its('stdout').should('contain', 'Success: Done!'); + cy.wait(500); // eslint-disable-line cypress/no-unnecessary-waiting + + /** + * Both results should be returned,but the post with the value in its + * content should be returned first. + */ + cy.visit('/?s=abc123'); + cy.get('.entry-title').first().should('contain.text', 'Test meta weighting, post content'); + cy.get('.entry-title').last().should('contain.text', 'Test meta weighting, post meta'); - // Reset setting. + /** + * Update the weighting so the meta field is weighted higher. + */ cy.visitAdminPage('admin.php?page=elasticpress-weighting'); - cy.get('@postTitleWeight').clearThenType('1'); + cy.get('.components-panel__header') + .contains('Posts') + .closest('.components-panel') + .as('panel') + .find('fieldset') + .contains('Content') + .closest('fieldset') + .find('input[type="number"]') + .clearThenType(1); + cy.get('@panel') + .find('fieldset') + .contains('_my_custom_field') + .closest('fieldset') + .find('input[type="number"]') + .clearThenType(100); + /** + * Save weighting settings. + */ cy.intercept('/wp-json/elasticpress/v1/weighting*').as('apiRequest'); - cy.get('button.is-primary').click(); + cy.get('button').contains('Save Changes').click(); cy.wait('@apiRequest'); + + /** + * The post with the value in its content should be now be returned + * first. + */ + cy.visit('/?s=abc123'); + cy.get('.entry-title').first().should('contain.text', 'Test meta weighting, post meta'); + cy.get('.entry-title').last().should('contain.text', 'Test meta weighting, post content'); }); }); From a18faed79fc65016cd50a5fe2744fe3cd93ddef3 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Fri, 10 Mar 2023 02:33:17 +1100 Subject: [PATCH 28/63] Correct weighting script. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ab3b51112d..35b03c9884 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "synonyms-script": "./assets/js/synonyms/index.js", "woocommerce-order-search-script": "./assets/js/woocommerce/admin/orders/index.js", "admin-script": "./assets/js/admin.js", - "weighting-script": "./assets/js/weighting.js", + "weighting-script": "./assets/js/weighting/index.js", "search-editor-script": "./assets/js/search/editor/index.js", "autosuggest-styles": "./assets/css/autosuggest.css", "comments-styles": "./assets/css/comments.css", From 91399f63f62ed14b1a540adb26cbd24a1c938469 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Mon, 13 Mar 2023 16:07:14 +1100 Subject: [PATCH 29/63] Move meta mode to a filter. --- assets/js/weighting/components/weighting.js | 41 +++--------------- .../components/weighting/meta-mode.js | 43 ------------------- includes/classes/Feature/Search/Weighting.php | 30 +++++++------ tests/php/indexables/TestPost.php | 2 +- 4 files changed, 24 insertions(+), 92 deletions(-) delete mode 100644 assets/js/weighting/components/weighting/meta-mode.js diff --git a/assets/js/weighting/components/weighting.js b/assets/js/weighting/components/weighting.js index f0f386d8f2..b1faab40e1 100644 --- a/assets/js/weighting/components/weighting.js +++ b/assets/js/weighting/components/weighting.js @@ -11,7 +11,6 @@ import { isEqual } from 'lodash'; * Internal Dependencies. */ import Actions from './weighting/actions'; -import MetaMode from './weighting/meta-mode'; import PostType from './weighting/post-type'; /** @@ -34,12 +33,10 @@ const Weighting = ({ weightableFields, weightingConfiguration, }) => { - const [currentMetaMode, setCurrentMetaMode] = useState(metaMode); const [currentWeightingConfiguration, setCurrentWeightingConfiguration] = useState({ ...weightingConfiguration, }); - const [savedMetaMode, setSavedMetaMode] = useState(metaMode); const [savedWeightingConfiguration, setSavedWeightingConfiguration] = useState({ ...weightingConfiguration, }); @@ -50,34 +47,14 @@ const Weighting = ({ * Is the current data different to the saved data. */ const isChanged = useMemo( - () => - !( - currentMetaMode === savedMetaMode && - isEqual(currentWeightingConfiguration, savedWeightingConfiguration) - ), - [ - currentWeightingConfiguration, - currentMetaMode, - savedWeightingConfiguration, - savedMetaMode, - ], + () => !isEqual(currentWeightingConfiguration, savedWeightingConfiguration), + [currentWeightingConfiguration, savedWeightingConfiguration], ); /** * Whether to show weighting for metadata. */ - const showMeta = useMemo(() => currentMetaMode === 'manual', [currentMetaMode]); - - /** - * Handle meta mode change. - * - * @param {boolean} checked Is manual checked? - */ - const onChangeMetaMode = (checked) => { - const metaMode = checked ? 'manual' : 'auto'; - - setCurrentMetaMode(metaMode); - }; + const showMeta = useMemo(() => metaMode === 'manual', [metaMode]); /** * Handle data change. @@ -99,7 +76,6 @@ const Weighting = ({ * @returns {void} */ const onReset = () => { - setCurrentMetaMode(savedMetaMode); setCurrentWeightingConfiguration({ ...savedWeightingConfiguration }); }; @@ -116,10 +92,7 @@ const Weighting = ({ setIsBusy(true); const response = await apiFetch({ - body: JSON.stringify({ - meta_mode: currentMetaMode, - weighting_configuration: currentWeightingConfiguration, - }), + body: JSON.stringify(currentWeightingConfiguration), headers: { 'Content-Type': 'application/json', }, @@ -127,10 +100,7 @@ const Weighting = ({ url: apiUrl, }); - const { meta_mode, weighting_configuration } = response.data; - - setSavedWeightingConfiguration(weighting_configuration); - setSavedMetaMode(meta_mode); + setSavedWeightingConfiguration(response.data); noticeOperations.createNotice({ content: __('Search fields & weighting saved.', 'elasticpress'), @@ -169,7 +139,6 @@ const Weighting = ({ )}

- {Object.entries(weightableFields).map(([postType, { groups, label }]) => { const originalValues = savedWeightingConfiguration[postType] || {}; const values = currentWeightingConfiguration[postType] || {}; diff --git a/assets/js/weighting/components/weighting/meta-mode.js b/assets/js/weighting/components/weighting/meta-mode.js deleted file mode 100644 index 224837872e..0000000000 --- a/assets/js/weighting/components/weighting/meta-mode.js +++ /dev/null @@ -1,43 +0,0 @@ -/** - * WordPress dependencies. - */ -import { CheckboxControl, Panel, PanelBody, PanelRow } from '@wordpress/components'; -import { WPElement, createInterpolateElement } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; - -/** - * Metadata mode component. - * - * @param {object} props Component props. - * @param {boolean} props.checked Is manual checked. - * @param {Function} props.onChange Change handler. - * @returns {WPElement} Component element. - */ -export default ({ checked, onChange }) => { - /** - * Render. - */ - return ( - - - - If you leave this box unchecked, ElasticPress will index all public meta (i.e. meta that does not begin with _).', - ), - // eslint-disable-next-line jsx-a11y/anchor-has-content, jsx-a11y/control-has-associated-label - { br:
, code: }, - )} - label={__( - 'Customize meta sync and search (may require re-sync)', - 'elasticpress', - )} - onChange={onChange} - /> -
-
-
- ); -}; diff --git a/includes/classes/Feature/Search/Weighting.php b/includes/classes/Feature/Search/Weighting.php index 66659fe30b..da09f0d6b6 100644 --- a/includes/classes/Feature/Search/Weighting.php +++ b/includes/classes/Feature/Search/Weighting.php @@ -272,13 +272,26 @@ public function get_weighting_configuration_with_defaults() { * Returns the current meta mode. * * @return array - * @since 4.4.0 + * @since 5.0.0 */ public function get_meta_mode() { if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) { return 'auto'; } - return get_option( 'ep_meta_mode', 'auto' ); + + /** + * Filter meta management mode. + * + * Setting the meta mode to 'auto' will restore the pre-5.0.0 behavior + * of syncing all public meta fields, but without the ability to + * add fields to make them searchable through the UI. + * + * @hook ep_meta_mode + * @since 5.0.0 + * @param string $ep_meta_mode Meta mode. + * @return string New meta mode. + */ + return apply_filters( 'ep_meta_mode', 'manual' ); } /** @@ -438,10 +451,8 @@ public function handle_save( $request = null ) { return; } - $meta_mode = $request->get_param( 'meta_mode' ); - $weighting = $request->get_param( 'weighting_configuration' ); - - update_option( 'ep_meta_mode', $meta_mode ); + $meta_mode = $this->get_meta_mode(); + $weighting = $request->get_json_params(); /** * If metadata is not being managed manually, remove any metadata @@ -470,12 +481,7 @@ public function handle_save( $request = null ) { */ do_action( 'ep_saved_weighting_configuration' ); - wp_send_json_success( - [ - 'meta_mode' => $meta_mode, - 'weighting_configuration' => $weighting, - ] - ); + wp_send_json_success( $weighting ); exit; } diff --git a/tests/php/indexables/TestPost.php b/tests/php/indexables/TestPost.php index 8b4a4ed35d..1427b90694 100644 --- a/tests/php/indexables/TestPost.php +++ b/tests/php/indexables/TestPost.php @@ -3836,7 +3836,7 @@ public function testPrepareMetaManual() { $change_meta_mode = function() { return 'manual'; }; - add_filter( 'pre_option_ep_meta_mode', $change_meta_mode ); + add_filter( 'ep_meta_mode', $change_meta_mode ); $weighting = ElasticPress\Features::factory()->get_registered_feature( 'search' )->weighting; $this->assertSame( $weighting->get_meta_mode(), 'manual' ); From cf3f002c33139612724c6d512c8dcdc38e709e3d Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Mon, 13 Mar 2023 16:20:56 +1100 Subject: [PATCH 30/63] Update copy to replace index with sync. --- .../components/weighting/post-type/field.js | 2 +- .../components/weighting/post-type/fields.js | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/assets/js/weighting/components/weighting/post-type/field.js b/assets/js/weighting/components/weighting/post-type/field.js index 7bd5e28ceb..3881b1c317 100644 --- a/assets/js/weighting/components/weighting/post-type/field.js +++ b/assets/js/weighting/components/weighting/post-type/field.js @@ -35,7 +35,7 @@ export default ({ label, onChange, onDelete, originalValue, value }) => { ); /** - * Handle change of indexable. + * Handle change of searchable. * * @param {boolean} enabled New searchable value. * @returns {void} diff --git a/assets/js/weighting/components/weighting/post-type/fields.js b/assets/js/weighting/components/weighting/post-type/fields.js index f03f181323..14e167e44c 100644 --- a/assets/js/weighting/components/weighting/post-type/fields.js +++ b/assets/js/weighting/components/weighting/post-type/fields.js @@ -28,7 +28,7 @@ export default ({ fields, isEditable, onChange, originalValues, values }) => { /** * Weightable fields that can be added to the group, if it is editable. * - * Fields that are automatically indexed by features are excluded, while + * Fields that are automatically synced by features are excluded, while * fields that have already been added are disabled. */ const availableFields = useMemo(() => { @@ -42,7 +42,7 @@ export default ({ fields, isEditable, onChange, originalValues, values }) => { }, [fields, values]); /** - * Fields that are automatically indexed by features. + * Fields that are automatically synced by features. */ const featureFields = useMemo(() => { return fields.filter((f) => f.used_by_feature); @@ -90,10 +90,10 @@ export default ({ fields, isEditable, onChange, originalValues, values }) => { }; /** - * Handle a change to whether fields automatically indexed by features are + * Handle a change to whether fields automatically synced by features are * shown. * - * @param {boolean} showFeatureFields Whether to show fields indexed by features. + * @param {boolean} showFeatureFields Whether to show fields synced by features. * @returns {void} */ const onChangeShowFeatureFields = (showFeatureFields) => { @@ -135,7 +135,7 @@ export default ({ fields, isEditable, onChange, originalValues, values }) => { @@ -180,7 +180,7 @@ export default ({ fields, isEditable, onChange, originalValues, values }) => { labelPosition="side" onChange={onChangeToAdd} options={[ - { value: '', label: 'Select field to index', disabled: true }, + { value: '', label: 'Select field to sync', disabled: true }, ...availableFields, ]} value={toAdd} From 63a899e874befecf07a9a9c72cdc203a6e037db0 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Mon, 13 Mar 2023 16:24:26 +1100 Subject: [PATCH 31/63] Update ep_meta_mode hook description. --- includes/classes/Feature/Search/Weighting.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/includes/classes/Feature/Search/Weighting.php b/includes/classes/Feature/Search/Weighting.php index da09f0d6b6..979c66e785 100644 --- a/includes/classes/Feature/Search/Weighting.php +++ b/includes/classes/Feature/Search/Weighting.php @@ -284,7 +284,9 @@ public function get_meta_mode() { * * Setting the meta mode to 'auto' will restore the pre-5.0.0 behavior * of syncing all public meta fields, but without the ability to - * add fields to make them searchable through the UI. + * add fields to make them searchable through the UI. Weighting settings + * will need to be changed, and a sync performed, any time this value + * is changed. * * @hook ep_meta_mode * @since 5.0.0 From 272767b38162ed76f95f7b41fadaa8a309c5f56c Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Mon, 13 Mar 2023 17:50:47 +1100 Subject: [PATCH 32/63] Require a sync when fields are added. --- assets/js/weighting/components/weighting.js | 37 ++++++++++++++++++- .../weighting/components/weighting/actions.js | 5 ++- assets/js/weighting/index.js | 4 +- assets/js/weighting/utilities.js | 29 +++++++++++++++ includes/dashboard.php | 2 + 5 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 assets/js/weighting/utilities.js diff --git a/assets/js/weighting/components/weighting.js b/assets/js/weighting/components/weighting.js index b1faab40e1..1d5bcd78e8 100644 --- a/assets/js/weighting/components/weighting.js +++ b/assets/js/weighting/components/weighting.js @@ -10,6 +10,7 @@ import { isEqual } from 'lodash'; /** * Internal Dependencies. */ +import { getMetaFieldsFromWeightingConfiguration, isFieldSyncedByFeature } from '../utilities'; import Actions from './weighting/actions'; import PostType from './weighting/post-type'; @@ -21,6 +22,7 @@ import PostType from './weighting/post-type'; * @param {'auto'|'manual'} props.metaMode Metadata management mode. * @param {object} props.noticeOperations Notice operations from withNotices. * @param {WPElement} props.noticeUI Notice UI from withNotices. + * @param {string} props.syncUrl Sync URL. * @param {object} props.weightableFields Weightable fields, indexed by post type. * @param {object} props.weightingConfiguration Weighting configuration, indexed by post type. * @returns {WPElement} Element. @@ -30,6 +32,7 @@ const Weighting = ({ metaMode, noticeOperations, noticeUI, + syncUrl, weightableFields, weightingConfiguration, }) => { @@ -51,6 +54,27 @@ const Weighting = ({ [currentWeightingConfiguration, savedWeightingConfiguration], ); + /** + * Is a sync going to be required? + * + * A sync is required whenever a new meta field is added unless that field + * is already being synced by a feature. Removing a meta field does not + * require a sync, but the user may choose to perform one if they need the + * field removed from the index. + * + * @returns {boolean} Whether a sync is required. + */ + const isSyncRequired = useMemo(() => { + const savedMeta = getMetaFieldsFromWeightingConfiguration(savedWeightingConfiguration); + const currentMeta = getMetaFieldsFromWeightingConfiguration(currentWeightingConfiguration); + + const newMeta = currentMeta.filter( + (c) => !savedMeta.includes(c) && !isFieldSyncedByFeature(c, weightableFields), + ); + + return newMeta.length > 0; + }, [currentWeightingConfiguration, savedWeightingConfiguration, weightableFields]); + /** * Whether to show weighting for metadata. */ @@ -100,6 +124,11 @@ const Weighting = ({ url: apiUrl, }); + if (isSyncRequired) { + window.location = syncUrl; + return; + } + setSavedWeightingConfiguration(response.data); noticeOperations.createNotice({ @@ -157,7 +186,13 @@ const Weighting = ({ /> ); })} - + ); }; diff --git a/assets/js/weighting/components/weighting/actions.js b/assets/js/weighting/components/weighting/actions.js index e9f5b9ba75..0d6109a23c 100644 --- a/assets/js/weighting/components/weighting/actions.js +++ b/assets/js/weighting/components/weighting/actions.js @@ -12,17 +12,18 @@ import { undo } from '@wordpress/icons'; * @param {object} props Component props. * @param {boolean} props.isBusy Is the app busy? * @param {boolean} props.isChanged Are there changes? + * @param {boolean} props.isSyncRequired Is a sync required? * @param {Function} props.onReset Reset handler. * @returns {WPElement} Component element. */ -export default ({ isBusy, isChanged, onReset }) => { +export default ({ isBusy, isChanged, isSyncRequired, onReset }) => { /** * Render. */ return ( <>     + + ) : null} + + ); +}; diff --git a/assets/js/weighting/components/post-type.js b/assets/js/weighting/components/post-type.js new file mode 100644 index 0000000000..c0f0471e77 --- /dev/null +++ b/assets/js/weighting/components/post-type.js @@ -0,0 +1,49 @@ +/** + * WordPress dependencies. + */ +import { Panel, PanelBody, PanelHeader } from '@wordpress/components'; +import { WPElement } from '@wordpress/element'; + +/** + * Internal dependencies. + */ +import { useWeighting } from '../provider'; +import Group from './group'; + +/** + * Post type weighting settings component. + * + * @param {object} props Components props. + * @param {object[]} props.postType Post type. + * @returns {WPElement} Component element. + */ +export default ({ postType }) => { + const { isManual, weightableFields } = useWeighting(); + + const { label, groups } = weightableFields.find((f) => f.key === postType); + + /** + * Render. + */ + return ( + + +
+
+

{label}

+
+
+
+ + {groups.map(({ key, label }) => { + const isMetadata = key === 'ep_metadata'; + + return !isMetadata || isManual ? ( + + + + ) : null; + })} + + ); +}; diff --git a/assets/js/weighting/components/weighting.js b/assets/js/weighting/components/weighting.js deleted file mode 100644 index 1d5bcd78e8..0000000000 --- a/assets/js/weighting/components/weighting.js +++ /dev/null @@ -1,200 +0,0 @@ -/** - * WordPress dependencies. - */ -import apiFetch from '@wordpress/api-fetch'; -import { withNotices } from '@wordpress/components'; -import { WPElement, useMemo, useState } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; -import { isEqual } from 'lodash'; - -/** - * Internal Dependencies. - */ -import { getMetaFieldsFromWeightingConfiguration, isFieldSyncedByFeature } from '../utilities'; -import Actions from './weighting/actions'; -import PostType from './weighting/post-type'; - -/** - * Weighting settings app. - * - * @param {object} props Component props. - * @param {string} props.apiUrl API URL. - * @param {'auto'|'manual'} props.metaMode Metadata management mode. - * @param {object} props.noticeOperations Notice operations from withNotices. - * @param {WPElement} props.noticeUI Notice UI from withNotices. - * @param {string} props.syncUrl Sync URL. - * @param {object} props.weightableFields Weightable fields, indexed by post type. - * @param {object} props.weightingConfiguration Weighting configuration, indexed by post type. - * @returns {WPElement} Element. - */ -const Weighting = ({ - apiUrl, - metaMode, - noticeOperations, - noticeUI, - syncUrl, - weightableFields, - weightingConfiguration, -}) => { - const [currentWeightingConfiguration, setCurrentWeightingConfiguration] = useState({ - ...weightingConfiguration, - }); - - const [savedWeightingConfiguration, setSavedWeightingConfiguration] = useState({ - ...weightingConfiguration, - }); - - const [isBusy, setIsBusy] = useState(false); - - /** - * Is the current data different to the saved data. - */ - const isChanged = useMemo( - () => !isEqual(currentWeightingConfiguration, savedWeightingConfiguration), - [currentWeightingConfiguration, savedWeightingConfiguration], - ); - - /** - * Is a sync going to be required? - * - * A sync is required whenever a new meta field is added unless that field - * is already being synced by a feature. Removing a meta field does not - * require a sync, but the user may choose to perform one if they need the - * field removed from the index. - * - * @returns {boolean} Whether a sync is required. - */ - const isSyncRequired = useMemo(() => { - const savedMeta = getMetaFieldsFromWeightingConfiguration(savedWeightingConfiguration); - const currentMeta = getMetaFieldsFromWeightingConfiguration(currentWeightingConfiguration); - - const newMeta = currentMeta.filter( - (c) => !savedMeta.includes(c) && !isFieldSyncedByFeature(c, weightableFields), - ); - - return newMeta.length > 0; - }, [currentWeightingConfiguration, savedWeightingConfiguration, weightableFields]); - - /** - * Whether to show weighting for metadata. - */ - const showMeta = useMemo(() => metaMode === 'manual', [metaMode]); - - /** - * Handle data change. - * - * @param {string} postType Updated post type. - * @param {Array} values Updated data. - * @returns {void} - */ - const onChangePostType = (postType, values) => { - setCurrentWeightingConfiguration({ - ...currentWeightingConfiguration, - [postType]: values, - }); - }; - - /** - * Handle resetting all settings. - * - * @returns {void} - */ - const onReset = () => { - setCurrentWeightingConfiguration({ ...savedWeightingConfiguration }); - }; - - /** - * Handle for submission. - * - * @param {Event} event Submit event. - * @returns {void} - */ - const onSubmit = async (event) => { - event.preventDefault(); - - try { - setIsBusy(true); - - const response = await apiFetch({ - body: JSON.stringify(currentWeightingConfiguration), - headers: { - 'Content-Type': 'application/json', - }, - method: 'POST', - url: apiUrl, - }); - - if (isSyncRequired) { - window.location = syncUrl; - return; - } - - setSavedWeightingConfiguration(response.data); - - noticeOperations.createNotice({ - content: __('Search fields & weighting saved.', 'elasticpress'), - status: 'success', - }); - } catch { - noticeOperations.createNotice({ - content: __('Whoops! Something went wrong. Please try again.', 'elasticpress'), - status: 'error', - }); - } finally { - document.body.scrollIntoView({ behavior: 'smooth' }); - - setIsBusy(false); - } - }; - - /** - * Render. - */ - return ( -
-

{__('Manage Search Fields & Weighting', 'elasticpress')}

- {noticeUI} -
-

- {__( - 'This dashboard enables you to select which fields ElasticPress should sync, whether to use those fields in searches, and how heavily to weight fields in the search algorithm. In general, increasing the Weight of a field will increase the relevancy score of a post that has matching text in that field.', - 'elasticpress', - )} -

-

- {__( - 'For example, adding more weight to the title attribute will cause search matches on the post title to appear more prominently.', - 'elasticpress', - )} -

-
- {Object.entries(weightableFields).map(([postType, { groups, label }]) => { - const originalValues = savedWeightingConfiguration[postType] || {}; - const values = currentWeightingConfiguration[postType] || {}; - - return ( - { - onChangePostType(postType, values); - }} - originalValues={originalValues} - showMeta={showMeta} - values={values} - /> - ); - })} - - - ); -}; - -export default withNotices(Weighting); diff --git a/assets/js/weighting/components/weighting/post-type.js b/assets/js/weighting/components/weighting/post-type.js deleted file mode 100644 index 5f5c38673f..0000000000 --- a/assets/js/weighting/components/weighting/post-type.js +++ /dev/null @@ -1,101 +0,0 @@ -/** - * WordPress dependencies. - */ -import { Panel, PanelBody, PanelHeader } from '@wordpress/components'; -import { useMemo, WPElement } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; -import { isEqual } from 'lodash'; - -/** - * Internal dependencies. - */ -import Fields from './post-type/fields'; -import UndoButton from '../common/undo-button'; - -/** - * Post type weighting settings component. - * - * @param {object} props Components props. - * @param {object[]} props.groups Field groups. - * @param {string} props.label Post type label. - * @param {Function} props.onChange Data change handler. - * @param {object} props.originalValues Saved post type settings. - * @param {boolean} props.showMeta Whether to show the meta group. - * @param {object} props.values Current post type settings. - * @returns {WPElement} Component element. - */ -export default ({ groups, label, onChange, originalValues, showMeta, values }) => { - /** - * Have any values changed? - */ - const isChanged = useMemo(() => !isEqual(originalValues, values), [originalValues, values]); - - /** - * The field groups to display. - * - * Filters out any groups without fields. - */ - const fieldGroups = useMemo( - () => - Object.entries(groups).reduce((previousValue, currentValue) => { - const [key, group] = currentValue; - - if (Object.keys(group.children).length > 0) { - return [...previousValue, { key, ...group }]; - } - - return previousValue; - }, []), - [groups], - ); - - /** - * Handle resetting all data for the post type. - * - * @returns {void} - */ - const onReset = () => { - onChange(originalValues); - }; - - /** - * Render. - */ - return ( - - -
-
-

{label}

-
-
- {isChanged ? ( - - ) : null} -
-
-
- {fieldGroups.map(({ children, key, label }) => { - const isMeta = key === 'ep_metadata'; - const fields = Object.values(children); - - return !isMeta || showMeta ? ( - - - - ) : null; - })} -
- ); -}; diff --git a/assets/js/weighting/components/weighting/post-type/fields.js b/assets/js/weighting/components/weighting/post-type/fields.js deleted file mode 100644 index 14e167e44c..0000000000 --- a/assets/js/weighting/components/weighting/post-type/fields.js +++ /dev/null @@ -1,196 +0,0 @@ -/** - * WordPress dependencies. - */ -import { Button, PanelRow, SelectControl, ToggleControl } from '@wordpress/components'; -import { useMemo, useState, WPElement } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; - -/** - * Internal dependencies. - */ -import Field from './field'; - -/** - * Post type propertes component. - * - * @param {object} props Component props. - * @param {object[]} props.fields Post type fields. - * @param {boolean} props.isEditable Whether to display as an editable list. - * @param {Function} props.onChange Change handler. - * @param {object[]} props.originalValues Saved property values. - * @param {object[]} props.values Current property values. - * @returns {WPElement} Component element. - */ -export default ({ fields, isEditable, onChange, originalValues, values }) => { - const [toAdd, setToAdd] = useState(''); - const [showFeatureFields, setShowFeatureFields] = useState(false); - - /** - * Weightable fields that can be added to the group, if it is editable. - * - * Fields that are automatically synced by features are excluded, while - * fields that have already been added are disabled. - */ - const availableFields = useMemo(() => { - return fields - .filter((f) => !f.used_by_feature) - .map((f) => ({ - label: f.label, - value: f.key, - disabled: Object.prototype.hasOwnProperty.call(values, f.key), - })); - }, [fields, values]); - - /** - * Fields that are automatically synced by features. - */ - const featureFields = useMemo(() => { - return fields.filter((f) => f.used_by_feature); - }, [fields]); - - /** - * Fields that can be weighted. - * - * For editable groups fields are sorted to match the order of the saved - * configuration, to preserve the order in which the fields were added. - */ - const weightedFields = useMemo(() => { - return fields - .filter((f) => !f.used_by_feature) - .filter((f) => Object.prototype.hasOwnProperty.call(values, f.key)) - .sort((a, b) => { - const { key: aKey } = a; - const { key: bKey } = b; - - const keys = Object.keys(values); - - return keys.indexOf(aKey) - keys.indexOf(bKey); - }); - }, [fields, values]); - - /** - * Handle changes to a property. - * - * @param {object} value New property data. - * @param {number} key Property key. - * @returns {void} - */ - const onChangeProperty = (value, key) => { - onChange({ ...values, [key]: value }); - }; - - /** - * Handle selecting a new property to enable. - * - * @param {string} key Key of property to enable. - * @returns {void} - */ - const onChangeToAdd = (key) => { - setToAdd(key); - }; - - /** - * Handle a change to whether fields automatically synced by features are - * shown. - * - * @param {boolean} showFeatureFields Whether to show fields synced by features. - * @returns {void} - */ - const onChangeShowFeatureFields = (showFeatureFields) => { - setShowFeatureFields(showFeatureFields); - }; - - /** - * Handle clicking to add a new property. - * - * @returns {void} - */ - const onClickAdd = () => { - const newValues = { ...values, [toAdd]: { enabled: false, weight: 0 } }; - - onChange(newValues); - setToAdd(''); - }; - - /** - * Handle removing a field. - * - * @param {number} key field key. - * @returns {void} - */ - const onDeleteField = (key) => { - const newValues = { ...values }; - - delete newValues[key]; - - onChange(newValues); - }; - - /** - * Render. - */ - return ( - <> - {featureFields.length > 0 ? ( - - - - ) : null} - {featureFields - .filter((f) => showFeatureFields || values?.[f.key]?.enabled === true) - .map(({ key, label }) => ( - - { - onChangeProperty(value, key); - }} - /> - - ))} - {(isEditable ? weightedFields : fields).map(({ key, label }) => ( - - { - onChangeProperty(value, key); - }} - onDelete={ - isEditable - ? () => { - onDeleteField(key); - } - : null - } - /> - - ))} - {isEditable && availableFields ? ( - - -   - - - ) : null} - - ); -}; diff --git a/assets/js/weighting/config.js b/assets/js/weighting/config.js new file mode 100644 index 0000000000..5ee2464a03 --- /dev/null +++ b/assets/js/weighting/config.js @@ -0,0 +1,3 @@ +const { apiUrl, metaMode, syncUrl, weightableFields, weightingConfiguration } = window.epWeighting; + +export { apiUrl, metaMode, syncUrl, weightableFields, weightingConfiguration }; diff --git a/assets/js/weighting/index.js b/assets/js/weighting/index.js index d60b84b7e4..4cf62a8417 100644 --- a/assets/js/weighting/index.js +++ b/assets/js/weighting/index.js @@ -1,32 +1,36 @@ /** * WordPress dependencies. */ -import { render } from '@wordpress/element'; +import { createRoot, render, WPElement } from '@wordpress/element'; /** * Internal Dependencies. */ -import Weighting from './components/weighting'; +import { apiUrl, metaMode, syncUrl, weightableFields, weightingConfiguration } from './config'; +import WeightingProvider from './provider'; +import SettingsPage from './apps/settings-page'; /** - * Initialize. + * App component. * - * @returns {void} + * @returns {WPElement} App component. */ -const init = () => { - const { apiUrl, metaMode, syncUrl, weightableFields, weightingConfiguration } = - window.epWeighting; +const App = () => ( + + + +); - render( - , - document.getElementById('ep-weighting-screen'), - ); -}; +if (typeof createRoot === 'function') { + const root = createRoot(document.getElementById('ep-weighting-screen')); -init(); + root.render(); +} else { + render(, document.getElementById('ep-weighting-screen')); +} diff --git a/assets/js/weighting/provider.js b/assets/js/weighting/provider.js new file mode 100644 index 0000000000..d5db4518f5 --- /dev/null +++ b/assets/js/weighting/provider.js @@ -0,0 +1,149 @@ +/** + * WordPress dependencies. + */ +import apiFetch from '@wordpress/api-fetch'; +import { withNotices } from '@wordpress/components'; +import { createContext, WPElement, useContext, useMemo, useState } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { isEqual } from 'lodash'; + +/** + * Instant Results context. + */ +const Context = createContext(); + +/** + * Weighting settings app. + * + * @param {object} props Component props. + * @param {string} props.apiUrl API URL. + * @param {Function} props.children Component children. + * @param {'auto'|'manual'} props.metaMode Metadata management mode. + * @param {object} props.noticeOperations Notice operations from withNotices. + * @param {WPElement} props.noticeUI Notice UI from withNotices. + * @param {object} props.weightableFields Weightable fields, indexed by post type. + * @param {object} props.weightingConfiguration Weighting configuration, indexed by post type. + * @returns {WPElement} Element. + */ +const WeightingProvider = ({ + apiUrl, + children, + metaMode, + noticeOperations, + noticeUI, + weightableFields, + weightingConfiguration, +}) => { + const [currentWeightingConfiguration, setCurrentWeightingConfiguration] = useState({ + ...weightingConfiguration, + }); + + const [savedWeightingConfiguration, setSavedWeightingConfiguration] = useState({ + ...weightingConfiguration, + }); + + const [isBusy, setIsBusy] = useState(false); + + /** + * Is the current data different to the saved data. + */ + const isChanged = useMemo( + () => !isEqual(currentWeightingConfiguration, savedWeightingConfiguration), + [currentWeightingConfiguration, savedWeightingConfiguration], + ); + + /** + * Whether to show weighting for metadata. + */ + const isManual = useMemo(() => metaMode === 'manual', [metaMode]); + + /** + * Handle data change. + * + * @param {string} postType Post type to update. + * @param {Array} values New valus. + * @returns {void} + */ + const setWeightingForPostType = (postType, values) => { + setCurrentWeightingConfiguration({ + ...currentWeightingConfiguration, + [postType]: values, + }); + }; + + /** + * Handle resetting all settings. + * + * @returns {void} + */ + const reset = () => { + setCurrentWeightingConfiguration({ ...savedWeightingConfiguration }); + }; + + /** + * Save settings. + * + * @returns {void} + */ + const save = async () => { + try { + setIsBusy(true); + + const response = await apiFetch({ + body: JSON.stringify(currentWeightingConfiguration), + headers: { + 'Content-Type': 'application/json', + }, + method: 'POST', + url: apiUrl, + }); + + setSavedWeightingConfiguration(response.data); + + noticeOperations.createNotice({ + content: __('Search fields & weighting saved.', 'elasticpress'), + status: 'success', + }); + } catch { + noticeOperations.createNotice({ + content: __('Whoops! Something went wrong. Please try again.', 'elasticpress'), + status: 'error', + }); + } finally { + document.body.scrollIntoView({ behavior: 'smooth' }); + + setIsBusy(false); + } + }; + + // eslint-disable-next-line react/jsx-no-constructed-context-values + const contextValue = { + currentWeightingConfiguration, + isBusy, + isChanged, + isManual, + noticeOperations, + noticeUI, + reset, + save, + savedWeightingConfiguration, + setWeightingForPostType, + weightableFields, + }; + + /** + * Render. + */ + return {children}; +}; + +export default withNotices(WeightingProvider); + +/** + * Use the API Search context. + * + * @returns {object} API Search Context. + */ +export const useWeighting = () => { + return useContext(Context); +}; diff --git a/assets/js/weighting/utilities.js b/assets/js/weighting/utilities.js deleted file mode 100644 index 364fbdb0df..0000000000 --- a/assets/js/weighting/utilities.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Get a list of meta fields from the weighting configuration. - * - * @param {object} weightingConfiguration Weighting configuration. - * @returns {Array} Meta fields. - */ -export const getMetaFieldsFromWeightingConfiguration = (weightingConfiguration) => { - const fields = Object.values(weightingConfiguration) - .map((v) => Object.keys(v)) - .flat(); - - return fields.filter((f) => f.startsWith('meta.')); -}; - -/** - * Is a field synced by a feature? - * - * @param {string} field Field name. - * @param {object} weightableFields Weightable fields. - * @returns {boolean} If the field is synced by a feature. - */ -export const isFieldSyncedByFeature = (field, weightableFields) => { - return Object.values(weightableFields) - .map((v) => Object.values(v.groups)) - .flat() - .map((g) => Object.values(g.children)) - .flat() - .some((c) => c.key === field && c.used_by_feature === true); -}; diff --git a/includes/classes/Feature/Search/Weighting.php b/includes/classes/Feature/Search/Weighting.php index 23502ea074..17181b663a 100644 --- a/includes/classes/Feature/Search/Weighting.php +++ b/includes/classes/Feature/Search/Weighting.php @@ -122,12 +122,12 @@ public function get_weightable_fields_for_post_type( $post_type ) { continue; } - $used_by_feature = in_array( $meta_key, $allowed_protected_keys, true ); + $required = in_array( $meta_key, $allowed_protected_keys, true ); $fields['ep_metadata']['children'][ $key ] = [ - 'key' => $key, - 'label' => $meta_key, - 'used_by_feature' => true, + 'key' => $key, + 'label' => $meta_key, + 'required' => $required, ]; } @@ -145,7 +145,7 @@ public function get_weightable_fields_for_post_type( $post_type ) { /** * Get weightable fields for all searchable post types. * - * @since 4.4.0 + * @since 5.0.0 * @return array */ public function get_weightable_fields() { @@ -156,9 +156,32 @@ public function get_weightable_fields() { $post_type_object = get_post_type_object( $post_type ); $post_type_labels = get_post_type_labels( $post_type_object ); - $weightable[ $post_type ] = [ + $weightable_fields = $this->get_weightable_fields_for_post_type( $post_type ); + + $groups = []; + $fields = []; + + foreach ( $weightable_fields as $group => $weightable_field ) { + $groups[] = [ + 'key' => $group, + 'label' => $weightable_field['label'], + ]; + + foreach ( $weightable_field['children'] as $field ) { + $fields[] = [ + 'group' => $group, + 'key' => $field['key'], + 'label' => $field['label'], + 'required' => isset( $field['required'] ) ? $field['required'] : false, + ]; + } + } + + $weightable[] = [ + 'key' => $post_type, 'label' => $post_type_labels->menu_name, - 'groups' => $this->get_weightable_fields_for_post_type( $post_type ), + 'groups' => $groups, + 'fields' => $fields, ]; } diff --git a/includes/classes/Feature/WooCommerce/Products.php b/includes/classes/Feature/WooCommerce/Products.php index b5b08217fa..ed501ebd60 100644 --- a/includes/classes/Feature/WooCommerce/Products.php +++ b/includes/classes/Feature/WooCommerce/Products.php @@ -285,6 +285,8 @@ public function add_product_attributes_to_weighting( $fields, $post_type ) { $sku_key = 'meta._sku.value'; + unset( $fields['ep_metadata']['children'][ $sku_key ] ); + $fields['attributes']['children'][ $sku_key ] = array( 'key' => $sku_key, 'label' => __( 'SKU', 'elasticpress' ), @@ -292,6 +294,8 @@ public function add_product_attributes_to_weighting( $fields, $post_type ) { $variations_skus_key = 'meta._variations_skus.value'; + unset( $fields['ep_metadata']['children'][ $variations_skus_key ] ); + $fields['attributes']['children'][ $variations_skus_key ] = array( 'key' => $variations_skus_key, 'label' => __( 'Variations SKUs', 'elasticpress' ), diff --git a/includes/dashboard.php b/includes/dashboard.php index da3c5f69b2..66c0c07018 100644 --- a/includes/dashboard.php +++ b/includes/dashboard.php @@ -510,7 +510,6 @@ function action_admin_enqueue_dashboard_scripts() { $api_url = esc_url_raw( rest_url( 'elasticpress/v1/weighting' ) ); $meta_mode = $weighting->get_meta_mode(); - $sync_url = admin_url( 'admin.php?page=elasticpress-sync&do_sync' ); $weightable_fields = $weighting->get_weightable_fields(); $weighting_configuration = $weighting->get_weighting_configuration_with_defaults(); @@ -520,7 +519,6 @@ function action_admin_enqueue_dashboard_scripts() { array( 'apiUrl' => $api_url, 'metaMode' => $meta_mode, - 'syncUrl' => $sync_url, 'weightableFields' => $weightable_fields, 'weightingConfiguration' => $weighting_configuration, ) From c7f454089ba2c8961384d3598ce6f5dda6f1a180 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 12 Sep 2023 21:11:42 +1000 Subject: [PATCH 38/63] Update package-lock.json. --- package-lock.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/package-lock.json b/package-lock.json index e355ff4b93..0096cb1acb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18390,6 +18390,15 @@ "ms": "^2.1.1" } }, + "node_modules/simple-bin-help/node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -33959,6 +33968,12 @@ "requires": { "ms": "^2.1.1" } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true } } }, From 0e56b999f62bd2df18b95918b9500d5a85d97bd4 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 12 Sep 2023 21:25:16 +1000 Subject: [PATCH 39/63] Rearrange for cleaner diff. --- includes/classes/Feature/Search/Weighting.php | 70 ++++++++++--------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/includes/classes/Feature/Search/Weighting.php b/includes/classes/Feature/Search/Weighting.php index 17181b663a..b1da572c97 100644 --- a/includes/classes/Feature/Search/Weighting.php +++ b/includes/classes/Feature/Search/Weighting.php @@ -356,10 +356,37 @@ public function render_settings_section( $post_type, $field, $current_values ) { _doing_it_wrong( __METHOD__, esc_html( 'Weighting sections display are now handled via React components.' ), - 'ElasticPress 4.4.0' + 'ElasticPress 5.0.0' ); } + /** + * DEPRECATED. Handles processing the new weighting values and saving them + * to the elasticpress.io service. + */ + public function handle_save() { + _doing_it_wrong( + __METHOD__, + esc_html( 'Weighting settings are now updated using the REST API.' ), + 'ElasticPress 5.0.0' + ); + + if ( ! isset( $_POST['ep-weighting-nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['ep-weighting-nonce'] ), 'save-weighting' ) ) { + return; + } + + if ( ! current_user_can( Utils\get_capability() ) ) { + return; + } + + $this->save_weighting_configuration( $_POST ); + + $redirect_url = admin_url( 'admin.php?page=elasticpress-weighting' ); + $redirect_url = add_query_arg( 'settings-updated', true, $redirect_url ); + + $this->redirect( $redirect_url ); + } + /** * DEPRECATED. We need this method to test handle_save properly. * @@ -368,9 +395,14 @@ public function render_settings_section( $post_type, $field, $current_values ) { protected function redirect( $redirect_url ) { _doing_it_wrong( __METHOD__, - esc_html( 'Weighting sections display are now handled via React components.' ), - 'ElasticPress 4.4.0' + esc_html( 'Weighting settings are now updated using the REST API, and do not redirect server-side.' ), + 'ElasticPress 5.0.0' ); + + // @codeCoverageIgnoreStart + wp_safe_redirect( $redirect_url ); + exit(); + // @codeCoverageIgnoreEnd } /** @@ -385,7 +417,7 @@ public function save_weighting_configuration( $settings ) { _doing_it_wrong( __METHOD__, esc_html( 'Weighting sections display are now handled via React components.' ), - 'ElasticPress 4.4.0' + 'ElasticPress 5.0.0' ); $new_config = array(); @@ -455,7 +487,7 @@ public function register_rest_routes() { 'elasticpress/v1', 'weighting', [ - 'callback' => [ $this, 'handle_save' ], + 'callback' => [ $this, 'update_weighting' ], 'methods' => 'POST', 'permission_callback' => function() { return current_user_can( Utils\get_capability() ); @@ -471,12 +503,7 @@ public function register_rest_routes() { * @param \WP_Rest_Request $request REST API request. * @return void */ - public function handle_save( $request = null ) { - if ( ! $request ) { - $this->deprecated_handle_save(); - return; - } - + public function update_weighting( $request = null ) { $meta_mode = $this->get_meta_mode(); $weighting = $request->get_json_params(); @@ -810,25 +837,4 @@ public function adjust_weight_for_cross_fields( $weighted_field, $field, $weight } return $weighted_field; } - - /** - * Old function that handled processing weighting values and saving them to the elasticpress.io service. - */ - final protected function deprecated_handle_save() { - _doing_it_wrong( - __METHOD__, - esc_html( 'Weighting sections display are now handled via React components.' ), - 'ElasticPress 4.4.0' - ); - - if ( ! isset( $_POST['ep-weighting-nonce'] ) || ! wp_verify_nonce( $_POST['ep-weighting-nonce'], 'save-weighting' ) ) { - return; - } - - if ( ! current_user_can( 'manage_options' ) ) { - return; - } - - $this->save_weighting_configuration( $_POST ); - } } From a8b3bdb3b89127d176111a9a701888a21a837c56 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 12 Sep 2023 22:37:53 +1000 Subject: [PATCH 40/63] Update validation of custom fields. --- includes/classes/Feature/Search/Weighting.php | 95 +++---------------- 1 file changed, 14 insertions(+), 81 deletions(-) diff --git a/includes/classes/Feature/Search/Weighting.php b/includes/classes/Feature/Search/Weighting.php index b1da572c97..a86c88cf82 100644 --- a/includes/classes/Feature/Search/Weighting.php +++ b/includes/classes/Feature/Search/Weighting.php @@ -370,21 +370,6 @@ public function handle_save() { esc_html( 'Weighting settings are now updated using the REST API.' ), 'ElasticPress 5.0.0' ); - - if ( ! isset( $_POST['ep-weighting-nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['ep-weighting-nonce'] ), 'save-weighting' ) ) { - return; - } - - if ( ! current_user_can( Utils\get_capability() ) ) { - return; - } - - $this->save_weighting_configuration( $_POST ); - - $redirect_url = admin_url( 'admin.php?page=elasticpress-weighting' ); - $redirect_url = add_query_arg( 'settings-updated', true, $redirect_url ); - - $this->redirect( $redirect_url ); } /** @@ -398,19 +383,14 @@ protected function redirect( $redirect_url ) { esc_html( 'Weighting settings are now updated using the REST API, and do not redirect server-side.' ), 'ElasticPress 5.0.0' ); - - // @codeCoverageIgnoreStart - wp_safe_redirect( $redirect_url ); - exit(); - // @codeCoverageIgnoreEnd } /** - * DEPRECATED. Save weighting configuration for each searchable post_type + * DEPRECATED. Save weighting configuration for each searchable post_type. * * @param array $settings weighting settings * - * @return array final settings + * @return void * @since 3.4.1 */ public function save_weighting_configuration( $settings ) { @@ -419,61 +399,6 @@ public function save_weighting_configuration( $settings ) { esc_html( 'Weighting sections display are now handled via React components.' ), 'ElasticPress 5.0.0' ); - - $new_config = array(); - $previous_config_formatted = array(); - $current_config = $this->get_weighting_configuration(); - - foreach ( $current_config as $post_type => $post_type_weighting ) { - // This also ensures the string is safe, since this would return false otherwise - if ( ! post_type_exists( $post_type ) ) { - continue; - } - - // We need a way to know if fields have been explicitly set before, let's compare a previous state against $_POST['weighting'] - foreach ( $post_type_weighting as $weighting_field => $weighting_values ) { - $previous_config_formatted[ $post_type ][ sanitize_text_field( $weighting_field ) ] = [ - 'weight' => isset( $settings['weighting'][ $post_type ][ $weighting_field ]['weight'] ) ? intval( $settings['weighting'][ $post_type ][ $weighting_field ]['weight'] ) : 0, - 'enabled' => isset( $settings['weighting'][ $post_type ][ $weighting_field ]['enabled'] ) && 'on' === $settings['weighting'][ $post_type ][ $weighting_field ]['enabled'] ? true : false, - ]; - } - } - - $search = Features::factory()->get_registered_feature( 'search' ); - $post_types = $search->get_searchable_post_types(); - - foreach ( $post_types as $post_type ) { - // This also ensures the string is safe, since this would return false otherwise - if ( ! post_type_exists( $post_type ) ) { - continue; - } - - /** override default post_type settings while saving */ - $new_config[ $post_type ] = array(); - - if ( isset( $settings['weighting'][ $post_type ] ) ) { - foreach ( $settings['weighting'][ $post_type ] as $weighting_field => $weighting_values ) { - $new_config[ $post_type ][ sanitize_text_field( $weighting_field ) ] = [ - 'weight' => isset( $weighting_values['weight'] ) ? intval( $weighting_values['weight'] ) : 0, - 'enabled' => isset( $weighting_values['enabled'] ) && 'on' === $weighting_values['enabled'] ? true : false, - ]; - } - } - } - - $final_config = array_replace_recursive( $previous_config_formatted, $new_config ); - - update_option( 'elasticpress_weighting', $final_config ); - - /** - * Fires right after the weighting configuration is saved. - * - * @since 3.5.x - * @hook ep_saved_weighting_configuration - */ - do_action( 'ep_saved_weighting_configuration' ); - - return $final_config; } /** @@ -508,16 +433,24 @@ public function update_weighting( $request = null ) { $weighting = $request->get_json_params(); /** - * If metadata is not being managed manually, remove any metadata - * fields that do not belong to the metadata group from the weighting - * configuration. + * If metadata is not being managed manually, remove any custom + * fields that have not been registered as weightable fields. */ if ( 'manual' !== $meta_mode ) { foreach ( $weighting as $post_type => $fields ) { $weightable_fields = $this->get_weightable_fields_for_post_type( $post_type ); foreach ( $fields as $key => $value ) { - if ( isset( $weightable_fields['ep_metadata']['children'][ $key ] ) ) { + $is_weightable = false; + + foreach ( $weightable_fields as $group ) { + if ( isset( $group['children'][ $key ] ) ) { + $is_weightable = true; + continue; + } + } + + if ( ! $is_weightable ) { unset( $weighting[ $post_type ][ $key ] ); } } From 0661a10397f064984d1363eeb9910c809ae376b8 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 12 Sep 2023 22:43:04 +1000 Subject: [PATCH 41/63] Update @since versions. --- includes/classes/Feature/Search/Weighting.php | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/includes/classes/Feature/Search/Weighting.php b/includes/classes/Feature/Search/Weighting.php index a86c88cf82..ef412c020b 100644 --- a/includes/classes/Feature/Search/Weighting.php +++ b/includes/classes/Feature/Search/Weighting.php @@ -37,10 +37,9 @@ public function setup() { } add_action( 'admin_menu', [ $this, 'add_weighting_submenu_page' ], 15 ); - add_action( 'admin_post_ep-weighting', [ $this, 'handle_save' ] ); - add_action( 'rest_api_init', [ $this, 'register_rest_routes' ] ); add_filter( 'ep_formatted_args', [ $this, 'do_weighting' ], 20, 2 ); // After date decay, etc are injected add_filter( 'ep_query_weighting_fields', [ $this, 'adjust_weight_for_cross_fields' ], 10, 5 ); + add_action( 'rest_api_init', [ $this, 'register_rest_routes' ] ); } /** @@ -274,7 +273,7 @@ public function get_weighting_configuration() { * missing fields. * * @return array Current weighting configuration with defaults. - * @since 4.4.0 + * @since 5.0.0 */ public function get_weighting_configuration_with_defaults() { $search = Features::factory()->get_registered_feature( 'search' ); @@ -346,11 +345,12 @@ public function render_settings_page() { } /** - * DEPRECATED. Recursively renders each settings section and its children. + * Recursively renders each settings section and its children. * * @param string $post_type Current post type we're rendering * @param array $field Current field to render * @param array $current_values Current stored weighting values + * @deprecated */ public function render_settings_section( $post_type, $field, $current_values ) { _doing_it_wrong( @@ -361,8 +361,10 @@ public function render_settings_section( $post_type, $field, $current_values ) { } /** - * DEPRECATED. Handles processing the new weighting values and saving them + * Handles processing the new weighting values and saving them * to the elasticpress.io service. + * + * @deprecated */ public function handle_save() { _doing_it_wrong( @@ -373,9 +375,10 @@ public function handle_save() { } /** - * DEPRECATED. We need this method to test handle_save properly. + * We need this method to test handle_save properly. * * @param string $redirect_url Redirect URL. + * @deprecated */ protected function redirect( $redirect_url ) { _doing_it_wrong( @@ -386,12 +389,12 @@ protected function redirect( $redirect_url ) { } /** - * DEPRECATED. Save weighting configuration for each searchable post_type. + * Save weighting configuration for each searchable post_type. * * @param array $settings weighting settings - * * @return void * @since 3.4.1 + * @deprecated */ public function save_weighting_configuration( $settings ) { _doing_it_wrong( @@ -405,7 +408,7 @@ public function save_weighting_configuration( $settings ) { * Register REST routes. * * @return void - * @since 4.4.0 + * @since 5.0.0 */ public function register_rest_routes() { register_rest_route( @@ -427,6 +430,7 @@ public function register_rest_routes() { * * @param \WP_Rest_Request $request REST API request. * @return void + * @since 5.0.0 */ public function update_weighting( $request = null ) { $meta_mode = $this->get_meta_mode(); From 3c5c48a71d73ba7ce9f4965345fc041074bf8cb7 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 12 Sep 2023 22:50:56 +1000 Subject: [PATCH 42/63] Fix PHPCS issues. Remove redundant test. --- tests/php/indexables/TestPost.php | 50 ++++--------------------------- 1 file changed, 6 insertions(+), 44 deletions(-) diff --git a/tests/php/indexables/TestPost.php b/tests/php/indexables/TestPost.php index d70fe898c7..2b1e2b74de 100644 --- a/tests/php/indexables/TestPost.php +++ b/tests/php/indexables/TestPost.php @@ -3878,21 +3878,24 @@ public function testPrepareMetaManual() { // Set default weighting $weighting_default = $weighting->get_weighting_configuration_with_defaults(); + $set_default_weighting = function() use ( $weighting_default ) { return $weighting_default; }; + add_filter( 'ep_weighting_configuration', $set_default_weighting ); $post_id = $this->ep_factory->post->create( [ 'meta_input' => [ - 'test_meta_1' => 'value 1', - 'test_meta_2' => 'value 2', + 'test_meta_1' => 'value 1', + 'test_meta_2' => 'value 2', '_test_private_meta_1' => 'private value 1', '_test_private_meta_2' => 'private value 2', - ] + ], ] ); + $post = get_post( $post_id ); $prepared_meta = ElasticPress\Indexables::factory()->get( 'post' )->prepare_meta( $post ); @@ -8088,47 +8091,6 @@ public function testGetAllDistinctValues() { } /** - * Tests get_distinct_meta_field_keys_db - * - * @return void - * @group post - */ - public function testGetDistinctMetaFieldKeysDb() { - global $wpdb; - - $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); - - $meta_keys = $wpdb->get_col( "SELECT DISTINCT meta_key FROM {$wpdb->postmeta} ORDER BY meta_key" ); - $this->assertSame( $meta_keys, $indexable->get_distinct_meta_field_keys_db() ); - - /** - * Test the `ep_post_pre_meta_keys_db` filter - */ - $return_custom_array = function() { - return [ 'totally_custom_key' ]; - }; - add_filter( 'ep_post_pre_meta_keys_db', $return_custom_array ); - - $num_queries = $wpdb->num_queries; - $this->assertGreaterThan( 0, $num_queries ); - - $this->assertSame( [ 'totally_custom_key' ], $indexable->get_distinct_meta_field_keys_db() ); - $this->assertSame( $num_queries, $wpdb->num_queries ); - - remove_filter( 'ep_post_pre_meta_keys_db', $return_custom_array ); - - /** - * Test the `ep_post_pre_meta_keys_db` filter - */ - $return_custom_array = function( $meta_keys ) { - return array_merge( $meta_keys, [ 'custom_key' ] ); - }; - add_filter( 'ep_post_meta_keys_db', $return_custom_array ); - - $this->assertSame( array_merge( $meta_keys, [ 'custom_key' ] ), $indexable->get_distinct_meta_field_keys_db() ); - } - - /* * Tests search term wrapped in html tags. */ public function testHighlightTags() { From 38aecdb55e58a6b319eeaada831bccea58c98808 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 12 Sep 2023 23:07:24 +1000 Subject: [PATCH 43/63] Update custom field test. --- tests/cypress/integration/features/search/weighting.cy.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/cypress/integration/features/search/weighting.cy.js b/tests/cypress/integration/features/search/weighting.cy.js index 68b127aa19..bf5d34af97 100644 --- a/tests/cypress/integration/features/search/weighting.cy.js +++ b/tests/cypress/integration/features/search/weighting.cy.js @@ -225,11 +225,9 @@ describe('Post Search Feature - Weighting Functionality', () => { .closest('fieldset') .find('input[type="number"]') .clearThenType(100); - cy.get('@panel') - .find('select') - .should('contain', '_my_custom_field') - .select('_my_custom_field'); - cy.get('@panel').find('button').contains('Add field').click(); + cy.get('@panel').find('button').contains('Metadata').click(); + cy.get('@panel').find('input[type="text"]').clearThenType('_my_custom_field'); + cy.get('@panel').find('button').contains('Add').click(); cy.get('@panel') .find('fieldset') .contains('_my_custom_field') From 5ce47263d3b6e5d486cfaac5ee557cf2cd360f2d Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Wed, 13 Sep 2023 02:15:56 +1000 Subject: [PATCH 44/63] Only show custom fields for relevant post types. --- includes/classes/Feature/Search/Weighting.php | 4 ++++ includes/classes/Feature/WooCommerce/Orders.php | 11 ++++++++--- includes/classes/Feature/WooCommerce/Products.php | 11 ++++++++--- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/includes/classes/Feature/Search/Weighting.php b/includes/classes/Feature/Search/Weighting.php index ef412c020b..039d2517a9 100644 --- a/includes/classes/Feature/Search/Weighting.php +++ b/includes/classes/Feature/Search/Weighting.php @@ -108,9 +108,13 @@ public function get_weightable_fields_for_post_type( $post_type ) { $empty_post = new \WP_Post( new \stdClass() ); + $empty_post->post_type = $post_type; + /** This filter is documented in includes/classes/Indexable/Post/Post.php */ $allowed_protected_keys = apply_filters( 'ep_prepare_meta_allowed_protected_keys', [], $empty_post ); + sort( $allowed_protected_keys, SORT_STRING ); + /** This filter is documented in includes/classes/Indexable/Post/Post.php */ $excluded_public_keys = apply_filters( 'ep_prepare_meta_excluded_public_keys', [], $empty_post ); diff --git a/includes/classes/Feature/WooCommerce/Orders.php b/includes/classes/Feature/WooCommerce/Orders.php index e44ca2a249..f3fcad1b4e 100644 --- a/includes/classes/Feature/WooCommerce/Orders.php +++ b/includes/classes/Feature/WooCommerce/Orders.php @@ -39,7 +39,7 @@ public function __construct( WooCommerce $woocommerce ) { */ public function setup() { add_filter( 'ep_sync_insert_permissions_bypass', [ $this, 'bypass_order_permissions_check' ], 10, 2 ); - add_filter( 'ep_prepare_meta_allowed_protected_keys', [ $this, 'allow_meta_keys' ] ); + add_filter( 'ep_prepare_meta_allowed_protected_keys', [ $this, 'allow_meta_keys' ], 10, 2 ); add_filter( 'ep_post_sync_args_post_prepare_meta', [ $this, 'add_order_items_search' ], 20, 2 ); add_filter( 'ep_pc_skip_post_content_cleanup', [ $this, 'keep_order_fields' ], 20, 2 ); add_action( 'parse_query', [ $this, 'maybe_hook_woocommerce_search_fields' ], 1 ); @@ -86,10 +86,15 @@ public function get_admin_searchable_post_types() { /** * Index WooCommerce orders meta fields * - * @param array $meta Existing post meta + * @param array $meta Existing post meta + * @param \WP_Post $post Post object. * @return array */ - public function allow_meta_keys( $meta ) { + public function allow_meta_keys( $meta, $post ) { + if ( ! in_array( $post->post_type, [ 'shop_order', 'shop_order_refund' ], true ) ) { + return $meta; + } + return array_unique( array_merge( $meta, diff --git a/includes/classes/Feature/WooCommerce/Products.php b/includes/classes/Feature/WooCommerce/Products.php index ed501ebd60..45e92f0939 100644 --- a/includes/classes/Feature/WooCommerce/Products.php +++ b/includes/classes/Feature/WooCommerce/Products.php @@ -41,7 +41,7 @@ public function __construct( WooCommerce $woocommerce ) { */ public function setup() { add_action( 'ep_formatted_args', [ $this, 'price_filter' ], 10, 3 ); - add_filter( 'ep_prepare_meta_allowed_protected_keys', [ $this, 'allow_meta_keys' ] ); + add_filter( 'ep_prepare_meta_allowed_protected_keys', [ $this, 'allow_meta_keys' ], 10, 2 ); add_filter( 'ep_sync_taxonomies', [ $this, 'sync_taxonomies' ] ); add_filter( 'ep_term_suggest_post_type', [ $this, 'suggest_wc_add_post_type' ] ); add_filter( 'ep_facet_include_taxonomies', [ $this, 'add_product_attributes' ] ); @@ -128,10 +128,15 @@ public function price_filter( $args, $query_args, $query ) { /** * Index WooCommerce products meta fields * - * @param array $meta Existing post meta + * @param array $meta Existing post meta + * @param \WP_Post $post Post object. * @return array */ - public function allow_meta_keys( $meta ) { + public function allow_meta_keys( $meta, $post ) { + if ( 'product' !== $post->post_type ) { + return $meta; + } + return array_unique( array_merge( $meta, From aee10b27bf779faf89ca56e5d5be9cf05f9b288f Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Wed, 13 Sep 2023 02:16:10 +1000 Subject: [PATCH 45/63] Remove undo functionality. --- assets/js/weighting/components/actions.js | 21 ++------ .../components/common/delete-button.js | 25 ---------- .../components/common/undo-button.js | 21 -------- assets/js/weighting/components/field.js | 49 +++---------------- assets/js/weighting/provider.js | 29 +---------- package.json | 1 - 6 files changed, 12 insertions(+), 134 deletions(-) delete mode 100644 assets/js/weighting/components/common/delete-button.js delete mode 100644 assets/js/weighting/components/common/undo-button.js diff --git a/assets/js/weighting/components/actions.js b/assets/js/weighting/components/actions.js index ad3ac95329..32979d0778 100644 --- a/assets/js/weighting/components/actions.js +++ b/assets/js/weighting/components/actions.js @@ -4,7 +4,6 @@ import { Button } from '@wordpress/components'; import { WPElement } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; -import { undo } from '@wordpress/icons'; import { useWeighting } from '../provider'; /** @@ -13,26 +12,14 @@ import { useWeighting } from '../provider'; * @returns {WPElement} Component element. */ export default () => { - const { isBusy, isChanged, reset } = useWeighting(); + const { isBusy } = useWeighting(); /** * Render. */ return ( - <> - -   - - + ); }; diff --git a/assets/js/weighting/components/common/delete-button.js b/assets/js/weighting/components/common/delete-button.js deleted file mode 100644 index 528ec2dfd8..0000000000 --- a/assets/js/weighting/components/common/delete-button.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * WordPress dependencies. - */ -import { Button } from '@wordpress/components'; -import { WPElement } from '@wordpress/element'; -import { trash } from '@wordpress/icons'; - -/** - * Undo button component. - * - * @param {object} props Component props. - * @returns {WPElement} Component element. - */ -export default (props) => { - /** - * Render. - */ - return ( -
-
- - + removeNotice(notice)} /> diff --git a/assets/js/weighting/components/actions.js b/assets/js/weighting/components/actions.js deleted file mode 100644 index 32979d0778..0000000000 --- a/assets/js/weighting/components/actions.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * WordPress dependencies. - */ -import { Button } from '@wordpress/components'; -import { WPElement } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; -import { useWeighting } from '../provider'; - -/** - * Actions component. - * - * @returns {WPElement} Component element. - */ -export default () => { - const { isBusy } = useWeighting(); - - /** - * Render. - */ - return ( - - ); -}; diff --git a/assets/js/weighting/components/post-type.js b/assets/js/weighting/components/post-type.js index c0f0471e77..b1e7b16931 100644 --- a/assets/js/weighting/components/post-type.js +++ b/assets/js/weighting/components/post-type.js @@ -28,12 +28,7 @@ export default ({ postType }) => { return ( -
-
-

{label}

-
-
-
+

{label}

{groups.map(({ key, label }) => { const isMetadata = key === 'ep_metadata'; From b29937ba38a432cc7b6a95d11f7fddab6c743b32 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Thu, 28 Sep 2023 08:37:47 +1000 Subject: [PATCH 49/63] Implement SettingsScreenProvider for weighting settings. --- assets/css/weighting.css | 21 ------ assets/css/weighting/post-type.css | 6 -- assets/js/sync-ui/index.js | 30 ++++---- assets/js/weighting/apps/settings-page.js | 71 ------------------- assets/js/weighting/apps/weighting.js | 64 +++++++++++++++++ assets/js/weighting/components/group.js | 10 ++- assets/js/weighting/components/post-type.js | 4 +- .../weighting => js/weighting/css}/action.css | 0 .../weighting/css}/add-new.css | 0 .../weighting => js/weighting/css}/field.css | 0 .../weighting/css/post-type.css} | 10 ++- assets/js/weighting/index.js | 31 +++++--- assets/js/weighting/provider.js | 28 +++----- assets/js/weighting/style.css | 4 ++ includes/dashboard.php | 7 +- package.json | 1 - 16 files changed, 129 insertions(+), 158 deletions(-) delete mode 100644 assets/css/weighting.css delete mode 100644 assets/css/weighting/post-type.css delete mode 100644 assets/js/weighting/apps/settings-page.js create mode 100644 assets/js/weighting/apps/weighting.js rename assets/{css/weighting => js/weighting/css}/action.css (100%) rename assets/{css/weighting => js/weighting/css}/add-new.css (100%) rename assets/{css/weighting => js/weighting/css}/field.css (100%) rename assets/{css/weighting/screen.css => js/weighting/css/post-type.css} (70%) create mode 100644 assets/js/weighting/style.css diff --git a/assets/css/weighting.css b/assets/css/weighting.css deleted file mode 100644 index a37f356d0a..0000000000 --- a/assets/css/weighting.css +++ /dev/null @@ -1,21 +0,0 @@ -@import "weighting/action.css"; -@import "weighting/add-new.css"; -@import "weighting/field.css"; -@import "weighting/post-type.css"; -@import "weighting/screen.css"; - -html.wp-toolbar { - background: transparent; -} - -.components-snackbar-list { - bottom: 40px; - left: 0; - padding: 0 16px; - position: fixed; - - @media (min-width: 600px) { - left: auto; - padding: 0; - } -} diff --git a/assets/css/weighting/post-type.css b/assets/css/weighting/post-type.css deleted file mode 100644 index 5a4a19f8d5..0000000000 --- a/assets/css/weighting/post-type.css +++ /dev/null @@ -1,6 +0,0 @@ -.ep-weighting-post-type { - - & .components-panel__header h2 { - font-size: 1rem; - } -} diff --git a/assets/js/sync-ui/index.js b/assets/js/sync-ui/index.js index a88a2051c2..a162a2b9bc 100644 --- a/assets/js/sync-ui/index.js +++ b/assets/js/sync-ui/index.js @@ -34,20 +34,24 @@ import './style.css'; * @returns {WPElement} App component. */ const App = () => ( - - - + + + - - - + + + ); if (typeof createRoot === 'function') { diff --git a/assets/js/weighting/apps/settings-page.js b/assets/js/weighting/apps/settings-page.js deleted file mode 100644 index 613a4ee603..0000000000 --- a/assets/js/weighting/apps/settings-page.js +++ /dev/null @@ -1,71 +0,0 @@ -/** - * WordPress dependencies. - */ -import { Button, SnackbarList } from '@wordpress/components'; -import { useDispatch, useSelect } from '@wordpress/data'; -import { WPElement } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; -import { store as noticeStore } from '@wordpress/notices'; - -/** - * Internal Dependencies. - */ -import PostType from '../components/post-type'; -import { useWeighting } from '../provider'; - -/** - * Weighting settings app. - * - * @returns {WPElement} Element. - */ -export default () => { - const { removeNotice } = useDispatch(noticeStore); - - const { notices } = useSelect((select) => { - return { - notices: select(noticeStore).getNotices(), - }; - }, []); - - const { isBusy, save, weightableFields } = useWeighting(); - - /** - * Submit event. - * - * @param {Event} event Submit event. - */ - const onSubmit = (event) => { - event.preventDefault(); - - save(); - }; - - return ( - <> -
-

{__('Manage Search Fields & Weighting', 'elasticpress')}

-
-

- {__( - 'This dashboard enables you to select which fields ElasticPress should sync, whether to use those fields in searches, and how heavily to weight fields in the search algorithm. In general, increasing the Weight of a field will increase the relevancy score of a post that has matching text in that field.', - 'elasticpress', - )} -

-

- {__( - 'For example, adding more weight to the title attribute will cause search matches on the post title to appear more prominently.', - 'elasticpress', - )} -

-
- {weightableFields.map(({ key }) => { - return ; - })} - - - removeNotice(notice)} /> - - ); -}; diff --git a/assets/js/weighting/apps/weighting.js b/assets/js/weighting/apps/weighting.js new file mode 100644 index 0000000000..b536a08ede --- /dev/null +++ b/assets/js/weighting/apps/weighting.js @@ -0,0 +1,64 @@ +/** + * WordPress dependencies. + */ +import { Button } from '@wordpress/components'; +import { WPElement } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal Dependencies. + */ +import { useSettingsScreen } from '../../settings-screen'; +import PostType from '../components/post-type'; +import { useWeightingSettings } from '../provider'; + +/** + * Weighting settings app. + * + * @returns {WPElement} Element. + */ +export default () => { + const { createNotice } = useSettingsScreen(); + const { isBusy, save, weightableFields } = useWeightingSettings(); + + /** + * Submit event. + * + * @param {Event} event Submit event. + */ + const onSubmit = async (event) => { + event.preventDefault(); + + try { + await save(); + createNotice('success', __('Settings saved.', 'elasticpress')); + } catch (e) { + createNotice('error', __('Something went wrong. Please try again.', 'elasticpress')); + } + }; + + return ( + <> +

+ {__( + 'This dashboard enables you to select which fields ElasticPress should sync, whether to use those fields in searches, and how heavily to weight fields in the search algorithm. In general, increasing the Weight of a field will increase the relevancy score of a post that has matching text in that field.', + 'elasticpress', + )} +

+

+ {__( + 'For example, adding more weight to the title attribute will cause search matches on the post title to appear more prominently.', + 'elasticpress', + )} +

+
+ {weightableFields.map(({ key }) => { + return ; + })} + + + + ); +}; diff --git a/assets/js/weighting/components/group.js b/assets/js/weighting/components/group.js index b4a2b4649d..0d55eec505 100644 --- a/assets/js/weighting/components/group.js +++ b/assets/js/weighting/components/group.js @@ -2,16 +2,15 @@ * WordPress dependencies. */ import { Button, PanelRow, TextControl } from '@wordpress/components'; -import { useDispatch } from '@wordpress/data'; import { useMemo, useState, WPElement } from '@wordpress/element'; import { __, sprintf } from '@wordpress/i18n'; -import { store as noticeStore } from '@wordpress/notices'; /** * Internal dependencies. */ +import { useSettingsScreen } from '../../settings-screen'; +import { useWeightingSettings } from '../provider'; import Field from './field'; -import { useWeighting } from '../provider'; /** * Post type propertes component. @@ -22,10 +21,9 @@ import { useWeighting } from '../provider'; * @returns {WPElement} Component element. */ export default ({ group, postType }) => { - const { createNotice } = useDispatch(noticeStore); - + const { createNotice } = useSettingsScreen(); const { currentWeightingConfiguration, setWeightingForPostType, weightableFields } = - useWeighting(); + useWeightingSettings(); /** * State. diff --git a/assets/js/weighting/components/post-type.js b/assets/js/weighting/components/post-type.js index b1e7b16931..a5af21f6a1 100644 --- a/assets/js/weighting/components/post-type.js +++ b/assets/js/weighting/components/post-type.js @@ -7,7 +7,7 @@ import { WPElement } from '@wordpress/element'; /** * Internal dependencies. */ -import { useWeighting } from '../provider'; +import { useWeightingSettings } from '../provider'; import Group from './group'; /** @@ -18,7 +18,7 @@ import Group from './group'; * @returns {WPElement} Component element. */ export default ({ postType }) => { - const { isManual, weightableFields } = useWeighting(); + const { isManual, weightableFields } = useWeightingSettings(); const { label, groups } = weightableFields.find((f) => f.key === postType); diff --git a/assets/css/weighting/action.css b/assets/js/weighting/css/action.css similarity index 100% rename from assets/css/weighting/action.css rename to assets/js/weighting/css/action.css diff --git a/assets/css/weighting/add-new.css b/assets/js/weighting/css/add-new.css similarity index 100% rename from assets/css/weighting/add-new.css rename to assets/js/weighting/css/add-new.css diff --git a/assets/css/weighting/field.css b/assets/js/weighting/css/field.css similarity index 100% rename from assets/css/weighting/field.css rename to assets/js/weighting/css/field.css diff --git a/assets/css/weighting/screen.css b/assets/js/weighting/css/post-type.css similarity index 70% rename from assets/css/weighting/screen.css rename to assets/js/weighting/css/post-type.css index cf6e71a9c8..0b765587aa 100644 --- a/assets/css/weighting/screen.css +++ b/assets/js/weighting/css/post-type.css @@ -1,10 +1,8 @@ -.ep-weighting-screen { - margin-left: auto; - margin-right: auto; - max-width: 800px; +.ep-weighting-post-type { + margin-bottom: 16px; - & .components-panel { - margin-bottom: 1rem; + & .components-panel__header h2 { + font-size: 14px; } & .components-toggle-control__label { diff --git a/assets/js/weighting/index.js b/assets/js/weighting/index.js index 4cf62a8417..869332d50d 100644 --- a/assets/js/weighting/index.js +++ b/assets/js/weighting/index.js @@ -2,13 +2,20 @@ * WordPress dependencies. */ import { createRoot, render, WPElement } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; /** * Internal Dependencies. */ +import { SettingsScreenProvider } from '../settings-screen'; import { apiUrl, metaMode, syncUrl, weightableFields, weightingConfiguration } from './config'; -import WeightingProvider from './provider'; -import SettingsPage from './apps/settings-page'; +import { WeightingSettingsProvider } from './provider'; +import Weighting from './apps/weighting'; + +/** + * Styles. + */ +import './style.css'; /** * App component. @@ -16,15 +23,17 @@ import SettingsPage from './apps/settings-page'; * @returns {WPElement} App component. */ const App = () => ( - - - + + + + + ); if (typeof createRoot === 'function') { diff --git a/assets/js/weighting/provider.js b/assets/js/weighting/provider.js index 0862ed800b..8fb37f2473 100644 --- a/assets/js/weighting/provider.js +++ b/assets/js/weighting/provider.js @@ -2,10 +2,7 @@ * WordPress dependencies. */ import apiFetch from '@wordpress/api-fetch'; -import { useDispatch } from '@wordpress/data'; import { createContext, WPElement, useContext, useMemo, useState } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; -import { store as noticeStore } from '@wordpress/notices'; /** * Instant Results context. @@ -23,9 +20,13 @@ const Context = createContext(); * @param {object} props.weightingConfiguration Weighting configuration, indexed by post type. * @returns {WPElement} Element. */ -export default ({ apiUrl, children, metaMode, weightableFields, weightingConfiguration }) => { - const { createNotice } = useDispatch(noticeStore); - +export const WeightingSettingsProvider = ({ + apiUrl, + children, + metaMode, + weightableFields, + weightingConfiguration, +}) => { const [currentWeightingConfiguration, setCurrentWeightingConfiguration] = useState({ ...weightingConfiguration, }); @@ -57,9 +58,9 @@ export default ({ apiUrl, children, metaMode, weightableFields, weightingConfigu * @returns {void} */ const save = async () => { - try { - setIsBusy(true); + setIsBusy(true); + try { await apiFetch({ body: JSON.stringify(currentWeightingConfiguration), headers: { @@ -68,16 +69,9 @@ export default ({ apiUrl, children, metaMode, weightableFields, weightingConfigu method: 'POST', url: apiUrl, }); - - createNotice('success', __('Settings saved.', 'elasticpress'), { - isDismissible: true, - }); } catch (e) { console.error(e); // eslint-disable-line no-console - - createNotice('error', __('Something went wrong. Please try again.', 'elasticpress'), { - isDismissible: true, - }); + throw e; } finally { setIsBusy(false); } @@ -104,6 +98,6 @@ export default ({ apiUrl, children, metaMode, weightableFields, weightingConfigu * * @returns {object} API Search Context. */ -export const useWeighting = () => { +export const useWeightingSettings = () => { return useContext(Context); }; diff --git a/assets/js/weighting/style.css b/assets/js/weighting/style.css new file mode 100644 index 0000000000..8ab58bb81c --- /dev/null +++ b/assets/js/weighting/style.css @@ -0,0 +1,4 @@ +@import "./css/action.css"; +@import "./css/add-new.css"; +@import "./css/field.css"; +@import "./css/post-type.css"; diff --git a/includes/dashboard.php b/includes/dashboard.php index 99a2945c30..6c7de26ae4 100644 --- a/includes/dashboard.php +++ b/includes/dashboard.php @@ -472,13 +472,12 @@ function action_admin_enqueue_dashboard_scripts() { } if ( 'weighting' === Screen::factory()->get_current_screen() ) { - wp_enqueue_style( 'wp-edit-post' ); wp_enqueue_style( 'ep_weighting_styles', - EP_URL . 'dist/css/weighting-styles.css', - Utils\get_asset_info( 'weighting-styles', 'dependencies' ), - Utils\get_asset_info( 'weighting-styles', 'version' ) + EP_URL . 'dist/css/weighting-script.css', + [ 'wp-components', 'wp-edit-post' ], + Utils\get_asset_info( 'weighting-script', 'version' ) ); wp_enqueue_script( diff --git a/package.json b/package.json index cb5bb23f5c..8cfecdc16f 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,6 @@ "related-posts-block-styles": "./assets/css/related-posts-block.css", "status-report-styles": "./assets/css/status-report.css", "synonyms-styles": "./assets/css/synonyms.css", - "weighting-styles": "./assets/css/weighting.css", "woocommerce-order-search-styles": "./assets/css/woocommerce/admin/orders.css" }, "wpDependencyExternals": true From d3bc1bd1e93fb899ed1b25ea95788f2d15f7dd4f Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 24 Oct 2023 22:30:53 +1100 Subject: [PATCH 50/63] Add fields to weighting for Facets tests. --- assets/js/weighting/apps/weighting.js | 2 +- .../cypress/integration/features/facets.cy.js | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/assets/js/weighting/apps/weighting.js b/assets/js/weighting/apps/weighting.js index b536a08ede..000e55d372 100644 --- a/assets/js/weighting/apps/weighting.js +++ b/assets/js/weighting/apps/weighting.js @@ -56,7 +56,7 @@ export default () => { return ; })} diff --git a/tests/cypress/integration/features/facets.cy.js b/tests/cypress/integration/features/facets.cy.js index 100e178c62..2f5197629f 100644 --- a/tests/cypress/integration/features/facets.cy.js +++ b/tests/cypress/integration/features/facets.cy.js @@ -19,6 +19,31 @@ describe('Facets Feature', { tags: '@slow' }, () => { wp_delete_post( $post, true ); } `); + + cy.updateWeighting(); + + cy.visitAdminPage('admin.php?page=elasticpress-weighting'); + + cy.intercept('/wp-json/elasticpress/v1/weighting*').as('apiRequest'); + cy.contains('h2', 'Posts').closest('.components-panel').as('postsPanel'); + + cy.get('@postsPanel').contains('button', 'Metadata').click(); + + cy.get('@postsPanel').find('input[type="text"]').as('metaInput'); + cy.get('@postsPanel').contains('button', 'Add').as('metaAdd'); + + cy.get('@metaInput').clearThenType('meta_field_1'); + cy.get('@metaAdd').click(); + cy.get('@metaInput').clearThenType('meta_field_2'); + cy.get('@metaAdd').click(); + cy.get('@metaInput').clearThenType('numeric_meta_field'); + cy.get('@metaAdd').click(); + cy.get('@metaInput').clearThenType('non_numeric_meta_field'); + cy.get('@metaAdd').click(); + + cy.contains('button', 'Save changes').click(); + + cy.wait('@apiRequest'); }); /** From 5b7a6f0564c6cdcd492673532f28f1d7721857ff Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 24 Oct 2023 22:36:37 +1100 Subject: [PATCH 51/63] Update posts indexable test. --- tests/cypress/integration/indexables/post.cy.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/cypress/integration/indexables/post.cy.js b/tests/cypress/integration/indexables/post.cy.js index b30719ca75..275216b3e2 100644 --- a/tests/cypress/integration/indexables/post.cy.js +++ b/tests/cypress/integration/indexables/post.cy.js @@ -9,8 +9,17 @@ describe('Post Indexable', () => { // Make sure post categories are searchable. cy.visitAdminPage('admin.php?page=elasticpress-weighting'); - cy.get('#post-terms\\.category\\.name-enabled').check(); - cy.get('#submit').click(); + cy.intercept('/wp-json/elasticpress/v1/weighting*').as('apiRequest'); + + cy.contains('h2', 'Posts').closest('.components-panel').as('postsPanel'); + cy.get('@postsPanel') + .contains('legend', 'Categories') + .closest('fieldset') + .find('input[type="checkbox"]') + .check(); + + cy.contains('button', 'Save changes').click(); + cy.wait('@apiRequest'); cy.setPerIndexCycle(); cy.visitAdminPage('edit-tags.php?taxonomy=category'); From 72cde58e473d366ee3b27f10b285db2ecef94e49 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Tue, 24 Oct 2023 23:16:32 +1100 Subject: [PATCH 52/63] Fix button label in weighting tests. --- tests/cypress/integration/features/search/weighting.cy.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/cypress/integration/features/search/weighting.cy.js b/tests/cypress/integration/features/search/weighting.cy.js index bf5d34af97..83fd2a8f30 100644 --- a/tests/cypress/integration/features/search/weighting.cy.js +++ b/tests/cypress/integration/features/search/weighting.cy.js @@ -70,7 +70,7 @@ describe('Post Search Feature - Weighting Functionality', () => { * Save weighting settings. */ cy.intercept('/wp-json/elasticpress/v1/weighting*').as('apiRequest'); - cy.get('button').contains('Save Changes').click(); + cy.contains('button', 'Save changes').click(); cy.wait('@apiRequest'); /** @@ -146,7 +146,7 @@ describe('Post Search Feature - Weighting Functionality', () => { * Save weighting settings. */ cy.intercept('/wp-json/elasticpress/v1/weighting*').as('apiRequest'); - cy.get('button').contains('Save Changes').click(); + cy.contains('button', 'Save changes').click(); cy.wait('@apiRequest'); /** @@ -239,7 +239,7 @@ describe('Post Search Feature - Weighting Functionality', () => { * Save weighting settings. */ cy.intercept('/wp-json/elasticpress/v1/weighting*').as('apiRequest'); - cy.get('button').contains('Save Changes').click(); + cy.contains('button', 'Save changes').click(); cy.wait('@apiRequest'); /** @@ -281,7 +281,7 @@ describe('Post Search Feature - Weighting Functionality', () => { * Save weighting settings. */ cy.intercept('/wp-json/elasticpress/v1/weighting*').as('apiRequest'); - cy.get('button').contains('Save Changes').click(); + cy.contains('button', 'Save changes').click(); cy.wait('@apiRequest'); /** From 46ead924af594bdfa4795370e03cc827c36c3a9c Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Wed, 25 Oct 2023 00:51:33 +1100 Subject: [PATCH 53/63] Fix weighting test. --- tests/cypress/integration/features/search/weighting.cy.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/cypress/integration/features/search/weighting.cy.js b/tests/cypress/integration/features/search/weighting.cy.js index 83fd2a8f30..c643fe17b3 100644 --- a/tests/cypress/integration/features/search/weighting.cy.js +++ b/tests/cypress/integration/features/search/weighting.cy.js @@ -260,7 +260,6 @@ describe('Post Search Feature - Weighting Functionality', () => { * Update the weighting so the meta field is weighted higher. */ cy.visitAdminPage('admin.php?page=elasticpress-weighting'); - cy.get('.components-panel__body-title').contains('Metadata').should('exist'); cy.get('.components-panel__header') .contains('Posts') .closest('.components-panel') @@ -270,6 +269,7 @@ describe('Post Search Feature - Weighting Functionality', () => { .closest('fieldset') .find('input[type="number"]') .clearThenType(1); + cy.get('@panel').find('button').contains('Metadata').click(); cy.get('@panel') .find('fieldset') .contains('_my_custom_field') @@ -307,12 +307,12 @@ describe('Post Search Feature - Weighting Functionality', () => { cy.get('.components-panel__body-title').contains('Metadata').should('not.exist'); /** - * With automatic meta management the post with a value in a public key + * With automatic meta management the post with a value in content * should be returned, but the post with the value in a protected key * should not be. */ cy.visit('/?s=abc123'); cy.get('.entry-title').contains('Test meta weighting, post content').should('exist'); - cy.get('.entry-title').contains('Test meta weighting, post meta').should('exist'); + cy.get('.entry-title').contains('Test meta weighting, post meta').should('not.exist'); }); }); From ee931286ffbed66179f1167cc64f7d5fbcd994b7 Mon Sep 17 00:00:00 2001 From: Jacob Peattie Date: Wed, 25 Oct 2023 01:42:59 +1100 Subject: [PATCH 54/63] Add message about syncing. --- assets/js/weighting/components/group.js | 4 ++++ assets/js/weighting/css/add-new.css | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/assets/js/weighting/components/group.js b/assets/js/weighting/components/group.js index 0d55eec505..8334a7681a 100644 --- a/assets/js/weighting/components/group.js +++ b/assets/js/weighting/components/group.js @@ -173,6 +173,10 @@ export default ({ group, postType }) => { {isMetadata ? ( setToAdd(toAdd)} onKeyDown={onKeyDown} diff --git a/assets/js/weighting/css/add-new.css b/assets/js/weighting/css/add-new.css index 7a1845885e..68d468c376 100644 --- a/assets/js/weighting/css/add-new.css +++ b/assets/js/weighting/css/add-new.css @@ -1,10 +1,16 @@ .ep-weighting-add-new { - align-items: flex-end; + align-items: start; gap: 4px; justify-content: start; margin-top: 16px; + max-width: 100%; + width: 50ch; & .components-base-control .components-text-control__input { min-height: 36px; } + + & .components-button { + margin-top: calc(1.4 * 11px + 8px); + } } From a2a7398662997c624982386f9904dbda2850f3ff Mon Sep 17 00:00:00 2001 From: Felipe Elia Date: Wed, 25 Oct 2023 15:38:49 -0300 Subject: [PATCH 55/63] Adjust unit tests for the weighting dashboard --- includes/classes/Feature/Search/Weighting.php | 28 +- tests/php/features/TestWeighting.php | 248 ++++++++---------- 2 files changed, 132 insertions(+), 144 deletions(-) diff --git a/includes/classes/Feature/Search/Weighting.php b/includes/classes/Feature/Search/Weighting.php index 06d02e3376..4a7324edc1 100644 --- a/includes/classes/Feature/Search/Weighting.php +++ b/includes/classes/Feature/Search/Weighting.php @@ -429,17 +429,25 @@ public function register_rest_routes() { } /** - * Handles processing the new weighting values and saving them to the - * elasticpress.io service. + * Handles processing the new weighting values and saving them. * * @param \WP_Rest_Request $request REST API request. - * @return void + * @return array * @since 5.0.0 */ public function update_weighting( $request = null ) { $meta_mode = $this->get_meta_mode(); $weighting = $request->get_json_params(); + $post_types = Features::factory()->get_registered_feature( 'search' )->get_searchable_post_types(); + + // Add default fields for post types not sent. + foreach ( $post_types as $post_type ) { + if ( ! isset( $weighting[ $post_type ] ) ) { + $weighting[ $post_type ] = []; + } + } + /** * If metadata is not being managed manually, remove any custom * fields that have not been registered as weightable fields. @@ -465,6 +473,13 @@ public function update_weighting( $request = null ) { } } + // Cleanup any post type sent (or added via filters) that does not exist. + foreach ( $weighting as $post_type => $fields ) { + if ( ! post_type_exists( $post_type ) ) { + unset( $weighting[ $post_type ] ); + } + } + update_option( 'elasticpress_weighting', $weighting ); /** @@ -475,9 +490,10 @@ public function update_weighting( $request = null ) { */ do_action( 'ep_saved_weighting_configuration' ); - wp_send_json_success( $weighting ); - - exit; + return [ + 'data' => $weighting, + 'success' => true, + ]; } /** diff --git a/tests/php/features/TestWeighting.php b/tests/php/features/TestWeighting.php index b436b2ca32..d4aefb6b57 100644 --- a/tests/php/features/TestWeighting.php +++ b/tests/php/features/TestWeighting.php @@ -20,44 +20,42 @@ class TestWeighting extends BaseTestCase { * @var array */ public $weighting_settings = [ - 'weighting' => [ - 'post' => [ - 'post_title' => [ - 'weight' => 1, - 'enabled' => 'on', - ], - 'post_content' => [ - 'weight' => 1, - 'enabled' => 'on', - ], - 'post_excerpt' => [ - 'weight' => 1, - 'enabled' => 'on', - ], + 'post' => [ + 'post_title' => [ + 'weight' => 1, + 'enabled' => 'on', + ], + 'post_content' => [ + 'weight' => 1, + 'enabled' => 'on', + ], + 'post_excerpt' => [ + 'weight' => 1, + 'enabled' => 'on', + ], - 'author_name' => [ - 'weight' => 0, - 'enabled' => 'on', - ], + 'author_name' => [ + 'weight' => 0, + 'enabled' => 'on', + ], + ], + 'page' => [ + 'post_title' => [ + 'weight' => 1, + 'enabled' => 'on', + ], + 'post_content' => [ + 'weight' => 1, + 'enabled' => 'on', + ], + 'post_excerpt' => [ + 'weight' => 1, + 'enabled' => 'on', ], - 'page' => [ - 'post_title' => [ - 'weight' => 1, - 'enabled' => 'on', - ], - 'post_content' => [ - 'weight' => 1, - 'enabled' => 'on', - ], - 'post_excerpt' => [ - 'weight' => 1, - 'enabled' => 'on', - ], - 'author_name' => [ - 'weight' => 0, - 'enabled' => false, - ], + 'author_name' => [ + 'weight' => 0, + 'enabled' => false, ], ], ]; @@ -109,29 +107,26 @@ public function get_weighting_feature() { } /** - * Test searchable post_types exist after configuration change + * Test searchable post_types exist after configuration change with meta mode 'auto' * - * @expectedIncorrectUsage ElasticPress\Feature\Search\Weighting::save_weighting_configuration + * @group weighting + * @since 5.0.0 */ - public function testWeightablePostType() { + public function test_weightable_post_type_auto() { $search = ElasticPress\Features::factory()->get_registered_feature( 'search' ); $searchable_post_types = $search->get_searchable_post_types(); $weighting_settings = [ - 'weighting' => [ - 'post' => [ - 'post_title' => [ - 'enabled' => 'on', - 'weight' => 1, - ], + 'post' => [ + 'post_title' => [ + 'enabled' => 'on', + 'weight' => 1, ], ], ]; - $this->get_weighting_feature()->save_weighting_configuration( $weighting_settings ); - - $weighting_configuration = $this->get_weighting_feature()->get_weighting_configuration(); + $weighting_configuration = $this->save_weighting_configuration( $weighting_settings ); $this->assertEquals( count( $searchable_post_types ), count( array_keys( $weighting_configuration ) ) ); @@ -141,10 +136,21 @@ public function testWeightablePostType() { /** * Test settings toggle * + * @since 5.0.0 + * @group weighting * @expectedIncorrectUsage ElasticPress\Feature\Search\Weighting::save_weighting_configuration */ - public function testWeightingConfiguration() { + public function test_weighting_configuration_deprecated() { + $this->get_weighting_feature()->save_weighting_configuration( [] ); + } + /** + * Test saving weighting configuration + * + * @since 5.0.0 + * @group weighting + */ + public function test_weighting_configuration() { $weighting_ep_test = $this->get_weighting_feature()->get_post_type_default_settings( 'ep_test' ); $this->assertEquals( true, $weighting_ep_test['post_title']['enabled'] ); @@ -152,26 +158,24 @@ public function testWeightingConfiguration() { $this->assertEmpty( $weighting_configuration ); $weighting_settings = [ - 'weighting' => [ - 'post' => [ - 'post_title' => [ - 'enabled' => 'on', - 'weight' => 1, - ], + 'post' => [ + 'post_title' => [ + 'enabled' => true, + 'weight' => 1, ], ], ]; // enable post_title weighting - $this->get_weighting_feature()->save_weighting_configuration( $weighting_settings ); - $weighting_configuration = $this->get_weighting_feature()->get_weighting_configuration(); + + $weighting_configuration = $this->save_weighting_configuration( $weighting_settings ); $this->assertEquals( true, $weighting_configuration['post']['post_title']['enabled'] ); $this->assertEquals( 1, $weighting_configuration['post']['post_title']['weight'] ); // disable post_title weighting - $weighting_settings['weighting']['post']['post_title']['enabled'] = ''; - $this->get_weighting_feature()->save_weighting_configuration( $weighting_settings ); - $weighting_configuration = $this->get_weighting_feature()->get_weighting_configuration(); + $weighting_settings['post']['post_title']['enabled'] = false; + + $weighting_configuration = $this->save_weighting_configuration( $weighting_settings ); $this->assertEquals( false, $weighting_configuration['post']['post_title']['enabled'] ); } @@ -183,7 +187,6 @@ public function testWeightingConfiguration() { * * @since 3.6.5 * @group weighting - * @expectedIncorrectUsage ElasticPress\Feature\Search\Weighting::save_weighting_configuration */ public function testWeightingDefaultEnabledTaxonomies() { // By default, `post_format` should not be enabled, only `category` and `post_tag`. @@ -208,8 +211,7 @@ function ( $taxs, $post_type ) { $this->assertTrue( $post_default_config['terms.post_format.name']['enabled'] ); // `$this->weighting_settings` does not have post_format. So, once saved, the configuration should not have it enabled too. - $this->get_weighting_feature()->save_weighting_configuration( $this->weighting_settings ); - $weighting_configuration = $this->get_weighting_feature()->get_weighting_configuration(); + $weighting_configuration = $this->save_weighting_configuration( $this->weighting_settings ); $this->assertArrayNotHasKey( 'post_format', $weighting_configuration['post'] ); $this->assertArrayNotHasKey( 'terms.post_format.name', $weighting_configuration['post'] ); } @@ -255,80 +257,35 @@ public function testRenderSettingsPage() { $this->assertStringContainsString( 'id="ep-weighting-screen"', $content ); } - /** - * Test the `render_settings_page` method (success) - */ - public function testRenderSettingsPageSaveSuccess() { - $_GET['settings-updated'] = true; - ob_start(); - $this->get_weighting_feature()->render_settings_page(); - $content = ob_get_clean(); - - $this->assertStringContainsString( 'Changes Saved', $content ); - } - - /** - * Test the `render_settings_page` method (failed) - */ - public function testRenderSettingsPageSaveFailed() { - $_GET['settings-updated'] = false; - ob_start(); - $this->get_weighting_feature()->render_settings_page(); - $content = ob_get_clean(); - - $this->assertStringContainsString( 'An error occurred when saving', $content ); - } - /** * Test the `handle_save` method * - * @expectedIncorrectUsage ElasticPress\Feature\Search\Weighting::deprecated_handle_save - * @expectedIncorrectUsage ElasticPress\Feature\Search\Weighting::save_weighting_configuration + * @since 5.0.0 + * @group weighting + * @expectedIncorrectUsage ElasticPress\Feature\Search\Weighting::handle_save */ - public function testHandleSave() { - $weighting_class = $this->getMockBuilder( 'ElasticPress\Feature\Search\Weighting' ) - ->setMethods( [ 'redirect' ] ) - ->getMock(); - - $_POST['ep-weighting-nonce'] = false; - $this->assertEquals( null, $weighting_class->handle_save() ); - - // Change to non admin user - wp_set_current_user( $this->factory->user->create( array( 'role' => 'author' ) ) ); - - $_POST['ep-weighting-nonce'] = wp_create_nonce( 'save-weighting' ); - $this->assertEquals( null, $weighting_class->handle_save() ); - - wp_set_current_user( $this->factory->user->create( array( 'role' => 'administrator' ) ) ); - $_POST = [ - 'ep-weighting-nonce' => wp_create_nonce( 'save-weighting' ), - 'weighting' => [ - 'post' => [ - 'post_title' => [ - 'enabled' => 'on', - 'weight' => 1, - ], - ], - ], - ]; - - $weighting_class->expects( $this->once() )->method( 'redirect' ); - $weighting_class->handle_save(); + public function test_handle_save() { + $this->get_weighting_feature()->handle_save(); } /** * Test the `save_weighting_configuration` method (invalid post type) * - * @expectedIncorrectUsage ElasticPress\Feature\Search\Weighting::save_weighting_configuration + * @group weighting */ public function testSaveWeightingConfigurationInvalidPostType() { + add_filter( + 'ep_meta_mode', + function () { + return 'auto'; + } + ); + $weighting_settings = [ - 'weighting' => [ - 'post' => [ - 'post_title' => [ - 'enabled' => 'on', - 'weight' => 1, - ], + 'post' => [ + 'post_title' => [ + 'enabled' => 'on', + 'weight' => 1, ], ], ]; @@ -347,7 +304,7 @@ function( $config ) { } ); - $this->assertNotContains( 'invalid_post_type', $this->get_weighting_feature()->save_weighting_configuration( $weighting_settings ) ); + $this->assertNotContains( 'invalid_post_type', array_keys( $this->save_weighting_configuration( $weighting_settings ) ) ); } /** @@ -355,15 +312,16 @@ function( $config ) { */ public function testRecursivelyInjectWeightsToFieldsInvalidArgs() { $invalid_args = ''; - $this->assertEquals( null, $this->get_weighting_feature()->recursively_inject_weights_to_fields( $invalid_args, $this->weighting_settings['weighting']['post'] ) ); + $this->assertEquals( null, $this->get_weighting_feature()->recursively_inject_weights_to_fields( $invalid_args, $this->weighting_settings['post'] ) ); } /** * Test the `post_type_has_fields` method * - * @expectedIncorrectUsage ElasticPress\Feature\Search\Weighting::save_weighting_configuration + * @since 5.0.0 + * @group weighting */ - public function testPostTypeHasFieldsWithDefaultConfig() { + public function test_post_type_has_fields_with_default_config() { $this->assertTrue( $this->get_weighting_feature()->post_type_has_fields( 'post' ) ); } @@ -373,16 +331,14 @@ public function testPostTypeHasFieldsWithDefaultConfig() { public function testPostTypeHasFieldsWithCustomConfig() { // Test with configuration saved for post only, page will return false. $weighting_settings = [ - 'weighting' => [ - 'post' => [ - 'post_title' => [ - 'enabled' => 'on', - 'weight' => 1, - ], + 'post' => [ + 'post_title' => [ + 'enabled' => 'on', + 'weight' => 1, ], ], ]; - $this->get_weighting_feature()->save_weighting_configuration( $weighting_settings ); + $this->save_weighting_configuration( $weighting_settings ); $this->assertTrue( $this->get_weighting_feature()->post_type_has_fields( 'post' ) ); $this->assertFalse( $this->get_weighting_feature()->post_type_has_fields( 'page' ) ); @@ -464,10 +420,11 @@ public function testDoWeightingWithDefaultConfig() { /** * Test the `do_weighting` method (with the custom config) * - * @expectedIncorrectUsage ElasticPress\Feature\Search\Weighting::save_weighting_configuration + * @since 5.0.0 + * @group weighting */ - public function testDoWeightingWithCustomConfig() { - $this->get_weighting_feature()->save_weighting_configuration( $this->weighting_settings ); + public function test_do_weighting_with_custom_config() { + $this->save_weighting_configuration( $this->weighting_settings ); $new_formatted_args = $this->get_weighting_feature()->do_weighting( ...$this->getArgs() ); @@ -535,4 +492,19 @@ public function testApplyFilterWhenWeightingConfigWasNotSaved() { ['bool']['should'][0]['multi_match']; $this->assertEquals( [ 'post_content_filtered^40' ], $query_multi_match['fields'] ); } + + /** + * Save the weighting configuration using the REST API endpoint + * + * @param array $settings New settings + * @return array + */ + protected function save_weighting_configuration( $settings ) { + $request = new \WP_REST_Request( 'POST', '/elasticpress/v1/update_weighting' ); + $request->set_header( 'Content-Type', 'application/json' ); + $request->set_body( wp_json_encode( $settings ) ); + $this->get_weighting_feature()->update_weighting( $request ); + + return $this->get_weighting_feature()->get_weighting_configuration(); + } } From f63f8f40c953d9533767fd5546b86f166e4afcf3 Mon Sep 17 00:00:00 2001 From: Felipe Elia Date: Wed, 25 Oct 2023 16:43:27 -0300 Subject: [PATCH 56/63] Delete elasticpress_weighting on uninstall --- uninstall.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/uninstall.php b/uninstall.php index 95c7cb312a..782eea27cf 100644 --- a/uninstall.php +++ b/uninstall.php @@ -44,6 +44,8 @@ class EP_Uninstaller { 'ep_bulk_setting', 'ep_sync_history', + 'elasticpress_weighting', + // Admin notices options 'ep_hide_host_error_notice', 'ep_hide_es_below_compat_notice', From 18b432781f9d090de974e724e85995a3f5f93081 Mon Sep 17 00:00:00 2001 From: Felipe Elia Date: Wed, 25 Oct 2023 16:44:00 -0300 Subject: [PATCH 57/63] Use defaults when the weighting config was not saved --- includes/classes/Indexable/Post/Post.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/classes/Indexable/Post/Post.php b/includes/classes/Indexable/Post/Post.php index 8e65367815..13d173e5ba 100644 --- a/includes/classes/Indexable/Post/Post.php +++ b/includes/classes/Indexable/Post/Post.php @@ -2492,7 +2492,7 @@ protected function parse_tax_query_field( string $field ) : string { protected function filter_allowed_metas_manual( $metas, $post ) { $filtered_metas = []; - $weighting = \ElasticPress\Features::factory()->get_registered_feature( 'search' )->weighting->get_weighting_configuration(); + $weighting = \ElasticPress\Features::factory()->get_registered_feature( 'search' )->weighting->get_weighting_configuration_with_defaults(); if ( empty( $post->post_type ) || empty( $weighting[ $post->post_type ] ) ) { return $filtered_metas; } From 8cf25c54b4c42e540cb852cd06307a43ca913893 Mon Sep 17 00:00:00 2001 From: Felipe Elia Date: Wed, 25 Oct 2023 17:13:48 -0300 Subject: [PATCH 58/63] Allow ep_exclude_from_search by default --- includes/classes/Feature/Search/Search.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/includes/classes/Feature/Search/Search.php b/includes/classes/Feature/Search/Search.php index c43781077e..415d5b6df9 100644 --- a/includes/classes/Feature/Search/Search.php +++ b/includes/classes/Feature/Search/Search.php @@ -107,6 +107,7 @@ public function search_setup() { add_action( 'ep_highlighting_pre_add_highlight', [ $this, 'allow_excerpt_html' ] ); add_action( 'init', [ $this, 'register_meta' ], 20 ); + add_filter( 'ep_prepare_meta_allowed_keys', [ $this, 'add_exclude_from_search' ] ); add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_block_editor_assets' ] ); add_filter( 'ep_post_filters', [ $this, 'exclude_posts_from_search' ], 10, 3 ); add_action( 'post_submitbox_misc_actions', [ $this, 'output_exclude_from_search_setting' ] ); @@ -688,6 +689,18 @@ public function register_meta() { ); } + /** + * Add ep_exclude_from_search to the allowed meta fields list. + * + * @since 5.0.0 + * @param array $keys List of allowed meta fields + * @return array + */ + public function add_exclude_from_search( $keys ) { + $keys[] = 'ep_exclude_from_search'; + return $keys; + } + /** * Enqueue block editor assets. */ From cb49f96f9a63b9d26a27c5082aadf24885b77e81 Mon Sep 17 00:00:00 2001 From: Felipe Elia Date: Wed, 25 Oct 2023 17:13:56 -0300 Subject: [PATCH 59/63] Adjust testTooManyFieldsNoticeInAdmin --- tests/php/TestAdminNotices.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/php/TestAdminNotices.php b/tests/php/TestAdminNotices.php index db86ec4920..fec5b424fd 100644 --- a/tests/php/TestAdminNotices.php +++ b/tests/php/TestAdminNotices.php @@ -561,6 +561,13 @@ function() use ( $es_version ) { * @since 4.4.0 */ public function testTooManyFieldsNoticeInAdmin() { + add_filter( + 'ep_prepare_meta_allowed_keys', + function( $allowed_metakeys ) { + return array_merge( $allowed_metakeys, [ 'meta_key_1', 'meta_key_2', 'meta_key_3', 'meta_key_4' ] ); + } + ); + add_filter( 'ep_total_field_limit', function() { From 72e5f8fd8cd2b0572a5492d6b28c361c015858ae Mon Sep 17 00:00:00 2001 From: Felipe Elia Date: Wed, 25 Oct 2023 18:06:12 -0300 Subject: [PATCH 60/63] If a type is not searchable, do not apply weighting config --- includes/classes/Indexable/Post/Post.php | 35 +++++++++++++++--------- tests/php/features/TestFacetTypeMeta.php | 7 +++++ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/includes/classes/Indexable/Post/Post.php b/includes/classes/Indexable/Post/Post.php index 13d173e5ba..e1e9e124e3 100644 --- a/includes/classes/Indexable/Post/Post.php +++ b/includes/classes/Indexable/Post/Post.php @@ -2491,26 +2491,35 @@ protected function parse_tax_query_field( string $field ) : string { */ protected function filter_allowed_metas_manual( $metas, $post ) { $filtered_metas = []; + $search_feature = \ElasticPress\Features::factory()->get_registered_feature( 'search' ); - $weighting = \ElasticPress\Features::factory()->get_registered_feature( 'search' )->weighting->get_weighting_configuration_with_defaults(); - if ( empty( $post->post_type ) || empty( $weighting[ $post->post_type ] ) ) { + if ( empty( $post->post_type ) ) { + return $filtered_metas; + } + + $weighting = $search_feature->weighting->get_weighting_configuration_with_defaults(); + $is_searchable = in_array( $search_feature, $search_feature->get_searchable_post_types(), true ); + if ( empty( $weighting[ $post->post_type ] ) && $is_searchable ) { return $filtered_metas; } /** This filter is documented in includes/classes/Indexable/Post/Post.php */ $allowed_protected_keys = apply_filters( 'ep_prepare_meta_allowed_protected_keys', [], $post ); - $selected_keys = array_map( - function ( $field ) { - if ( false === strpos( $field, 'meta.' ) ) { - return null; - } - $field_name_parts = explode( '.', $field ); - return $field_name_parts[1]; - }, - array_keys( $weighting[ $post->post_type ] ) - ); - $selected_keys = array_filter( $selected_keys ); + $selected_keys = []; + if ( ! empty( $weighting[ $post->post_type ] ) ) { + $selected_keys = array_map( + function ( $field ) { + if ( false === strpos( $field, 'meta.' ) ) { + return null; + } + $field_name_parts = explode( '.', $field ); + return $field_name_parts[1]; + }, + array_keys( $weighting[ $post->post_type ] ) + ); + $selected_keys = array_filter( $selected_keys ); + } /** * Filter indexable meta keys for posts diff --git a/tests/php/features/TestFacetTypeMeta.php b/tests/php/features/TestFacetTypeMeta.php index b49c5ea2c6..e3252ff9a6 100644 --- a/tests/php/features/TestFacetTypeMeta.php +++ b/tests/php/features/TestFacetTypeMeta.php @@ -146,6 +146,13 @@ public function testSetWpQueryAggs() { * @group facets */ public function testGetMetaValues() { + add_filter( + 'ep_prepare_meta_allowed_keys', + function( $allowed_metakeys ) { + return array_merge( $allowed_metakeys, [ 'new_meta_key_1', 'new_meta_key_2' ] ); + } + ); + $facet_feature = Features::factory()->get_registered_feature( 'facets' ); $facet_type = $facet_feature->types['meta']; From f0b728496d20e0f52ab170c0ec09b3c72ffb0e9b Mon Sep 17 00:00:00 2001 From: Felipe Elia Date: Wed, 25 Oct 2023 19:14:27 -0300 Subject: [PATCH 61/63] Fix TestPost normalizing all meta key names --- tests/php/indexables/TestPost.php | 202 +++++++++++++++++------------- 1 file changed, 117 insertions(+), 85 deletions(-) diff --git a/tests/php/indexables/TestPost.php b/tests/php/indexables/TestPost.php index e15031c35a..f0ffce19aa 100644 --- a/tests/php/indexables/TestPost.php +++ b/tests/php/indexables/TestPost.php @@ -50,6 +50,25 @@ public function set_up() { // Need to call this since it's hooked to init ElasticPress\Features::factory()->get_registered_feature( 'search' )->search_setup(); + + // Allow some meta fields to be indexed. + add_filter( + 'ep_prepare_meta_allowed_keys', + function( $allowed_metakeys ) { + return array_merge( + $allowed_metakeys, + [ + 'test_key', + 'test_key1', + 'test_key2', + 'test_key3', + 'test_key4', + 'test_key5', + 'test_key6', + ] + ); + } + ); } /** @@ -3443,21 +3462,21 @@ public function testMetaQueryMultipleArray() { $this->ep_factory->post->create( array( 'post_content' => 'findme', - 'meta_input' => array( 'meta_key_1' => '1' ), + 'meta_input' => array( 'test_key1' => '1' ), ) ); $this->ep_factory->post->create( array( 'post_content' => 'findme', - 'meta_input' => array( 'meta_key_1' => '1' ), + 'meta_input' => array( 'test_key1' => '1' ), ) ); $this->ep_factory->post->create( array( 'post_content' => 'findme', 'meta_input' => array( - 'meta_key_1' => '1', - 'meta_key_2' => '4', + 'test_key1' => '1', + 'test_key2' => '4', ), ) ); @@ -3465,8 +3484,8 @@ public function testMetaQueryMultipleArray() { array( 'post_content' => 'findme', 'meta_input' => array( - 'meta_key_1' => '1', - 'meta_key_2' => '0', + 'test_key1' => '1', + 'test_key2' => '0', ), ) ); @@ -3474,8 +3493,8 @@ public function testMetaQueryMultipleArray() { array( 'post_content' => 'findme', 'meta_input' => array( - 'meta_key_1' => '1', - 'meta_key_3' => '4', + 'test_key1' => '1', + 'test_key3' => '4', ), ) ); @@ -3486,7 +3505,7 @@ public function testMetaQueryMultipleArray() { 's' => 'findme', 'meta_query' => array( array( - 'key' => 'meta_key_2', + 'key' => 'test_key2', 'value' => '0', 'compare' => '>=', ), @@ -3504,18 +3523,18 @@ public function testMetaQueryMultipleArray() { 'meta_query' => array( 'relation' => 'AND', array( - 'key' => 'meta_key_1', + 'key' => 'test_key1', 'value' => '1', ), array( 'relation' => 'OR', array( - 'key' => 'meta_key_2', + 'key' => 'test_key2', 'value' => '2', 'compare' => '>=', ), array( - 'key' => 'meta_key_3', + 'key' => 'test_key3', 'value' => '4', ), ), @@ -3836,9 +3855,9 @@ public function testPrepareMeta() { 'value 2', ); - add_post_meta( $post_id, 'test_meta_1', 'value 1' ); - add_post_meta( $post_id, 'test_meta_1', 'value 2' ); - add_post_meta( $post_id, 'test_meta_1', $meta_values ); + add_post_meta( $post_id, 'test_key1', 'value 1' ); + add_post_meta( $post_id, 'test_key1', 'value 2' ); + add_post_meta( $post_id, 'test_key1', $meta_values ); add_post_meta( $post_id, '_test_private_meta_1', 'value 1' ); add_post_meta( $post_id, '_test_private_meta_1', 'value 2' ); add_post_meta( $post_id, '_test_private_meta_1', $meta_values ); @@ -3849,14 +3868,20 @@ public function testPrepareMeta() { $meta_2 = ElasticPress\Indexables::factory()->get( 'post' )->prepare_meta( $post ); + add_filter( + 'ep_meta_mode', + function () { + return 'auto'; + } + ); add_filter( 'ep_prepare_meta_excluded_public_keys', array( $this, 'filter_ep_prepare_meta_excluded_public_keys' ) ); $meta_3 = ElasticPress\Indexables::factory()->get( 'post' )->prepare_meta( $post ); $this->assertTrue( is_array( $meta_1 ) && 1 === count( $meta_1 ) ); - $this->assertTrue( is_array( $meta_1 ) && array_key_exists( 'test_meta_1', $meta_1 ) ); + $this->assertTrue( is_array( $meta_1 ) && array_key_exists( 'test_key1', $meta_1 ) ); $this->assertTrue( is_array( $meta_2 ) && 2 === count( $meta_2 ) ); - $this->assertTrue( is_array( $meta_2 ) && array_key_exists( 'test_meta_1', $meta_2 ) && array_key_exists( '_test_private_meta_1', $meta_2 ) ); + $this->assertTrue( is_array( $meta_2 ) && array_key_exists( 'test_key1', $meta_2 ) && array_key_exists( '_test_private_meta_1', $meta_2 ) ); $this->assertTrue( is_array( $meta_3 ) && 1 === count( $meta_3 ) ); $this->assertTrue( is_array( $meta_3 ) && array_key_exists( '_test_private_meta_1', $meta_3 ) ); } @@ -3892,8 +3917,8 @@ public function testPrepareMetaManual() { $post_id = $this->ep_factory->post->create( [ 'meta_input' => [ - 'test_meta_1' => 'value 1', - 'test_meta_2' => 'value 2', + 'not_allowed_key1' => 'value 1', + 'not_allowed_key2' => 'value 2', '_test_private_meta_1' => 'private value 1', '_test_private_meta_2' => 'private value 2', ], @@ -3925,18 +3950,18 @@ public function testPrepareMetaManual() { $this->assertInstanceOf( '\WP_Post', $post ); $this->assertIsArray( $fields ); - $fields[] = 'test_meta_1'; + $fields[] = 'not_allowed_key1'; return $fields; }; add_filter( 'ep_prepare_meta_allowed_keys', $add_meta_via_allowed, 10, 2 ); $prepared_meta = ElasticPress\Indexables::factory()->get( 'post' )->prepare_meta( $post ); - $this->assertSame( [ 'test_meta_1', '_test_private_meta_1' ], array_keys( $prepared_meta ) ); + $this->assertSame( [ 'not_allowed_key1', '_test_private_meta_1' ], array_keys( $prepared_meta ) ); // Set changed weighting remove_filter( 'ep_weighting_configuration', $set_default_weighting ); $set_changed_weighting = function() use ( $weighting_default ) { - $weighting_default['post']['meta.test_meta_2.value'] = [ + $weighting_default['post']['meta.test_key2.value'] = [ 'enabled' => true, 'weight' => 1, ]; @@ -3950,7 +3975,7 @@ public function testPrepareMetaManual() { $prepared_meta = ElasticPress\Indexables::factory()->get( 'post' )->prepare_meta( $post ); $this->assertSame( - [ 'test_meta_1', 'test_meta_2', '_test_private_meta_1', '_test_private_meta_2' ], + [ 'not_allowed_key1', '_test_private_meta_1', '_test_private_meta_2' ], array_keys( $prepared_meta ) ); } @@ -3977,7 +4002,7 @@ public function filter_ep_prepare_meta_allowed_protected_keys( $meta_keys ) { */ public function filter_ep_prepare_meta_excluded_public_keys( $meta_keys ) { - $meta_keys[] = 'test_meta_1'; + $meta_keys[] = 'test_key1'; return $meta_keys; @@ -3999,7 +4024,7 @@ public function testEmptyMetaKey() { 'value 1', 'value 2', ); - add_post_meta( $post_id, 'test_meta_1', $meta_values ); + add_post_meta( $post_id, 'test_key1', $meta_values ); $wpdb->insert( $wpdb->postmeta, @@ -4019,7 +4044,7 @@ public function testEmptyMetaKey() { $this->assertIsArray( $meta_data ); $this->assertCount( 1, $meta_data ); - $this->assertArrayHasKey( 'test_meta_1', $meta_data ); + $this->assertArrayHasKey( 'test_key1', $meta_data ); } /** @@ -4179,7 +4204,7 @@ public function testMetaKeyQueryMix() { 'post_content' => 'post content findme', 'meta_input' => array( 'test_key' => 5, - 'test_key_2' => 'aaa', + 'test_key2' => 'aaa', ), ) ); @@ -4191,7 +4216,7 @@ public function testMetaKeyQueryMix() { 'meta_value_num' => 5, 'meta_query' => array( array( - 'key' => 'test_key_2', + 'key' => 'test_key2', 'value' => 'aaa', ), ), @@ -6882,7 +6907,7 @@ public function testParseOrderbyMetaValueParams( $meta_value_type, $es_type, $me $posts = []; foreach ( $meta_values as $value ) { - $posts[] = $this->ep_factory->post->create( [ 'meta_input' => [ 'custom_meta_key' => $value ] ] ); + $posts[] = $this->ep_factory->post->create( [ 'meta_input' => [ 'test_key' => $value ] ] ); } ElasticPress\Elasticsearch::factory()->refresh_indices(); @@ -6891,14 +6916,14 @@ public function testParseOrderbyMetaValueParams( $meta_value_type, $es_type, $me 'fields' => 'ids', 'orderby' => 'meta_value' . ( $meta_value_type ? "_{$meta_value_type}" : '' ), 'order' => 'asc', - 'meta_key' => 'custom_meta_key', + 'meta_key' => 'test_key', ]; $assert_callback = function( $args ) use ( &$method_executed, $es_type ) { $method_executed = true; - $this->assertArrayHasKey( "meta.custom_meta_key.{$es_type}", $args['sort'][0] ); - $this->assertSame( 'asc', $args['sort'][0][ "meta.custom_meta_key.{$es_type}" ]['order'] ); + $this->assertArrayHasKey( "meta.test_key.{$es_type}", $args['sort'][0] ); + $this->assertSame( 'asc', $args['sort'][0][ "meta.test_key.{$es_type}" ]['order'] ); return $args; }; @@ -6933,7 +6958,7 @@ public function testParseOrderbyMetaValueWithoutMetaKeyParams( $meta_value_type, 'order' => 'asc', 'meta_query' => [ [ - 'key' => 'custom_meta_key', + 'key' => 'test_key', 'compare' => 'EXISTS', ], ], @@ -6942,8 +6967,8 @@ public function testParseOrderbyMetaValueWithoutMetaKeyParams( $meta_value_type, $assert_callback = function( $args ) use ( &$method_executed, $es_type ) { $method_executed = true; - $this->assertArrayHasKey( "meta.custom_meta_key.{$es_type}", $args['sort'][0] ); - $this->assertSame( 'asc', $args['sort'][0][ "meta.custom_meta_key.{$es_type}" ]['order'] ); + $this->assertArrayHasKey( "meta.test_key.{$es_type}", $args['sort'][0] ); + $this->assertSame( 'asc', $args['sort'][0][ "meta.test_key.{$es_type}" ]['order'] ); return $args; }; @@ -6975,11 +7000,11 @@ public function testParseOrderbyMetaQueryTypes( $meta_value_type, $es_type ) { 'order' => 'asc', 'meta_query' => [ [ - 'key' => 'unused_key', + 'key' => 'test_key1', 'type' => 'NUMERIC', ], 'named_clause' => [ - 'key' => 'custom_meta_key', + 'key' => 'test_key', 'type' => $meta_value_type, ], ], @@ -6988,8 +7013,8 @@ public function testParseOrderbyMetaQueryTypes( $meta_value_type, $es_type ) { $assert_callback = function( $args ) use ( &$method_executed, $es_type ) { $method_executed = true; - $this->assertArrayHasKey( "meta.custom_meta_key.{$es_type}", $args['sort'][0] ); - $this->assertSame( 'asc', $args['sort'][0][ "meta.custom_meta_key.{$es_type}" ]['order'] ); + $this->assertArrayHasKey( "meta.test_key.{$es_type}", $args['sort'][0] ); + $this->assertSame( 'asc', $args['sort'][0][ "meta.test_key.{$es_type}" ]['order'] ); return $args; }; @@ -7430,7 +7455,7 @@ public function testPostSyncQueueEPKill() { // Turn on the filter to kill syncing. add_filter( 'ep_post_sync_kill', '__return_true' ); - update_post_meta( $post_id, 'custom_key', 123 ); + update_post_meta( $post_id, 'test_key', 123 ); // Make sure sync queue is still empty when meta is updated for // an existing post. @@ -7449,7 +7474,7 @@ public function testPostSyncQueueEPKill() { remove_filter( 'ep_post_sync_kill', '__return_true' ); // Now verify the queue when this filter is not enabled. - update_post_meta( $post_id, 'custom_key', 456 ); + update_post_meta( $post_id, 'test_key', 456 ); $this->assertNotEmpty( ElasticPress\Indexables::factory()->get( 'post' )->sync_manager->get_sync_queue() ); @@ -7653,8 +7678,8 @@ public function testDeleteAllMetadata() { array( 'post_title' => 'one', 'meta_input' => array( - 'common_meta_one' => 'lorem', - 'common_meta_two' => 'ipsum', + 'test_key1' => 'lorem', + 'test_key2' => 'ipsum', ), ) ); @@ -7662,13 +7687,13 @@ public function testDeleteAllMetadata() { array( 'post_title' => 'two', 'meta_input' => array( - 'common_meta_one' => 'lorem', - 'common_meta_two' => 'ipsum', + 'test_key1' => 'lorem', + 'test_key2' => 'ipsum', ), ) ); - delete_metadata( 'post', null, 'common_meta_one', 'lorem', true ); + delete_metadata( 'post', null, 'test_key1', 'lorem', true ); ElasticPress\Indexables::factory()->get( 'post' )->sync_manager->index_sync_queue(); ElasticPress\Elasticsearch::factory()->refresh_indices(); @@ -7677,7 +7702,7 @@ public function testDeleteAllMetadata() { array( 'post_type' => 'post', 'ep_integrate' => true, - 'meta_key' => 'common_meta_one', + 'meta_key' => 'test_key1', 'meta_value' => 'lorem', ) ); @@ -7689,7 +7714,7 @@ public function testDeleteAllMetadata() { array( 'post_type' => 'post', 'ep_integrate' => true, - 'meta_key' => 'common_meta_two', + 'meta_key' => 'test_key2', 'meta_value' => 'ipsum', ) ); @@ -8006,6 +8031,12 @@ public function testIsMetaAllowed() { $meta_protected = '_meta'; $meta_protected_allowed = '_meta_allowed'; + add_filter( + 'ep_prepare_meta_allowed_keys', + function( $allowed_metakeys ) { + return array_merge( $allowed_metakeys, [ 'meta' ] ); + } + ); add_filter( 'ep_prepare_meta_allowed_protected_keys', function () use ( $meta_protected_allowed ) { @@ -8020,12 +8051,13 @@ function () use ( $meta_not_protected_excluded ) { ); $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); + $post = new \WP_Post( (object) [ 'post_type' => 'post' ] ); - $this->assertTrue( $indexable->is_meta_allowed( $meta_not_protected, null ) ); - $this->assertTrue( $indexable->is_meta_allowed( $meta_protected_allowed, null ) ); + $this->assertTrue( $indexable->is_meta_allowed( $meta_not_protected, $post ) ); + $this->assertTrue( $indexable->is_meta_allowed( $meta_protected_allowed, $post ) ); - $this->assertFalse( $indexable->is_meta_allowed( $meta_not_protected_excluded, null ) ); - $this->assertFalse( $indexable->is_meta_allowed( $meta_protected, null ) ); + $this->assertFalse( $indexable->is_meta_allowed( $meta_not_protected_excluded, $post ) ); + $this->assertFalse( $indexable->is_meta_allowed( $meta_protected, $post ) ); } /** @@ -8037,16 +8069,16 @@ function () use ( $meta_not_protected_excluded ) { public function testGetDistinctMetaFieldKeys() { $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); - $this->ep_factory->post->create( array( 'meta_input' => array( 'new_meta_key_1' => '' ) ) ); - $this->ep_factory->post->create( array( 'meta_input' => array( 'new_meta_key_2' => '' ) ) ); + $this->ep_factory->post->create( array( 'meta_input' => array( 'test_key1' => '' ) ) ); + $this->ep_factory->post->create( array( 'meta_input' => array( 'test_key2' => '' ) ) ); ElasticPress\Elasticsearch::factory()->refresh_indices(); $distinct_meta_field_keys = $indexable->get_distinct_meta_field_keys(); $this->assertIsArray( $distinct_meta_field_keys ); - $this->assertContains( 'new_meta_key_1', $distinct_meta_field_keys ); - $this->assertContains( 'new_meta_key_2', $distinct_meta_field_keys ); + $this->assertContains( 'test_key1', $distinct_meta_field_keys ); + $this->assertContains( 'test_key2', $distinct_meta_field_keys ); } /** @@ -8058,36 +8090,36 @@ public function testGetDistinctMetaFieldKeys() { public function testGetAllDistinctValues() { $indexable = \ElasticPress\Indexables::factory()->get( 'post' ); - $this->ep_factory->post->create( array( 'meta_input' => array( 'new_meta_key_1' => 'foo' ) ) ); - $this->ep_factory->post->create( array( 'meta_input' => array( 'new_meta_key_1' => 'bar' ) ) ); - $this->ep_factory->post->create( array( 'meta_input' => array( 'new_meta_key_1' => 'foobar' ) ) ); + $this->ep_factory->post->create( array( 'meta_input' => array( 'test_key1' => 'foo' ) ) ); + $this->ep_factory->post->create( array( 'meta_input' => array( 'test_key1' => 'bar' ) ) ); + $this->ep_factory->post->create( array( 'meta_input' => array( 'test_key1' => 'foobar' ) ) ); - $this->ep_factory->post->create( array( 'meta_input' => array( 'new_meta_key_2' => 'lorem' ) ) ); - $this->ep_factory->post->create( array( 'meta_input' => array( 'new_meta_key_2' => 'ipsum' ) ) ); + $this->ep_factory->post->create( array( 'meta_input' => array( 'test_key2' => 'lorem' ) ) ); + $this->ep_factory->post->create( array( 'meta_input' => array( 'test_key2' => 'ipsum' ) ) ); ElasticPress\Elasticsearch::factory()->refresh_indices(); - $distinct_values = $indexable->get_all_distinct_values( 'meta.new_meta_key_1.raw' ); + $distinct_values = $indexable->get_all_distinct_values( 'meta.test_key1.raw' ); $this->assertCount( 3, $distinct_values ); $this->assertContains( 'foo', $distinct_values ); $this->assertContains( 'bar', $distinct_values ); $this->assertContains( 'foobar', $distinct_values ); - $distinct_values = $indexable->get_all_distinct_values( 'meta.new_meta_key_1.raw', 1 ); + $distinct_values = $indexable->get_all_distinct_values( 'meta.test_key1.raw', 1 ); $this->assertCount( 1, $distinct_values ); $this->assertContains( 'bar', $distinct_values ); $change_bucket_size = function( $count, $field ) { - return ( 'meta.new_meta_key_1.raw' === $field ) ? 1 : $count; + return ( 'meta.test_key1.raw' === $field ) ? 1 : $count; }; add_filter( 'ep_post_all_distinct_values', $change_bucket_size, 10, 2 ); - $distinct_values_1 = $indexable->get_all_distinct_values( 'meta.new_meta_key_1.raw' ); + $distinct_values_1 = $indexable->get_all_distinct_values( 'meta.test_key1.raw' ); $this->assertCount( 1, $distinct_values_1 ); $this->assertContains( 'bar', $distinct_values_1 ); - $distinct_values_2 = $indexable->get_all_distinct_values( 'meta.new_meta_key_2.raw' ); + $distinct_values_2 = $indexable->get_all_distinct_values( 'meta.test_key2.raw' ); $this->assertCount( 2, $distinct_values_2 ); $this->assertContains( 'lorem', $distinct_values_2 ); $this->assertContains( 'ipsum', $distinct_values_2 ); @@ -8558,7 +8590,7 @@ public function testGetDistinctMetaFieldKeysDbPerPostType() { $this->setupDistinctMetaFieldKeysDbPerPostType(); - $meta_keys = [ '_private_key', 'test_key_1', 'test_key_2' ]; + $meta_keys = [ '_private_key', 'test_key1', 'test_key2' ]; $this->assertSame( $meta_keys, $indexable->get_distinct_meta_field_keys_db_per_post_type( 'ep_test' ) ); /** @@ -8640,8 +8672,8 @@ public function testGetIndexableMetaKeysPerPostType() { 'post_type' => 'ep_test', 'meta_input' => [ '_private_key' => 'private-meta', - 'test_key_1' => 'meta value 1', - 'test_key_2' => 'meta value 2.1', + 'test_key1' => 'meta value 1', + 'test_key2' => 'meta value 2.1', ], ] ); @@ -8649,21 +8681,21 @@ public function testGetIndexableMetaKeysPerPostType() { [ 'post_type' => 'ep_test_2', 'meta_input' => [ - 'test_key_2' => 'meta value 2.2', - 'test_key_3' => 'meta value 3', + 'test_key2' => 'meta value 2.2', + 'test_key3' => 'meta value 3', ], ] ); - $meta_keys = [ 'test_key_1', 'test_key_2' ]; + $meta_keys = [ 'test_key1', 'test_key2' ]; $this->assertEqualsCanonicalizing( $meta_keys, $indexable->get_indexable_meta_keys_per_post_type( 'ep_test' ) ); $change_allowed_meta = function () { - return [ 'test_key_1' => 'meta value 1' ]; + return [ 'test_key1' => 'meta value 1' ]; }; add_filter( 'ep_prepare_meta_data', $change_allowed_meta ); - $meta_keys = [ 'test_key_1' ]; + $meta_keys = [ 'test_key1' ]; $this->assertEqualsCanonicalizing( $meta_keys, $indexable->get_indexable_meta_keys_per_post_type( 'ep_test' ) ); } @@ -8681,8 +8713,8 @@ public function testGetPredictedIndexableMetaKeys() { 'post_type' => 'ep_test', 'meta_input' => [ '_private_key' => 'private-meta', - 'test_key_1' => 'meta value 1', - 'test_key_2' => 'meta value 2.1', + 'test_key1' => 'meta value 1', + 'test_key2' => 'meta value 2.1', ], ] ); @@ -8690,21 +8722,21 @@ public function testGetPredictedIndexableMetaKeys() { [ 'post_type' => 'ep_test_2', 'meta_input' => [ - 'test_key_2' => 'meta value 2.2', - 'test_key_3' => 'meta value 3', + 'test_key2' => 'meta value 2.2', + 'test_key3' => 'meta value 3', ], ] ); - $meta_keys = [ 'test_key_1', 'test_key_2', 'test_key_3' ]; + $meta_keys = [ 'test_key1', 'test_key2', 'test_key3' ]; $this->assertEqualsCanonicalizing( $meta_keys, $indexable->get_predicted_indexable_meta_keys() ); $change_allowed_meta = function () { - return [ 'test_key_1' => 'meta value 1' ]; + return [ 'test_key1' => 'meta value 1' ]; }; add_filter( 'ep_prepare_meta_data', $change_allowed_meta ); - $meta_keys = [ 'test_key_1' ]; + $meta_keys = [ 'test_key1' ]; $this->assertEqualsCanonicalizing( $meta_keys, $indexable->get_predicted_indexable_meta_keys() ); } @@ -8750,8 +8782,8 @@ protected function setupDistinctMetaFieldKeysDbPerPostType() { 'post_type' => 'ep_test', 'meta_input' => [ '_private_key' => 'private-meta', - 'test_key_1' => 'meta value 1', - 'test_key_2' => 'meta value 2.1', + 'test_key1' => 'meta value 1', + 'test_key2' => 'meta value 2.1', ], ] ); @@ -8759,8 +8791,8 @@ protected function setupDistinctMetaFieldKeysDbPerPostType() { [ 'post_type' => 'ep_test_2', 'meta_input' => [ - 'test_key_2' => 'meta value 2.2', - 'test_key_3' => 'meta value 3', + 'test_key2' => 'meta value 2.2', + 'test_key3' => 'meta value 3', ], ] ); From 68d1213614b599da048af6bcce767620b4fc5ef8 Mon Sep 17 00:00:00 2001 From: Felipe Elia Date: Wed, 25 Oct 2023 19:17:48 -0300 Subject: [PATCH 62/63] Fix lint --- tests/php/indexables/TestPost.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/php/indexables/TestPost.php b/tests/php/indexables/TestPost.php index f0ffce19aa..412e9936b5 100644 --- a/tests/php/indexables/TestPost.php +++ b/tests/php/indexables/TestPost.php @@ -3961,7 +3961,7 @@ public function testPrepareMetaManual() { // Set changed weighting remove_filter( 'ep_weighting_configuration', $set_default_weighting ); $set_changed_weighting = function() use ( $weighting_default ) { - $weighting_default['post']['meta.test_key2.value'] = [ + $weighting_default['post']['meta.test_key2.value'] = [ 'enabled' => true, 'weight' => 1, ]; @@ -4203,7 +4203,7 @@ public function testMetaKeyQueryMix() { array( 'post_content' => 'post content findme', 'meta_input' => array( - 'test_key' => 5, + 'test_key' => 5, 'test_key2' => 'aaa', ), ) @@ -8672,8 +8672,8 @@ public function testGetIndexableMetaKeysPerPostType() { 'post_type' => 'ep_test', 'meta_input' => [ '_private_key' => 'private-meta', - 'test_key1' => 'meta value 1', - 'test_key2' => 'meta value 2.1', + 'test_key1' => 'meta value 1', + 'test_key2' => 'meta value 2.1', ], ] ); @@ -8713,8 +8713,8 @@ public function testGetPredictedIndexableMetaKeys() { 'post_type' => 'ep_test', 'meta_input' => [ '_private_key' => 'private-meta', - 'test_key1' => 'meta value 1', - 'test_key2' => 'meta value 2.1', + 'test_key1' => 'meta value 1', + 'test_key2' => 'meta value 2.1', ], ] ); @@ -8782,8 +8782,8 @@ protected function setupDistinctMetaFieldKeysDbPerPostType() { 'post_type' => 'ep_test', 'meta_input' => [ '_private_key' => 'private-meta', - 'test_key1' => 'meta value 1', - 'test_key2' => 'meta value 2.1', + 'test_key1' => 'meta value 1', + 'test_key2' => 'meta value 2.1', ], ] ); From e35af9b86ffecd3825bf97b53c9c3b4f1489bf9e Mon Sep 17 00:00:00 2001 From: Felipe Elia Date: Wed, 25 Oct 2023 19:48:24 -0300 Subject: [PATCH 63/63] Fix testIndexableContentReport --- tests/php/screen/TestStatusReport.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/php/screen/TestStatusReport.php b/tests/php/screen/TestStatusReport.php index 8ce68f74e2..9c63158f25 100644 --- a/tests/php/screen/TestStatusReport.php +++ b/tests/php/screen/TestStatusReport.php @@ -236,6 +236,15 @@ public function testIndexableContentReport() { $meta_fields = array(); $distinct_meta_keys = array(); + $allow_metakeys = function ( $keys ) use ( $post_types ) { + $keys[] = 'shared_meta_key'; + foreach ( $post_types as $post_type ) { + $keys[] = "unique_meta_key_{$post_type}"; + } + return $keys; + }; + add_filter( 'ep_prepare_meta_allowed_keys', $allow_metakeys ); + foreach ( $post_types as $post_type ) { $this->ep_factory->post->create_many( 10,