diff --git a/src/platform/plugins/shared/dashboard/public/dashboard_renderer/settings/settings_flyout.tsx b/src/platform/plugins/shared/dashboard/public/dashboard_renderer/settings/settings_flyout.tsx index 0f4e747023cd4..173558323b2fe 100644 --- a/src/platform/plugins/shared/dashboard/public/dashboard_renderer/settings/settings_flyout.tsx +++ b/src/platform/plugins/shared/dashboard/public/dashboard_renderer/settings/settings_flyout.tsx @@ -126,19 +126,10 @@ export const DashboardSettingsFlyout = ({ onClose, ariaLabelledBy }: DashboardSe if (!savedObjectsTaggingApi) return; return ( - - } - > - updateDashboardSetting({ tags: selectedTags })} - /> - + updateDashboardSetting({ tags: selectedTags })} + /> ); }; diff --git a/x-pack/platform/plugins/private/translations/translations/de-DE.json b/x-pack/platform/plugins/private/translations/translations/de-DE.json index e644a583bb6ed..c3e9195f0d817 100644 --- a/x-pack/platform/plugins/private/translations/translations/de-DE.json +++ b/x-pack/platform/plugins/private/translations/translations/de-DE.json @@ -1471,7 +1471,6 @@ "dashboard.embeddableApi.showSettings.flyout.form.syncColorsBetweenPanelsSwitchLabel": "Farbpaletten zwischen den Bedienfeldern synchronisieren", "dashboard.embeddableApi.showSettings.flyout.form.syncCursorBetweenPanelsSwitchLabel": "Cursor über alle Felder hinweg synchronisieren", "dashboard.embeddableApi.showSettings.flyout.form.syncTooltipsBetweenPanelsSwitchLabel": "Synchrone Tooltips für alle Bedienfelder", - "dashboard.embeddableApi.showSettings.flyout.form.tagsFormRowLabel": "Tags", "dashboard.embeddableApi.showSettings.flyout.form.useMarginsBetweenPanelsSwitchLabel": "Verwenden Sie Abstände zwischen den Panels", "dashboard.embeddableApi.showSettings.flyout.formRow.syncAcrossPanelsLabel": "Über alle Felder hinweg synchronisieren", "dashboard.embeddableApi.showSettings.flyout.title": "Dashboard-Einstellungen", diff --git a/x-pack/platform/plugins/private/translations/translations/fr-FR.json b/x-pack/platform/plugins/private/translations/translations/fr-FR.json index 773c556dfe997..bd37817442fca 100644 --- a/x-pack/platform/plugins/private/translations/translations/fr-FR.json +++ b/x-pack/platform/plugins/private/translations/translations/fr-FR.json @@ -1488,7 +1488,6 @@ "dashboard.embeddableApi.showSettings.flyout.form.syncColorsBetweenPanelsSwitchLabel": "Synchroniser les palettes de couleur de tous les panneaux", "dashboard.embeddableApi.showSettings.flyout.form.syncCursorBetweenPanelsSwitchLabel": "Synchroniser le curseur de tous les panneaux", "dashboard.embeddableApi.showSettings.flyout.form.syncTooltipsBetweenPanelsSwitchLabel": "Synchroniser les infobulles de tous les panneaux", - "dashboard.embeddableApi.showSettings.flyout.form.tagsFormRowLabel": "Balises", "dashboard.embeddableApi.showSettings.flyout.form.useMarginsBetweenPanelsSwitchLabel": "Utiliser des marges entre les panneaux", "dashboard.embeddableApi.showSettings.flyout.formRow.syncAcrossPanelsLabel": "Synchroniser sur tous les panneaux", "dashboard.embeddableApi.showSettings.flyout.title": "Paramètres du tableau de bord", diff --git a/x-pack/platform/plugins/private/translations/translations/ja-JP.json b/x-pack/platform/plugins/private/translations/translations/ja-JP.json index 5f3b1c2c55ec0..f46f9e68b4771 100644 --- a/x-pack/platform/plugins/private/translations/translations/ja-JP.json +++ b/x-pack/platform/plugins/private/translations/translations/ja-JP.json @@ -1491,7 +1491,6 @@ "dashboard.embeddableApi.showSettings.flyout.form.syncColorsBetweenPanelsSwitchLabel": "パネル全体でカラーパレットを同期", "dashboard.embeddableApi.showSettings.flyout.form.syncCursorBetweenPanelsSwitchLabel": "パネル全体でカーソルを同期", "dashboard.embeddableApi.showSettings.flyout.form.syncTooltipsBetweenPanelsSwitchLabel": "パネル間でツールチップを同期", - "dashboard.embeddableApi.showSettings.flyout.form.tagsFormRowLabel": "タグ", "dashboard.embeddableApi.showSettings.flyout.form.useMarginsBetweenPanelsSwitchLabel": "パネルの間に余白を使用", "dashboard.embeddableApi.showSettings.flyout.formRow.syncAcrossPanelsLabel": "パネル全体で同期", "dashboard.embeddableApi.showSettings.flyout.title": "ダッシュボード設定", diff --git a/x-pack/platform/plugins/private/translations/translations/zh-CN.json b/x-pack/platform/plugins/private/translations/translations/zh-CN.json index 9b5b2e0bf8d52..38bcf10f73546 100644 --- a/x-pack/platform/plugins/private/translations/translations/zh-CN.json +++ b/x-pack/platform/plugins/private/translations/translations/zh-CN.json @@ -1485,7 +1485,6 @@ "dashboard.embeddableApi.showSettings.flyout.form.syncColorsBetweenPanelsSwitchLabel": "在面板之间同步调色板", "dashboard.embeddableApi.showSettings.flyout.form.syncCursorBetweenPanelsSwitchLabel": "在面板之间同步光标", "dashboard.embeddableApi.showSettings.flyout.form.syncTooltipsBetweenPanelsSwitchLabel": "在面板之间同步工具提示", - "dashboard.embeddableApi.showSettings.flyout.form.tagsFormRowLabel": "标签", "dashboard.embeddableApi.showSettings.flyout.form.useMarginsBetweenPanelsSwitchLabel": "在面板间使用边距", "dashboard.embeddableApi.showSettings.flyout.formRow.syncAcrossPanelsLabel": "跨面板同步", "dashboard.embeddableApi.showSettings.flyout.title": "仪表板设置", diff --git a/x-pack/platform/plugins/shared/saved_objects_tagging/public/components/base/tag_selector.tsx b/x-pack/platform/plugins/shared/saved_objects_tagging/public/components/base/tag_selector.tsx index b42f7e41939b2..529da7ef5f9a3 100644 --- a/x-pack/platform/plugins/shared/saved_objects_tagging/public/components/base/tag_selector.tsx +++ b/x-pack/platform/plugins/shared/saved_objects_tagging/public/components/base/tag_selector.tsx @@ -113,7 +113,7 @@ export const TagSelector: FC = ({ allowCreate, openCreateModal, fullWidth = true, - ...otherProps + ...comboBoxProps }) => { const [currentSearch, setCurrentSearch] = useState(''); @@ -175,16 +175,26 @@ export const TagSelector: FC = ({ [selected, onTagsSelected, openCreateModal, currentSearch] ); + const { onSearchChange: onSearchChangeProp, ...restProps } = comboBoxProps; + + const handleOnSearchChange = useCallback( + (searchValue: string) => { + setCurrentSearch(searchValue); + onSearchChangeProp?.(searchValue); + }, + [onSearchChangeProp] + ); + return ( placeholder={''} options={options} selectedOptions={selectedOptions} - onSearchChange={setCurrentSearch} + onSearchChange={handleOnSearchChange} onChange={onChange} renderOption={renderOption} fullWidth={fullWidth} - {...otherProps} + {...restProps} /> ); }; diff --git a/x-pack/platform/plugins/shared/saved_objects_tagging/public/components/connected/saved_object_save_modal_tag_selector.tsx b/x-pack/platform/plugins/shared/saved_objects_tagging/public/components/connected/saved_object_save_modal_tag_selector.tsx index 3113e7dbb18a6..1e8481b054265 100644 --- a/x-pack/platform/plugins/shared/saved_objects_tagging/public/components/connected/saved_object_save_modal_tag_selector.tsx +++ b/x-pack/platform/plugins/shared/saved_objects_tagging/public/components/connected/saved_object_save_modal_tag_selector.tsx @@ -11,6 +11,7 @@ import useObservable from 'react-use/lib/useObservable'; import { EuiFormRow, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import type { SavedObjectSaveModalTagSelectorComponentProps } from '@kbn/saved-objects-tagging-oss-plugin/public'; +import { i18n } from '@kbn/i18n'; import type { TagsCapabilities } from '../../../common'; import { TagSelector } from '../base'; import type { ITagsCache } from '../../services'; @@ -35,6 +36,44 @@ export const getConnectedSavedObjectModalTagSelectorComponent = ({ }: SavedObjectSaveModalTagSelectorComponentProps) => { const tags = useObservable(cache.getState$(), cache.getState()); const [selected, setSelected] = useState(initialSelection); + const [searchValue, setSearchValue] = useState(''); + const [touched, setTouched] = useState(false); + + const normalizedSearchValue = searchValue.trim().toLowerCase(); + + const exactTagMatch = !!normalizedSearchValue + ? tags.find((tag) => tag.name.toLowerCase() === normalizedSearchValue) + : undefined; + + const isTagAlreadyCreated = !!exactTagMatch; + const isTagAlreadySelected = isTagAlreadyCreated && selected.includes(exactTagMatch.id); + const noMatchingTag = !!normalizedSearchValue && !isTagAlreadyCreated; + const isInvalid = touched && (noMatchingTag || isTagAlreadyCreated || isTagAlreadySelected); + + const getErrorMessage = () => { + if (isTagAlreadySelected) { + return i18n.translate('xpack.savedObjectsTagging.uiApi.saveModal.alreadySelectedHint', { + defaultMessage: 'Tag "{searchValue}" is already selected.', + values: { searchValue }, + }); + } + if (isTagAlreadyCreated) { + return i18n.translate('xpack.savedObjectsTagging.uiApi.saveModal.exactTagMatchHint', { + defaultMessage: 'Tag "{searchValue}" already exists. Select it from the existing tags.', + values: { searchValue }, + }); + } + return capabilities.create + ? i18n.translate('xpack.savedObjectsTagging.uiApi.saveModal.noMatchingTagCreateHint', { + defaultMessage: + 'No tags match "{searchValue}". Select an existing tag or create a new one.', + values: { searchValue }, + }) + : i18n.translate('xpack.savedObjectsTagging.uiApi.saveModal.noMatchingTagHint', { + defaultMessage: 'No tags match "{searchValue}".', + values: { searchValue }, + }); + }; const setSelectedInternal = useCallback( (newSelection: string[]) => { @@ -63,6 +102,8 @@ export const getConnectedSavedObjectModalTagSelectorComponent = ({ ) } + isInvalid={isInvalid} + error={isInvalid ? getErrorMessage() : undefined} > setTouched(true)} + isInvalid={isInvalid} {...rest} />