From e0b89760e5c8dcb2ff0f0fcc20a81d7b84160003 Mon Sep 17 00:00:00 2001 From: Hugo Solar Date: Thu, 10 Jul 2025 15:05:40 -0400 Subject: [PATCH 01/12] add helper text to watson settings --- .../nlu-feature.js | 31 +++++++++- .../settings/components/settings-row/index.js | 21 ++++++- src/scss/settings.scss | 60 +++++++++++++++++++ 3 files changed, 110 insertions(+), 2 deletions(-) diff --git a/src/js/settings/components/feature-additional-settings/nlu-feature.js b/src/js/settings/components/feature-additional-settings/nlu-feature.js index e66abf75e..543d9f02a 100644 --- a/src/js/settings/components/feature-additional-settings/nlu-feature.js +++ b/src/js/settings/components/feature-additional-settings/nlu-feature.js @@ -36,18 +36,46 @@ export const NLUFeatureSettings = () => { category: { label: __( 'Category', 'classifai' ), defaultThreshold: 70, + helperText: __( + '

IBM Watson analyzes your content and assigns a broad topic hierarchy that best describes the overall subject.

'+ + '

Example:/technology and computing/software

'+ + '

Categories are useful for general classification and site-wide content grouping.

'+ + '

Learn more

', + 'classifai' + ), }, keyword: { label: __( 'Keyword', 'classifai' ), defaultThreshold: 70, + helperText: __( + '

Keywords represent important terms in your content that are contextually significant.

'+ + '

Watson extracts these to help identify core concepts, topics, and SEO-friendly tags.

'+ + '

Keywords often map well to WordPress tags.

'+ + '

Learn more

', + 'classifai' + ), }, entity: { label: __( 'Entity', 'classifai' ), defaultThreshold: 70, + helperText: __( + '

Entities are named people, places, brands, and other proper nouns mentioned in your content.

'+ + '

Watson identifies and classifies these by type (e.g., Person, Company, Location) and optionally links them to known databases like Wikipedia.

'+ + '

Entities are helpful for structured data and enhancing rich snippets or metadata.

'+ + '

Learn more

', + 'classifai' + ), }, concept: { label: __( 'Concept', 'classifai' ), defaultThreshold: 70, + helperText: __( + '

Concepts reflect high-level abstract ideas Watson identifies in your content, even if the term isn\'t explicitly used.

'+ + '

For example, an article about "the iPhone" might be linked to the concept of “Apple Inc.”

'+ + '

Concepts are great for semantic tagging and content recommendation systems.

'+ + '

Learn more

', + 'classifai' + ), }, }; @@ -94,12 +122,13 @@ export const NLUFeatureSettings = () => { return ( <> { Object.keys( features ).map( ( feature ) => { - const { defaultThreshold, label } = features[ feature ]; + const { defaultThreshold, label, helperText } = features[ feature ]; return ( { + const [ showTooltip, setShowTooltip ] = useState( false ); + return (
-
{ props.label }
+
+ { props.label } + { props.helperText && ( +
setShowTooltip( true ) } + onMouseLeave={ () => setShowTooltip( false ) } + > + + { showTooltip && ( +
+
+
+ ) } +
+ ) } +
{ props.children }
diff --git a/src/scss/settings.scss b/src/scss/settings.scss index 35fe31414..961e19a22 100644 --- a/src/scss/settings.scss +++ b/src/scss/settings.scss @@ -128,6 +128,66 @@ .classifai-panel-header-close { margin-right: -10px; } + + // Tooltip styles for helper text + .tooltip-container { + position: relative; + display: inline-block; + + .helper-text-icon { + position: relative; + top: 1px; + cursor: pointer; + color: #666; + margin-left: 4px; + font-size: 18px; + transition: color 0.2s ease; + &:hover { + color: var(--wp-admin-theme-color, #3858e9); + } + } + + .settings-helper-text.tooltip { + position: absolute; + left: 0; + transform: translateX(-14%); + background: #FFF; + color: #333; + padding: 8px 12px; + border-radius: 4px; + border: 1px solid #333; + font-size: 10px; + font-weight: normal; + z-index: 1000; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + margin-top: 1px; + width: 400px; + + // Arrow pointing up + &::before { + content: ''; + position: absolute; + top: -4px; + left: 4.2rem; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-bottom: 4px solid #333; + } + + // Ensure tooltip doesn't go off-screen + @media (max-width: 600px) { + left: 0; + transform: none; + white-space: normal; + max-width: 250px; + + &::before { + left: 20px; + transform: none; + } + } + } + } } .classifai-settings-wrapper { From a8937a95ae2d393c0ca7c034b33231384b5bcea3 Mon Sep 17 00:00:00 2001 From: Hugo Solar Date: Sat, 12 Jul 2025 08:06:48 -0400 Subject: [PATCH 02/12] expand documentation to other screens and add threshold --- .../nlu-feature.js | 56 +++++++++---------- .../term-cleanup.js | 24 +++++++- src/js/settings/utils/helper-text.js | 47 ++++++++++++++++ src/scss/settings.scss | 11 ++++ 4 files changed, 107 insertions(+), 31 deletions(-) create mode 100644 src/js/settings/utils/helper-text.js diff --git a/src/js/settings/components/feature-additional-settings/nlu-feature.js b/src/js/settings/components/feature-additional-settings/nlu-feature.js index 543d9f02a..cdc94c237 100644 --- a/src/js/settings/components/feature-additional-settings/nlu-feature.js +++ b/src/js/settings/components/feature-additional-settings/nlu-feature.js @@ -8,6 +8,7 @@ import { __experimentalInputControl as InputControl, // eslint-disable-line @wordpress/no-unsafe-wp-apis } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; +import { useState } from '@wordpress/element'; /** * Internal dependencies @@ -15,6 +16,7 @@ import { __, sprintf } from '@wordpress/i18n'; import { SettingsRow } from '../settings-row'; import { STORE_NAME } from '../../data/store'; import { getFeature } from '../../utils/utils'; +import { thresholdInfo, nluHelperText } from '../../utils/helper-text'; /** * Component for render settings fields when IBM Watson NLU is selected as the provider. @@ -24,6 +26,7 @@ import { getFeature } from '../../utils/utils'; * @return {React.ReactElement} NLUFeatureSettings component. */ export const NLUFeatureSettings = () => { + const [ thresholdInfoStates, setThresholdInfoStates ] = useState( {} ); const featureSettings = useSelect( ( select ) => select( STORE_NAME ).getFeatureSettings() ); @@ -36,46 +39,22 @@ export const NLUFeatureSettings = () => { category: { label: __( 'Category', 'classifai' ), defaultThreshold: 70, - helperText: __( - '

IBM Watson analyzes your content and assigns a broad topic hierarchy that best describes the overall subject.

'+ - '

Example:/technology and computing/software

'+ - '

Categories are useful for general classification and site-wide content grouping.

'+ - '

Learn more

', - 'classifai' - ), + helperText: nluHelperText[ 'category' ], }, keyword: { label: __( 'Keyword', 'classifai' ), defaultThreshold: 70, - helperText: __( - '

Keywords represent important terms in your content that are contextually significant.

'+ - '

Watson extracts these to help identify core concepts, topics, and SEO-friendly tags.

'+ - '

Keywords often map well to WordPress tags.

'+ - '

Learn more

', - 'classifai' - ), + helperText: nluHelperText[ 'keyword' ], }, entity: { label: __( 'Entity', 'classifai' ), defaultThreshold: 70, - helperText: __( - '

Entities are named people, places, brands, and other proper nouns mentioned in your content.

'+ - '

Watson identifies and classifies these by type (e.g., Person, Company, Location) and optionally links them to known databases like Wikipedia.

'+ - '

Entities are helpful for structured data and enhancing rich snippets or metadata.

'+ - '

Learn more

', - 'classifai' - ), + helperText: nluHelperText[ 'entity' ], }, concept: { label: __( 'Concept', 'classifai' ), defaultThreshold: 70, - helperText: __( - '

Concepts reflect high-level abstract ideas Watson identifies in your content, even if the term isn\'t explicitly used.

'+ - '

For example, an article about "the iPhone" might be linked to the concept of “Apple Inc.”

'+ - '

Concepts are great for semantic tagging and content recommendation systems.

'+ - '

Learn more

', - 'classifai' - ), + helperText: nluHelperText[ 'concept' ], }, }; @@ -119,6 +98,13 @@ export const NLUFeatureSettings = () => { } ); } + const toggleThresholdInfo = ( feature ) => { + setThresholdInfoStates( prev => ({ + ...prev, + [ feature ]: ! prev[ feature ] + })); + }; + return ( <> { Object.keys( features ).map( ( feature ) => { @@ -144,7 +130,7 @@ export const NLUFeatureSettings = () => { /> { } ); } } /> +
+ toggleThresholdInfo( feature ) } + style={ { cursor: 'pointer' } } + > + { thresholdInfoStates[ feature ] && ( + thresholdInfo[ 'helper' ] + ) } +
+ { 'ibm_watson_nlu' === featureSettings.provider && ( { ); const { setFeatureSettings } = useDispatch( STORE_NAME ); const { taxonomies = {} } = getFeature( featureName ); - + const [ thresholdInfoStates, setThresholdInfoStates ] = useState( {} ); const options = Object.keys( taxonomies ).map( ( slug ) => { return { value: slug, @@ -47,6 +49,13 @@ export const TermCleanupSettings = () => { }; } ); + const toggleThresholdInfo = ( feature ) => { + setThresholdInfoStates( prev => ({ + ...prev, + [ feature ]: ! prev[ feature ] + })); + }; + const Description = () => { if ( window.classifAISettings?.isEPinstalled ) { return __( @@ -123,7 +132,7 @@ export const TermCleanupSettings = () => { /> { } ); } } /> +
+ toggleThresholdInfo( feature ) } + style={ { cursor: 'pointer' } } + > + { thresholdInfoStates[ feature ] && ( + thresholdInfo[ 'helper' ] + ) } +
); } ) } diff --git a/src/js/settings/utils/helper-text.js b/src/js/settings/utils/helper-text.js new file mode 100644 index 000000000..bba1f3810 --- /dev/null +++ b/src/js/settings/utils/helper-text.js @@ -0,0 +1,47 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +export const thresholdInfo = { + 'helper': ( +
+
+

{ __( 'Determines how confident the AI must be before suggesting a term.', 'classifai' ) }

+

{ __( 'Higher % = More precise (fewer, more accurate terms)', 'classifai' ) }

+

{ __( 'Lower % = More verbose (more suggestions, including lower-confidence terms)', 'classifai' ) }

+
+
+ ), +}; + +export const nluHelperText = { + 'category': __( + '

IBM Watson analyzes your content and assigns a broad topic hierarchy that best describes the overall subject.

'+ + '

Example:/technology and computing/software

'+ + '

Categories are useful for general classification and site-wide content grouping.

'+ + '

Learn more

', + 'classifai' + ), + 'keyword': __( + '

Keywords represent important terms in your content that are contextually significant.

'+ + '

Watson extracts these to help identify core concepts, topics, and SEO-friendly tags.

'+ + '

Keywords often map well to WordPress tags.

'+ + '

Learn more

', + 'classifai' + ), + 'entity': __( + '

Entities are named people, places, brands, and other proper nouns mentioned in your content.

'+ + '

Watson identifies and classifies these by type (e.g., Person, Company, Location) and optionally links them to known databases like Wikipedia.

'+ + '

Entities are helpful for structured data and enhancing rich snippets or metadata.

'+ + '

Learn more

', + 'classifai' + ), + 'concept': __( + '

Concepts reflect high-level abstract ideas Watson identifies in your content, even if the term isn\'t explicitly used.

'+ + '

For example, an article about "the iPhone" might be linked to the concept of "Apple Inc."

'+ + '

Concepts are great for semantic tagging and content recommendation systems.

'+ + '

Learn more

', + 'classifai' + ), +}; diff --git a/src/scss/settings.scss b/src/scss/settings.scss index 961e19a22..000064d00 100644 --- a/src/scss/settings.scss +++ b/src/scss/settings.scss @@ -129,6 +129,17 @@ margin-right: -10px; } + .display-container-wrapper { + margin-top: 8px; + margin-bottom: 8px; + + .helper-text-content { + margin-top: 8px; + padding: 4px 20px; + background-color: #f0f0f1; + } + } + // Tooltip styles for helper text .tooltip-container { position: relative; From b2cda4f6eb56374019789812875c810a91684298 Mon Sep 17 00:00:00 2001 From: Hugo Solar Date: Mon, 14 Jul 2025 07:36:42 -0400 Subject: [PATCH 03/12] add herlper text to moderation settings --- .../feature-additional-settings/moderation.js | 70 +++++++++++-------- src/js/settings/utils/helper-text.js | 9 +++ 2 files changed, 48 insertions(+), 31 deletions(-) diff --git a/src/js/settings/components/feature-additional-settings/moderation.js b/src/js/settings/components/feature-additional-settings/moderation.js index bf0d7c2c1..9f0f41c09 100644 --- a/src/js/settings/components/feature-additional-settings/moderation.js +++ b/src/js/settings/components/feature-additional-settings/moderation.js @@ -11,6 +11,7 @@ import { __ } from '@wordpress/i18n'; */ import { SettingsRow } from '../settings-row'; import { STORE_NAME } from '../../data/store'; +import { moderationHelperText } from '../../utils/helper-text'; /** * Component for Moderation feature settings. @@ -29,36 +30,43 @@ export const ModerationSettings = () => { }; return ( - - { Object.keys( contentTypes ).map( ( contentType ) => { - return ( - { - setFeatureSettings( { - content_types: { - ...featureSettings.content_types, - [ contentType ]: value ? contentType : '0', - }, - } ); - } } - __nextHasNoMarginBottom - /> - ); - } ) } - + <> + + { Object.keys( contentTypes ).map( ( contentType ) => { + return ( + { + setFeatureSettings( { + content_types: { + ...featureSettings.content_types, + [ contentType ]: value ? contentType : '0', + }, + } ); + } } + __nextHasNoMarginBottom + /> + ); + } ) } + +
+
+
+
+
+ ); }; diff --git a/src/js/settings/utils/helper-text.js b/src/js/settings/utils/helper-text.js index bba1f3810..90c1709af 100644 --- a/src/js/settings/utils/helper-text.js +++ b/src/js/settings/utils/helper-text.js @@ -45,3 +45,12 @@ export const nluHelperText = { 'classifai' ), }; + +export const moderationHelperText = { + 'content_types': __( + '

The Moderation endpoint provides a simple interface to classify user-generated content into specific content categories.

'+ + '

Categories include: hate, threatening, harassment, self-harm, sexual, violence

'+ + '

Learn more

', + 'classifai' + ), +}; \ No newline at end of file From bb19087a5ceb9da1174ca2a2214826c18da98d78 Mon Sep 17 00:00:00 2001 From: Hugo Solar Date: Mon, 14 Jul 2025 16:21:04 -0400 Subject: [PATCH 04/12] fix linting issues --- .../feature-additional-settings/moderation.js | 15 ++- .../nlu-feature.js | 43 +++++--- .../term-cleanup.js | 34 ++++-- .../settings/components/settings-row/index.js | 8 +- src/js/settings/utils/helper-text.js | 103 ++++++++++-------- src/scss/settings.scss | 8 ++ 6 files changed, 132 insertions(+), 79 deletions(-) diff --git a/src/js/settings/components/feature-additional-settings/moderation.js b/src/js/settings/components/feature-additional-settings/moderation.js index 9f0f41c09..13f82e4a3 100644 --- a/src/js/settings/components/feature-additional-settings/moderation.js +++ b/src/js/settings/components/feature-additional-settings/moderation.js @@ -45,15 +45,18 @@ export const ModerationSettings = () => { id={ contentType } key={ contentType } checked={ - featureSettings.content_types?.[ contentType ] === - contentType + featureSettings.content_types?.[ + contentType + ] === contentType } label={ contentTypes[ contentType ] } onChange={ ( value ) => { setFeatureSettings( { content_types: { ...featureSettings.content_types, - [ contentType ]: value ? contentType : '0', + [ contentType ]: value + ? contentType + : '0', }, } ); } } @@ -64,7 +67,11 @@ export const ModerationSettings = () => {
-
+
diff --git a/src/js/settings/components/feature-additional-settings/nlu-feature.js b/src/js/settings/components/feature-additional-settings/nlu-feature.js index cdc94c237..1b58b5165 100644 --- a/src/js/settings/components/feature-additional-settings/nlu-feature.js +++ b/src/js/settings/components/feature-additional-settings/nlu-feature.js @@ -39,22 +39,22 @@ export const NLUFeatureSettings = () => { category: { label: __( 'Category', 'classifai' ), defaultThreshold: 70, - helperText: nluHelperText[ 'category' ], + helperText: nluHelperText.category, }, keyword: { label: __( 'Keyword', 'classifai' ), defaultThreshold: 70, - helperText: nluHelperText[ 'keyword' ], + helperText: nluHelperText.keyword, }, entity: { label: __( 'Entity', 'classifai' ), defaultThreshold: 70, - helperText: nluHelperText[ 'entity' ], + helperText: nluHelperText.entity, }, concept: { label: __( 'Concept', 'classifai' ), defaultThreshold: 70, - helperText: nluHelperText[ 'concept' ], + helperText: nluHelperText.concept, }, }; @@ -99,16 +99,17 @@ export const NLUFeatureSettings = () => { } const toggleThresholdInfo = ( feature ) => { - setThresholdInfoStates( prev => ({ + setThresholdInfoStates( ( prev ) => ( { ...prev, - [ feature ]: ! prev[ feature ] - })); + [ feature ]: ! prev[ feature ], + } ) ); }; return ( <> { Object.keys( features ).map( ( feature ) => { - const { defaultThreshold, label, helperText } = features[ feature ]; + const { defaultThreshold, label, helperText } = + features[ feature ]; return ( { /> { } } />
- toggleThresholdInfo( feature ) } - style={ { cursor: 'pointer' } } - > - { thresholdInfoStates[ feature ] && ( - thresholdInfo[ 'helper' ] - ) } + aria-label={ __( + 'Click to show threshold information', + 'classifai' + ) } + > + { thresholdInfoStates[ feature ] && + thresholdInfo.helper }
- + { 'ibm_watson_nlu' === featureSettings.provider && ( { } ); const toggleThresholdInfo = ( feature ) => { - setThresholdInfoStates( prev => ({ + setThresholdInfoStates( ( prev ) => ( { ...prev, - [ feature ]: ! prev[ feature ] - })); + [ feature ]: ! prev[ feature ], + } ) ); }; const Description = () => { @@ -132,7 +132,10 @@ export const TermCleanupSettings = () => { /> { } } />
- toggleThresholdInfo( feature ) } - style={ { cursor: 'pointer' } } - > - { thresholdInfoStates[ feature ] && ( - thresholdInfo[ 'helper' ] - ) } + title={ __( + 'Click to show more information', + 'classifai' + ) } + onClick={ () => + toggleThresholdInfo( feature ) + } + aria-label={ __( + 'Click to show threshold information', + 'classifai' + ) } + > + { thresholdInfoStates[ feature ] && + thresholdInfo.helper }
); diff --git a/src/js/settings/components/settings-row/index.js b/src/js/settings/components/settings-row/index.js index bebe11e9d..4299728aa 100644 --- a/src/js/settings/components/settings-row/index.js +++ b/src/js/settings/components/settings-row/index.js @@ -19,7 +19,7 @@ export const SettingsRow = ( props ) => {
{ props.label } { props.helperText && ( -
setShowTooltip( true ) } onMouseLeave={ () => setShowTooltip( false ) } @@ -27,7 +27,11 @@ export const SettingsRow = ( props ) => { { showTooltip && (
-
+
) }
diff --git a/src/js/settings/utils/helper-text.js b/src/js/settings/utils/helper-text.js index 90c1709af..904b2dfdf 100644 --- a/src/js/settings/utils/helper-text.js +++ b/src/js/settings/utils/helper-text.js @@ -4,53 +4,68 @@ import { __ } from '@wordpress/i18n'; export const thresholdInfo = { - 'helper': ( -
-
-

{ __( 'Determines how confident the AI must be before suggesting a term.', 'classifai' ) }

-

{ __( 'Higher % = More precise (fewer, more accurate terms)', 'classifai' ) }

-

{ __( 'Lower % = More verbose (more suggestions, including lower-confidence terms)', 'classifai' ) }

-
-
- ), + helper: ( +
+
+

+ { __( + 'Determines how confident the AI must be before suggesting a term.', + 'classifai' + ) } +

+

+ { __( + 'Higher % = More precise (fewer, more accurate terms)', + 'classifai' + ) } +

+

+ { __( + 'Lower % = More verbose (more suggestions, including lower-confidence terms)', + 'classifai' + ) } +

+
+
+ ), }; export const nluHelperText = { - 'category': __( - '

IBM Watson analyzes your content and assigns a broad topic hierarchy that best describes the overall subject.

'+ - '

Example:/technology and computing/software

'+ - '

Categories are useful for general classification and site-wide content grouping.

'+ - '

Learn more

', - 'classifai' - ), - 'keyword': __( - '

Keywords represent important terms in your content that are contextually significant.

'+ - '

Watson extracts these to help identify core concepts, topics, and SEO-friendly tags.

'+ - '

Keywords often map well to WordPress tags.

'+ - '

Learn more

', - 'classifai' - ), - 'entity': __( - '

Entities are named people, places, brands, and other proper nouns mentioned in your content.

'+ - '

Watson identifies and classifies these by type (e.g., Person, Company, Location) and optionally links them to known databases like Wikipedia.

'+ - '

Entities are helpful for structured data and enhancing rich snippets or metadata.

'+ - '

Learn more

', - 'classifai' - ), - 'concept': __( - '

Concepts reflect high-level abstract ideas Watson identifies in your content, even if the term isn\'t explicitly used.

'+ - '

For example, an article about "the iPhone" might be linked to the concept of "Apple Inc."

'+ - '

Concepts are great for semantic tagging and content recommendation systems.

'+ - '

Learn more

', - 'classifai' - ), + category: __( + '

IBM Watson analyzes your content and assigns a broad topic hierarchy that best describes the overall subject.

' + + '

Example:/technology and computing/software

' + + '

Categories are useful for general classification and site-wide content grouping.

' + + '

Learn more

', + 'classifai' + ), + keyword: __( + '

Keywords represent important terms in your content that are contextually significant.

' + + '

Watson extracts these to help identify core concepts, topics, and SEO-friendly tags.

' + + '

Keywords often map well to WordPress tags.

' + + '

Learn more

', + 'classifai' + ), + entity: __( + '

Entities are named people, places, brands, and other proper nouns mentioned in your content.

' + + '

Watson identifies and classifies these by type (e.g., Person, Company, Location) and optionally links them to known databases like Wikipedia.

' + + '

Entities are helpful for structured data and enhancing rich snippets or metadata.

' + + '

Learn more

', + 'classifai' + ), + concept: __( + "

Concepts reflect high-level abstract ideas Watson identifies in your content, even if the term isn't explicitly used.

" + + '

For example, an article about "the iPhone" might be linked to the concept of "Apple Inc."

' + + '

Concepts are great for semantic tagging and content recommendation systems.

' + + '

Learn more

', + 'classifai' + ), }; export const moderationHelperText = { - 'content_types': __( - '

The Moderation endpoint provides a simple interface to classify user-generated content into specific content categories.

'+ - '

Categories include: hate, threatening, harassment, self-harm, sexual, violence

'+ - '

Learn more

', - 'classifai' - ), -}; \ No newline at end of file + content_types: __( + '

The Moderation endpoint provides a simple interface to classify user-generated content into specific content categories.

' + + '

Categories include: hate, threatening, harassment, self-harm, sexual, violence

' + + '

Learn more

', + 'classifai' + ), +}; diff --git a/src/scss/settings.scss b/src/scss/settings.scss index 000064d00..d63cbe3f1 100644 --- a/src/scss/settings.scss +++ b/src/scss/settings.scss @@ -133,6 +133,14 @@ margin-top: 8px; margin-bottom: 8px; + .helper-text-icon { + cursor: pointer; + background: none; + border: none; + padding: 0; + margin: 0; + } + .helper-text-content { margin-top: 8px; padding: 4px 20px; From ec0e05e45149cc77585d0c65bfb8ad5e0d4fbd2a Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Thu, 29 Jan 2026 15:04:51 -0700 Subject: [PATCH 05/12] Move the moderation message to the OpenAI component so if we add more moderation Providers in the future, we don't show that message when they are used. Update the message we show a bit --- .../feature-additional-settings/moderation.js | 73 ++++++++----------- .../provider-settings/openai-moderation.js | 27 +++++-- src/js/settings/utils/helper-text.js | 6 +- 3 files changed, 51 insertions(+), 55 deletions(-) diff --git a/src/js/settings/components/feature-additional-settings/moderation.js b/src/js/settings/components/feature-additional-settings/moderation.js index 13f82e4a3..bf0d7c2c1 100644 --- a/src/js/settings/components/feature-additional-settings/moderation.js +++ b/src/js/settings/components/feature-additional-settings/moderation.js @@ -11,7 +11,6 @@ import { __ } from '@wordpress/i18n'; */ import { SettingsRow } from '../settings-row'; import { STORE_NAME } from '../../data/store'; -import { moderationHelperText } from '../../utils/helper-text'; /** * Component for Moderation feature settings. @@ -30,50 +29,36 @@ export const ModerationSettings = () => { }; return ( - <> - - { Object.keys( contentTypes ).map( ( contentType ) => { - return ( - { - setFeatureSettings( { - content_types: { - ...featureSettings.content_types, - [ contentType ]: value - ? contentType - : '0', - }, - } ); - } } - __nextHasNoMarginBottom - /> - ); - } ) } - -
-
-
+ { Object.keys( contentTypes ).map( ( contentType ) => { + return ( + { + setFeatureSettings( { + content_types: { + ...featureSettings.content_types, + [ contentType ]: value ? contentType : '0', + }, + } ); } } + __nextHasNoMarginBottom /> -
-
- + ); + } ) } + ); }; diff --git a/src/js/settings/components/provider-settings/openai-moderation.js b/src/js/settings/components/provider-settings/openai-moderation.js index 5491ccc1b..c01ac6a9c 100644 --- a/src/js/settings/components/provider-settings/openai-moderation.js +++ b/src/js/settings/components/provider-settings/openai-moderation.js @@ -8,6 +8,7 @@ import { useSelect, useDispatch } from '@wordpress/data'; */ import { STORE_NAME } from '../../data/store'; import { OpenAISettings } from './openai'; +import { moderationHelperText } from '../../utils/helper-text'; /** * Component for OpenAI Moderation settings. @@ -28,14 +29,24 @@ export const OpenAIModerationSettings = ( { isConfigured = false } ) => { const { setProviderSettings } = useDispatch( STORE_NAME ); const onChange = ( data ) => setProviderSettings( providerName, data ); - if ( isConfigured ) { - return null; - } - return ( - + <> + { ! isConfigured && ( + + ) } + +
+
+
+
+
+ ); }; diff --git a/src/js/settings/utils/helper-text.js b/src/js/settings/utils/helper-text.js index 904b2dfdf..6f98a6391 100644 --- a/src/js/settings/utils/helper-text.js +++ b/src/js/settings/utils/helper-text.js @@ -63,9 +63,9 @@ export const nluHelperText = { export const moderationHelperText = { content_types: __( - '

The Moderation endpoint provides a simple interface to classify user-generated content into specific content categories.

' + - '

Categories include: hate, threatening, harassment, self-harm, sexual, violence

' + - '

Learn more

', + '

The OpenAI moderation endpoint will check if text is potentially harmful.

' + + '

Text will be checked against certain categories, like hate, threatening, harassment, self-harm, sexual, violence, and more. Each category is scored on a scale of 0 to 1, with 0 indicating no harm and 1 indicating the highest level of harm. If something is found to be harmful, it will be flagged and blocked.

' + + '

For more information on how OpenAI moderation works, see their documentation.

', 'classifai' ), }; From 89a475dd0ff95c8285dd0efc03c28804aa157a9b Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Thu, 29 Jan 2026 15:59:36 -0700 Subject: [PATCH 06/12] Change the tooltips we show on the Term Cleanup Feature to use WP components instead of our own custom HTML --- .../term-cleanup.js | 64 +++++++++++-------- src/js/settings/utils/helper-text.js | 42 ++++++------ 2 files changed, 57 insertions(+), 49 deletions(-) diff --git a/src/js/settings/components/feature-additional-settings/term-cleanup.js b/src/js/settings/components/feature-additional-settings/term-cleanup.js index 30f378ada..c79efae84 100644 --- a/src/js/settings/components/feature-additional-settings/term-cleanup.js +++ b/src/js/settings/components/feature-additional-settings/term-cleanup.js @@ -4,10 +4,14 @@ import { useSelect, useDispatch } from '@wordpress/data'; import { CheckboxControl, + Icon, __experimentalInputControl as InputControl, // eslint-disable-line @wordpress/no-unsafe-wp-apis + __experimentalInputControlSuffixWrapper as InputControlSuffixWrapper, // eslint-disable-line @wordpress/no-unsafe-wp-apis + Popover, } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; import { useState } from '@wordpress/element'; +import { info } from '@wordpress/icons'; +import { __ } from '@wordpress/i18n'; /** * Internal dependencies @@ -33,7 +37,7 @@ export const TermCleanupSettings = () => { ); const { setFeatureSettings } = useDispatch( STORE_NAME ); const { taxonomies = {} } = getFeature( featureName ); - const [ thresholdInfoStates, setThresholdInfoStates ] = useState( {} ); + const options = Object.keys( taxonomies ).map( ( slug ) => { return { value: slug, @@ -49,13 +53,6 @@ export const TermCleanupSettings = () => { }; } ); - const toggleThresholdInfo = ( feature ) => { - setThresholdInfoStates( ( prev ) => ( { - ...prev, - [ feature ]: ! prev[ feature ], - } ) ); - }; - const Description = () => { if ( window.classifAISettings?.isEPinstalled ) { return __( @@ -83,6 +80,31 @@ export const TermCleanupSettings = () => { ); }; + const ThresholdPopover = () => { + const [ popoverAnchor, setPopoverAnchor ] = useState(); + const [ isVisible, setIsVisible ] = useState( false ); + const toggleVisible = () => { + setIsVisible( ( state ) => ! state ); + }; + + return ( + <> + + { isVisible && ( + +
+ { thresholdInfo.helper } +
+
+ ) } + + ); + }; + return ( <> { }, } ); } } + __unstableInputWidth="8em" + suffix={ + + + + } min="0" max="100" step="0.01" /> -
- - { thresholdInfoStates[ feature ] && - thresholdInfo.helper } -
); } ) } diff --git a/src/js/settings/utils/helper-text.js b/src/js/settings/utils/helper-text.js index 6f98a6391..d00935932 100644 --- a/src/js/settings/utils/helper-text.js +++ b/src/js/settings/utils/helper-text.js @@ -5,28 +5,26 @@ import { __ } from '@wordpress/i18n'; export const thresholdInfo = { helper: ( -
-
-

- { __( - 'Determines how confident the AI must be before suggesting a term.', - 'classifai' - ) } -

-

- { __( - 'Higher % = More precise (fewer, more accurate terms)', - 'classifai' - ) } -

-

- { __( - 'Lower % = More verbose (more suggestions, including lower-confidence terms)', - 'classifai' - ) } -

-
-
+ <> +

+ { __( + 'Determines how confident the AI must be before suggesting a term.', + 'classifai' + ) } +

+

+ { __( + 'Higher % = More precise (fewer, more accurate terms)', + 'classifai' + ) } +

+

+ { __( + 'Lower % = More verbose (more suggestions, including lower-confidence terms)', + 'classifai' + ) } +

+ ), }; From 9763b26f9877c1e8650c6fe53ab045957a86110c Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Thu, 29 Jan 2026 16:06:26 -0700 Subject: [PATCH 07/12] Change the tooltips we show on the Classification Feature to use WP components instead of our own custom HTML --- .../nlu-feature.js | 63 ++++++++++++------- .../term-cleanup.js | 8 +++ src/js/settings/utils/helper-text.js | 8 +-- 3 files changed, 53 insertions(+), 26 deletions(-) diff --git a/src/js/settings/components/feature-additional-settings/nlu-feature.js b/src/js/settings/components/feature-additional-settings/nlu-feature.js index 9b9a6666f..d835f4306 100644 --- a/src/js/settings/components/feature-additional-settings/nlu-feature.js +++ b/src/js/settings/components/feature-additional-settings/nlu-feature.js @@ -4,9 +4,13 @@ import { useSelect, useDispatch } from '@wordpress/data'; import { CheckboxControl, + Icon, + Popover, SelectControl, __experimentalInputControl as InputControl, // eslint-disable-line @wordpress/no-unsafe-wp-apis + __experimentalInputControlSuffixWrapper as InputControlSuffixWrapper, // eslint-disable-line @wordpress/no-unsafe-wp-apis } from '@wordpress/components'; +import { info } from '@wordpress/icons'; import { __, sprintf } from '@wordpress/i18n'; import { useState } from '@wordpress/element'; @@ -26,7 +30,6 @@ import { thresholdInfo, nluHelperText } from '../../utils/helper-text'; * @return {React.ReactElement} NLUFeatureSettings component. */ export const NLUFeatureSettings = () => { - const [ thresholdInfoStates, setThresholdInfoStates ] = useState( {} ); const featureSettings = useSelect( ( select ) => select( STORE_NAME ).getFeatureSettings() ); @@ -98,11 +101,37 @@ export const NLUFeatureSettings = () => { } ); } - const toggleThresholdInfo = ( feature ) => { - setThresholdInfoStates( ( prev ) => ( { - ...prev, - [ feature ]: ! prev[ feature ], - } ) ); + const ThresholdPopover = () => { + const [ popoverAnchor, setPopoverAnchor ] = useState(); + const [ isVisible, setIsVisible ] = useState( false ); + const toggleVisible = () => { + setIsVisible( ( state ) => ! state ); + }; + + return ( + <> + + { isVisible && ( + +
+ { thresholdInfo.helper } +
+
+ ) } + + ); }; return ( @@ -145,26 +174,16 @@ export const NLUFeatureSettings = () => { [ `${ feature }_threshold` ]: value, } ); } } + __unstableInputWidth="8em" + suffix={ + + + + } min="0" max="100" step="0.01" /> -
- - { thresholdInfoStates[ feature ] && - thresholdInfo.helper } -
{ 'ibm_watson_nlu' === featureSettings.provider && ( { ref={ setPopoverAnchor } icon={ info } onClick={ toggleVisible } + aria-label={ __( + 'Click to show threshold information', + 'classifai' + ) } + title={ __( + 'Click to show threshold information', + 'classifai' + ) } /> { isVisible && ( diff --git a/src/js/settings/utils/helper-text.js b/src/js/settings/utils/helper-text.js index d00935932..9e9b87e2b 100644 --- a/src/js/settings/utils/helper-text.js +++ b/src/js/settings/utils/helper-text.js @@ -33,28 +33,28 @@ export const nluHelperText = { '

IBM Watson analyzes your content and assigns a broad topic hierarchy that best describes the overall subject.

' + '

Example:/technology and computing/software

' + '

Categories are useful for general classification and site-wide content grouping.

' + - '

Learn more

', + '

Learn more

', 'classifai' ), keyword: __( '

Keywords represent important terms in your content that are contextually significant.

' + '

Watson extracts these to help identify core concepts, topics, and SEO-friendly tags.

' + '

Keywords often map well to WordPress tags.

' + - '

Learn more

', + '

Learn more

', 'classifai' ), entity: __( '

Entities are named people, places, brands, and other proper nouns mentioned in your content.

' + '

Watson identifies and classifies these by type (e.g., Person, Company, Location) and optionally links them to known databases like Wikipedia.

' + '

Entities are helpful for structured data and enhancing rich snippets or metadata.

' + - '

Learn more

', + '

Learn more

', 'classifai' ), concept: __( "

Concepts reflect high-level abstract ideas Watson identifies in your content, even if the term isn't explicitly used.

" + '

For example, an article about "the iPhone" might be linked to the concept of "Apple Inc."

' + '

Concepts are great for semantic tagging and content recommendation systems.

' + - '

Learn more

', + '

Learn more

', 'classifai' ), }; From 6995561fbfc89be22c3ebf35b8725d802252d3e8 Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Thu, 29 Jan 2026 16:40:57 -0700 Subject: [PATCH 08/12] Extract our TooltipPopover component out to a shared utility file so we can reuse it. Use this for the label helper text for Watson. Ensure our helper text can be rendered as-is and not use dangerouslySetInnerHTML --- .../nlu-feature.js | 42 +----- .../term-cleanup.js | 44 +----- .../provider-settings/openai-moderation.js | 6 +- .../settings/components/settings-row/index.js | 25 +--- src/js/settings/utils/helper-text.js | 134 ++++++++++++++---- src/js/settings/utils/utils.js | 34 +++++ 6 files changed, 156 insertions(+), 129 deletions(-) diff --git a/src/js/settings/components/feature-additional-settings/nlu-feature.js b/src/js/settings/components/feature-additional-settings/nlu-feature.js index d835f4306..46d2af041 100644 --- a/src/js/settings/components/feature-additional-settings/nlu-feature.js +++ b/src/js/settings/components/feature-additional-settings/nlu-feature.js @@ -4,15 +4,11 @@ import { useSelect, useDispatch } from '@wordpress/data'; import { CheckboxControl, - Icon, - Popover, SelectControl, __experimentalInputControl as InputControl, // eslint-disable-line @wordpress/no-unsafe-wp-apis __experimentalInputControlSuffixWrapper as InputControlSuffixWrapper, // eslint-disable-line @wordpress/no-unsafe-wp-apis } from '@wordpress/components'; -import { info } from '@wordpress/icons'; import { __, sprintf } from '@wordpress/i18n'; -import { useState } from '@wordpress/element'; /** * Internal dependencies @@ -21,6 +17,7 @@ import { SettingsRow } from '../settings-row'; import { STORE_NAME } from '../../data/store'; import { getFeature } from '../../utils/utils'; import { thresholdInfo, nluHelperText } from '../../utils/helper-text'; +import { TooltipPopover } from '../../utils/utils'; /** * Component for render settings fields when IBM Watson NLU is selected as the provider. @@ -101,39 +98,6 @@ export const NLUFeatureSettings = () => { } ); } - const ThresholdPopover = () => { - const [ popoverAnchor, setPopoverAnchor ] = useState(); - const [ isVisible, setIsVisible ] = useState( false ); - const toggleVisible = () => { - setIsVisible( ( state ) => ! state ); - }; - - return ( - <> - - { isVisible && ( - -
- { thresholdInfo.helper } -
-
- ) } - - ); - }; - return ( <> { Object.keys( features ).map( ( feature ) => { @@ -177,7 +141,9 @@ export const NLUFeatureSettings = () => { __unstableInputWidth="8em" suffix={ - + } min="0" diff --git a/src/js/settings/components/feature-additional-settings/term-cleanup.js b/src/js/settings/components/feature-additional-settings/term-cleanup.js index 1500abe5f..965c24140 100644 --- a/src/js/settings/components/feature-additional-settings/term-cleanup.js +++ b/src/js/settings/components/feature-additional-settings/term-cleanup.js @@ -4,13 +4,9 @@ import { useSelect, useDispatch } from '@wordpress/data'; import { CheckboxControl, - Icon, __experimentalInputControl as InputControl, // eslint-disable-line @wordpress/no-unsafe-wp-apis __experimentalInputControlSuffixWrapper as InputControlSuffixWrapper, // eslint-disable-line @wordpress/no-unsafe-wp-apis - Popover, } from '@wordpress/components'; -import { useState } from '@wordpress/element'; -import { info } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; /** @@ -21,6 +17,7 @@ import { STORE_NAME } from '../../data/store'; import { useFeatureContext } from '../feature-settings/context'; import { getFeature } from '../../utils/utils'; import { thresholdInfo } from '../../utils/helper-text'; +import { TooltipPopover } from '../../utils/utils'; /** * Component for Term Cleanup feature settings. @@ -80,39 +77,6 @@ export const TermCleanupSettings = () => { ); }; - const ThresholdPopover = () => { - const [ popoverAnchor, setPopoverAnchor ] = useState(); - const [ isVisible, setIsVisible ] = useState( false ); - const toggleVisible = () => { - setIsVisible( ( state ) => ! state ); - }; - - return ( - <> - - { isVisible && ( - -
- { thresholdInfo.helper } -
-
- ) } - - ); - }; - return ( <> { __unstableInputWidth="8em" suffix={ - + } min="0" diff --git a/src/js/settings/components/provider-settings/openai-moderation.js b/src/js/settings/components/provider-settings/openai-moderation.js index c01ac6a9c..eec4a3a92 100644 --- a/src/js/settings/components/provider-settings/openai-moderation.js +++ b/src/js/settings/components/provider-settings/openai-moderation.js @@ -40,11 +40,7 @@ export const OpenAIModerationSettings = ( { isConfigured = false } ) => {
-
+
{ moderationHelperText.content_types }
diff --git a/src/js/settings/components/settings-row/index.js b/src/js/settings/components/settings-row/index.js index 4299728aa..d69299f44 100644 --- a/src/js/settings/components/settings-row/index.js +++ b/src/js/settings/components/settings-row/index.js @@ -2,7 +2,11 @@ * External dependencies */ import classNames from 'classnames'; -import { useState } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { TooltipPopover } from '../../utils/utils'; /** * Settings row component. @@ -12,29 +16,12 @@ import { useState } from '@wordpress/element'; * @param {Object} props.children The children of the component. */ export const SettingsRow = ( props ) => { - const [ showTooltip, setShowTooltip ] = useState( false ); - return (
{ props.label } { props.helperText && ( -
setShowTooltip( true ) } - onMouseLeave={ () => setShowTooltip( false ) } - > - - { showTooltip && ( -
-
-
- ) } -
+ ) }
diff --git a/src/js/settings/utils/helper-text.js b/src/js/settings/utils/helper-text.js index 9e9b87e2b..44218667e 100644 --- a/src/js/settings/utils/helper-text.js +++ b/src/js/settings/utils/helper-text.js @@ -29,41 +29,117 @@ export const thresholdInfo = { }; export const nluHelperText = { - category: __( - '

IBM Watson analyzes your content and assigns a broad topic hierarchy that best describes the overall subject.

' + - '

Example:/technology and computing/software

' + - '

Categories are useful for general classification and site-wide content grouping.

' + - '

Learn more

', - 'classifai' + category: ( + <> +

+ { __( + 'IBM Watson analyzes your content and assigns a broad topic hierarchy that best describes the overall subject.', + 'classifai' + ) } +

+

+ { __( + 'Example: /technology and computing/software', + 'classifai' + ) } +

+

+ { __( + 'Categories are useful for general classification and site-wide content grouping.', + 'classifai' + ) } +

+ ), - keyword: __( - '

Keywords represent important terms in your content that are contextually significant.

' + - '

Watson extracts these to help identify core concepts, topics, and SEO-friendly tags.

' + - '

Keywords often map well to WordPress tags.

' + - '

Learn more

', - 'classifai' + keyword: ( + <> +

+ { __( + 'Keywords represent important terms in your content that are contextually significant.', + 'classifai' + ) } +

+

+ { __( + 'Watson extracts these to help identify core concepts, topics, and SEO-friendly tags.', + 'classifai' + ) } +

+

+ { __( + 'Keywords often map well to WordPress tags.', + 'classifai' + ) } +

+ ), - entity: __( - '

Entities are named people, places, brands, and other proper nouns mentioned in your content.

' + - '

Watson identifies and classifies these by type (e.g., Person, Company, Location) and optionally links them to known databases like Wikipedia.

' + - '

Entities are helpful for structured data and enhancing rich snippets or metadata.

' + - '

Learn more

', - 'classifai' + entity: ( + <> +

+ { __( + 'Entities are named people, places, brands, and other proper nouns mentioned in your content.', + 'classifai' + ) } +

+

+ { __( + 'Watson identifies and classifies these by type (e.g., Person, Company, Location).', + 'classifai' + ) } +

+

+ { __( + 'Entities are helpful for structured data and enhancing rich snippets or metadata.', + 'classifai' + ) } +

+ ), - concept: __( - "

Concepts reflect high-level abstract ideas Watson identifies in your content, even if the term isn't explicitly used.

" + - '

For example, an article about "the iPhone" might be linked to the concept of "Apple Inc."

' + - '

Concepts are great for semantic tagging and content recommendation systems.

' + - '

Learn more

', - 'classifai' + concept: ( + <> +

+ { __( + "Concepts reflect high-level abstract ideas Watson identifies in your content, even if the term isn't explicitly used.", + 'classifai' + ) } +

+

+ { __( + 'For example, an article about "the iPhone" might be linked to the concept of "Apple Inc."', + 'classifai' + ) } +

+

+ { __( + 'Concepts are great for semantic tagging and content recommendation systems.', + 'classifai' + ) } +

+ ), }; export const moderationHelperText = { - content_types: __( - '

The OpenAI moderation endpoint will check if text is potentially harmful.

' + - '

Text will be checked against certain categories, like hate, threatening, harassment, self-harm, sexual, violence, and more. Each category is scored on a scale of 0 to 1, with 0 indicating no harm and 1 indicating the highest level of harm. If something is found to be harmful, it will be flagged and blocked.

' + - '

For more information on how OpenAI moderation works, see their documentation.

', - 'classifai' + content_types: ( + <> +

+ { __( + 'The OpenAI moderation endpoint will check if text is potentially harmful.', + 'classifai' + ) } +

+

+ { __( + 'Text will be checked against certain categories, like hate, threatening, harassment, self-harm, sexual, violence, and more. Each category is scored on a scale of 0 to 1, with 0 indicating no harm and 1 indicating the highest level of harm. If something is found to be harmful, it will be flagged and blocked.', + 'classifai' + ) } +

+

+ { __( + 'For more information on how OpenAI moderation works, see their documentation.', + 'classifai' + ) } +

+ ), }; diff --git a/src/js/settings/utils/utils.js b/src/js/settings/utils/utils.js index 8e2deeb4f..e50d3b121 100644 --- a/src/js/settings/utils/utils.js +++ b/src/js/settings/utils/utils.js @@ -2,6 +2,11 @@ * WordPress dependencies */ import { useSelect, useDispatch } from '@wordpress/data'; +import { Icon, Popover } from '@wordpress/components'; +import { useState } from '@wordpress/element'; +import { info } from '@wordpress/icons'; +import { __ } from '@wordpress/i18n'; + // Update URL based on the current tab and feature selected export const updateUrl = ( key, value ) => { @@ -205,3 +210,32 @@ export const isProviderConfigurationNeeded = ( feature ) => { return isEnabled && ! authenticated; }; + +export const TooltipPopover = ( { tooltipContent } ) => { + const [ popoverAnchor, setPopoverAnchor ] = useState(); + const [ isVisible, setIsVisible ] = useState( false ); + const toggleVisible = () => { + setIsVisible( ( state ) => ! state ); + }; + + return ( + <> + + { isVisible && ( + +
+ { tooltipContent } +
+
+ ) } + + ); +}; From a01747837f5e8031a927b708c504c36062b4a169 Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Thu, 29 Jan 2026 16:47:11 -0700 Subject: [PATCH 09/12] Remove styles no longer needed. Remove link as it wasn't rendering properly and we don't want to allow arbitrary HTML --- src/js/settings/utils/helper-text.js | 6 --- src/scss/settings.scss | 72 +--------------------------- 2 files changed, 2 insertions(+), 76 deletions(-) diff --git a/src/js/settings/utils/helper-text.js b/src/js/settings/utils/helper-text.js index 44218667e..e3bfd05ce 100644 --- a/src/js/settings/utils/helper-text.js +++ b/src/js/settings/utils/helper-text.js @@ -134,12 +134,6 @@ export const moderationHelperText = { 'classifai' ) }

-

- { __( - 'For more information on how OpenAI moderation works, see their documentation.', - 'classifai' - ) } -

), }; diff --git a/src/scss/settings.scss b/src/scss/settings.scss index df0163655..a9d99e873 100644 --- a/src/scss/settings.scss +++ b/src/scss/settings.scss @@ -129,81 +129,13 @@ } .display-container-wrapper { + background-color: #f0f0f1; margin-top: 8px; margin-bottom: 8px; - .helper-text-icon { - cursor: pointer; - background: none; - border: none; - padding: 0; - margin: 0; - } - .helper-text-content { - margin-top: 8px; + max-width: 600px; padding: 4px 20px; - background-color: #f0f0f1; - } - } - - // Tooltip styles for helper text - .tooltip-container { - position: relative; - display: inline-block; - - .helper-text-icon { - position: relative; - top: 1px; - cursor: pointer; - color: #666; - margin-left: 4px; - font-size: 18px; - transition: color 0.2s ease; - &:hover { - color: var(--wp-admin-theme-color, #3858e9); - } - } - - .settings-helper-text.tooltip { - position: absolute; - left: 0; - transform: translateX(-14%); - background: #FFF; - color: #333; - padding: 8px 12px; - border-radius: 4px; - border: 1px solid #333; - font-size: 10px; - font-weight: normal; - z-index: 1000; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); - margin-top: 1px; - width: 400px; - - // Arrow pointing up - &::before { - content: ''; - position: absolute; - top: -4px; - left: 4.2rem; - border-left: 4px solid transparent; - border-right: 4px solid transparent; - border-bottom: 4px solid #333; - } - - // Ensure tooltip doesn't go off-screen - @media (max-width: 600px) { - left: 0; - transform: none; - white-space: normal; - max-width: 250px; - - &::before { - left: 20px; - transform: none; - } - } } } } From 07516e89cfa0891037e93dda80ce372ad4c3386a Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Thu, 29 Jan 2026 16:51:36 -0700 Subject: [PATCH 10/12] Fix eslint errors --- .../components/feature-additional-settings/nlu-feature.js | 3 +-- .../components/feature-additional-settings/term-cleanup.js | 3 +-- src/js/settings/utils/utils.js | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/js/settings/components/feature-additional-settings/nlu-feature.js b/src/js/settings/components/feature-additional-settings/nlu-feature.js index 46d2af041..abebc8cd3 100644 --- a/src/js/settings/components/feature-additional-settings/nlu-feature.js +++ b/src/js/settings/components/feature-additional-settings/nlu-feature.js @@ -15,9 +15,8 @@ import { __, sprintf } from '@wordpress/i18n'; */ import { SettingsRow } from '../settings-row'; import { STORE_NAME } from '../../data/store'; -import { getFeature } from '../../utils/utils'; +import { getFeature, TooltipPopover } from '../../utils/utils'; import { thresholdInfo, nluHelperText } from '../../utils/helper-text'; -import { TooltipPopover } from '../../utils/utils'; /** * Component for render settings fields when IBM Watson NLU is selected as the provider. diff --git a/src/js/settings/components/feature-additional-settings/term-cleanup.js b/src/js/settings/components/feature-additional-settings/term-cleanup.js index 965c24140..3e7baefa8 100644 --- a/src/js/settings/components/feature-additional-settings/term-cleanup.js +++ b/src/js/settings/components/feature-additional-settings/term-cleanup.js @@ -15,9 +15,8 @@ import { __ } from '@wordpress/i18n'; import { SettingsRow } from '../settings-row'; import { STORE_NAME } from '../../data/store'; import { useFeatureContext } from '../feature-settings/context'; -import { getFeature } from '../../utils/utils'; +import { getFeature, TooltipPopover } from '../../utils/utils'; import { thresholdInfo } from '../../utils/helper-text'; -import { TooltipPopover } from '../../utils/utils'; /** * Component for Term Cleanup feature settings. diff --git a/src/js/settings/utils/utils.js b/src/js/settings/utils/utils.js index e50d3b121..299c2e99c 100644 --- a/src/js/settings/utils/utils.js +++ b/src/js/settings/utils/utils.js @@ -7,7 +7,6 @@ import { useState } from '@wordpress/element'; import { info } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; - // Update URL based on the current tab and feature selected export const updateUrl = ( key, value ) => { const urlParams = new URLSearchParams( window.location.search ); From caf1ab04361984df241e6e405bbb623568fd74c3 Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Fri, 30 Jan 2026 07:47:20 -0700 Subject: [PATCH 11/12] Add a container around our icon and use that to show/hide the tooltip as this seems to perform better --- src/js/settings/utils/helper-text.js | 15 +++++++++++++++ src/js/settings/utils/utils.js | 17 +++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/js/settings/utils/helper-text.js b/src/js/settings/utils/helper-text.js index e3bfd05ce..2986ed623 100644 --- a/src/js/settings/utils/helper-text.js +++ b/src/js/settings/utils/helper-text.js @@ -3,6 +3,11 @@ */ import { __ } from '@wordpress/i18n'; +/** + * Threshold information. + * + * @type {Object} + */ export const thresholdInfo = { helper: ( <> @@ -28,6 +33,11 @@ export const thresholdInfo = { ), }; +/** + * NLU helper text. + * + * @type {Object} + */ export const nluHelperText = { category: ( <> @@ -119,6 +129,11 @@ export const nluHelperText = { ), }; +/** + * Moderation helper text. + * + * @type {Object} + */ export const moderationHelperText = { content_types: ( <> diff --git a/src/js/settings/utils/utils.js b/src/js/settings/utils/utils.js index 299c2e99c..012f77a3e 100644 --- a/src/js/settings/utils/utils.js +++ b/src/js/settings/utils/utils.js @@ -210,6 +210,13 @@ export const isProviderConfigurationNeeded = ( feature ) => { return isEnabled && ! authenticated; }; +/** + * Tooltip Popover component. + * + * @param {Object} props The props object. + * @param {string} props.tooltipContent The tooltip content. + * @return {React.ReactElement} The TooltipPopover component. + */ export const TooltipPopover = ( { tooltipContent } ) => { const [ popoverAnchor, setPopoverAnchor ] = useState(); const [ isVisible, setIsVisible ] = useState( false ); @@ -218,12 +225,14 @@ export const TooltipPopover = ( { tooltipContent } ) => { }; return ( - <> +
{
) } - +
); }; From 9931e3d6d96351618355469a8f06ea3734079062 Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Fri, 30 Jan 2026 07:47:28 -0700 Subject: [PATCH 12/12] Fix E2E tests on trunk --- tests/bin/initialize.sh | 1 + tests/test-plugin/e2e-test-plugin.php | 3 +++ 2 files changed, 4 insertions(+) diff --git a/tests/bin/initialize.sh b/tests/bin/initialize.sh index c50b4be22..7ccc2716e 100755 --- a/tests/bin/initialize.sh +++ b/tests/bin/initialize.sh @@ -3,3 +3,4 @@ npm run env run tests-wordpress chmod -- -c ugo+w /var/www/html npm run env run tests-cli wp rewrite structure '/%postname%/' -- --hard npm run env run tests-cli wp plugin deactivate classic-editor npm run env run tests-cli wp plugin deactivate woocommerce +npm run env run tests-cli wp plugin deactivate elasticpress diff --git a/tests/test-plugin/e2e-test-plugin.php b/tests/test-plugin/e2e-test-plugin.php index a41a31ecb..5b3270eb7 100644 --- a/tests/test-plugin/e2e-test-plugin.php +++ b/tests/test-plugin/e2e-test-plugin.php @@ -10,6 +10,9 @@ add_filter( 'classifai_aws_polly_pre_connect_to_service', 'classifai_mock_aws_polly_connect_to_service' ); add_filter( 'classifai_aws_polly_pre_synthesize_speech', 'classifai_mock_aws_polly_pre_synthesize_speech' ); +// Disable ElasticPress admin bar. +add_filter( 'ep_admin_bar_should_display', '__return_false' ); + /** * Mock ClassifAI's HTTP requests. *