Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated Synonyms UI #3814

Merged
merged 23 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Updated synonyms UI.
  • Loading branch information
JakePT committed Jan 17, 2024
commit 64f0ad746162f08d903c266ff60529cea8551c99
33 changes: 20 additions & 13 deletions assets/js/settings-screen/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* WordPress dependencies.
*/
import { SnackbarList } from '@wordpress/components';
import { createSlotFill, SlotFillProvider, SnackbarList } from '@wordpress/components';
import { useDispatch, useSelect } from '@wordpress/data';
import { createContext, useContext, useMemo, WPElement } from '@wordpress/element';
import { store as noticeStore } from '@wordpress/notices';
Expand All @@ -12,6 +12,7 @@ import { store as noticeStore } from '@wordpress/notices';
import './style.css';

const Context = createContext();
const { Fill, Slot } = createSlotFill('SettingsPageAction');

/**
* ElasticPress Settings Screen provider component.
Expand All @@ -32,26 +33,32 @@ export const SettingsScreenProvider = ({ children, title }) => {

const contextValue = useMemo(
() => ({
ActionSlot: Fill,
createNotice,
removeNotice,
}),
[createNotice, removeNotice],
);

return (
<Context.Provider value={contextValue}>
<div className="ep-settings-page">
<div className="ep-settings-page__wrap">
<h1 className="ep-settings-page__title">{title}</h1>
{children}
<SlotFillProvider>
<Context.Provider value={contextValue}>
<div className="ep-settings-page">
<div className="ep-settings-page__wrap">
<header className="ep-settings-page__header">
<h1 className="ep-settings-page__title">{title}</h1>
<Slot />
</header>
{children}
</div>
<SnackbarList
className="ep-settings-page__snackbar-list"
notices={notices}
onRemove={(notice) => removeNotice(notice)}
/>
</div>
<SnackbarList
className="ep-settings-page__snackbar-list"
notices={notices}
onRemove={(notice) => removeNotice(notice)}
/>
</div>
</Context.Provider>
</Context.Provider>
</SlotFillProvider>
);
};

Expand Down
10 changes: 10 additions & 0 deletions assets/js/settings-screen/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@
max-width: 800px;
}

.ep-settings-page__header {
align-items: center;
display: flex;
justify-content: space-between;

& button {
margin-top: 5px;
}
}

.ep-settings-page__snackbar-list {
bottom: 40px;
left: 0;
Expand Down
2 changes: 2 additions & 0 deletions assets/js/sync-ui/components/previous-sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ export default ({ failures, method, stateDatetime, status, trigger }) => {
return __('Automatic sync after settings change.', 'elasticpress');
case 'install':
return __('Automatic sync after installation.', 'elasticpress');
case 'synonyms-error':
return __('Manual sync following an error in synonyms settings.', 'elasticpress');
case 'manual':
return __('Manual sync from Sync Settings.', 'elasticpress');
case 'upgrade':
Expand Down
6 changes: 6 additions & 0 deletions assets/js/sync-ui/components/progress.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ export default () => {
'Started manually from the Sync page at <time>%s</time>.',
'elasticpress',
);
case 'synonyms-error':
/* translators: %1$s Sync start date and time. */
return __(
'Started manually from an error on the Synonyms Settings page at <time>%s</time>.',
'elasticpress',
);
case 'upgrade':
/* translators: %1$s Sync start date and time. */
return __(
Expand Down
137 changes: 87 additions & 50 deletions assets/js/synonyms/apps/synonyms-settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,28 @@
*/
import { Button, Panel, PanelBody, PanelHeader, TabPanel } from '@wordpress/components';
import { WPElement } from '@wordpress/element';

import { __, sprintf } from '@wordpress/i18n';

/**
* Internal dependencies.
*/
import { useSettingsScreen } from '../../settings-screen';
import { useSynonymsSettings } from '../provider';
import Tab from '../components/shared/tab';
import Hyponyms from '../components/hyponyms';
import Replacements from '../components/replacements';
import SolrEditor from '../components/editors/SolrEditor';
import Synonyms from '../components/synonyms';
import GroupTab from '../components/common/group-tab';
import SolrEditor from '../components/editors/solr-editor';
import Hyponyms from '../components/groups/hyponyms';
import Replacements from '../components/groups/replacements';
import Synonyms from '../components/groups/synonyms';

/**
* Synonyms editor component.
* Synonyms settings app.
*
* @returns {WPElement} Synonyms editor component.
* @returns {WPElement} App element.
*/
const SynonymsEditor = () => {
const { hyponyms, isSolr, replacements, synonyms, switchEditor } = useSynonymsSettings();
export default () => {
const { ActionSlot, createNotice } = useSettingsScreen();
const { isBusy, hyponyms, isSolr, replacements, save, synonyms, switchEditor, syncUrl } =
useSynonymsSettings();

/**
* Handle clicking the editor switch button.
Expand All @@ -34,99 +36,134 @@ const SynonymsEditor = () => {
};

/**
* Handles submitting the form.
* Submit event.
*
* @returns {void}
* @param {Event} event Submit event.
*/
const onSubmit = () => {
return null;
const onSubmit = async (event) => {
event.preventDefault();

try {
await save();
createNotice('success', __('Synonym settings saved.', 'elasticpress'));
} catch (e) {
if (e.code === 'error-update-index') {
createNotice(
'error',
__(
'Could not update index with synonyms. Make sure your data is synced.',
'elasticpress',
),
{
actions: [
{
url: syncUrl,
label: __('Sync', 'elasticpress'),
},
],
},
);
} else {
createNotice(
'error',
__('Something went wrong. Please try again.', 'elasticpress'),
);
}
}
};

/**
* Tabs.
*
* @type {Array}
*/
const tabs = [
{
name: 'synonyms',
title: (
<Tab isInvalid={synonyms.some((s) => !s.valid)}>
<GroupTab isValid={!synonyms.some((s) => !s.valid)}>
{sprintf(__('Synonyms (%d)', 'elasticpress'), synonyms.length)}
</Tab>
</GroupTab>
),
},
{
name: 'hyponyms',
title: (
<Tab isInvalid={hyponyms.some((s) => !s.valid)}>
<GroupTab isValid={!hyponyms.some((s) => !s.valid)}>
{sprintf(__('Hyponyms (%d)', 'elasticpress'), hyponyms.length)}
</Tab>
</GroupTab>
),
},
{
name: 'replacements',
title: (
<Tab isInvalid={replacements.some((s) => !s.valid)}>
<GroupTab isValid={!replacements.some((s) => !s.valid)}>
{sprintf(__('Replacements (%d)', 'elasticpress'), replacements.length)}
</Tab>
</GroupTab>
),
},
];

return (
<>
<button onClick={onClick} type="button">
{isSolr
? __('Switch to Visual Editor', 'elasticpress')
: __('Switch to Advanced Text Editor', 'elasticpress')}
</button>

<ActionSlot>
<Button onClick={onClick} size="small" type="button" variant="secondary">
{isSolr
? __('Switch to visual editor', 'elasticpress')
: __('Switch to advanced text editor', 'elasticpress')}
</Button>
</ActionSlot>
<p>
{__(
'Synonyms enable more flexible search results that show relevant results even without an exact match. Synonyms can be defined as a sets where all words are synonyms for each other, or as alternatives where searches for the primary word will also match the rest, but no vice versa.',
'Synonym rules enable a more flexible search experience that returns relevant results even without an exact match. Rules can be defined as synonyms, for terms with similar meanings; hyponyms, for terms with a hierarchical relationship; or replacements, for corrections and substitutions.',
'elasticpress',
)}
</p>

{!isSolr ? (
<Panel className="ep-synonyms-panel">
<PanelBody>
<TabPanel tabs={tabs}>
{({ name }) => {
switch (name) {
case 'hyponyms':
return <Hyponyms />;
case 'replacements':
return <Replacements />;
case 'synonyms':
default:
return <Synonyms />;
}
}}
</TabPanel>
</PanelBody>
<TabPanel tabs={tabs}>
{({ name }) => (
<PanelBody>
{() => {
switch (name) {
case 'hyponyms':
return <Hyponyms />;
case 'replacements':
return <Replacements />;
case 'synonyms':
default:
return <Synonyms />;
}
}}
</PanelBody>
)}
</TabPanel>
</Panel>
) : (
<Panel>
<Panel className="ep-synonyms-panel">
<PanelHeader>
<h2>{__('Advanced Synonym Editor', 'elasticpress')}</h2>
<h2>{__('Advanced Synonyms Editor', 'elasticpress')}</h2>
</PanelHeader>
<PanelBody>
<p>
{__(
"When you add Sets and Alternatives above, we reduce them to SolrSynonyms which Elasticsearch can understand. If you are an advanced user, you can edit synonyms directly using Solr synonym formatting. This is beneficial if you want to import a large dictionary of synonyms, or want to export this site's synonyms for use on another site.",
'ElasticPress uses the Solr format to define your synonym rules for Elasticsearch. Advanced users can use the field below to edit the synonym rules in this format directly. This can also be used to import a large dictionary of synonyms, or to export your synonyms for use on another site.',
'elasticpress',
)}
</p>
<SolrEditor />
</PanelBody>
</Panel>
)}

<Button onClick={onSubmit} type="button" variant="primary">
<Button
disabled={isBusy}
isBusy={isBusy}
onClick={onSubmit}
type="button"
variant="primary"
>
{__('Save changes', 'elasticpress')}
</Button>
</>
);
};

export default SynonymsEditor;
Loading