diff --git a/.storybook/snapshots/__snapshots__/generativedirectanswer--primary.png b/.storybook/snapshots/__snapshots__/generativedirectanswer--primary.png index 71115a58..55c6daed 100644 Binary files a/.storybook/snapshots/__snapshots__/generativedirectanswer--primary.png and b/.storybook/snapshots/__snapshots__/generativedirectanswer--primary.png differ diff --git a/.storybook/snapshots/__snapshots__/mapboxmap--custom-pin.png b/.storybook/snapshots/__snapshots__/mapboxmap--custom-pin.png index 991814d9..64a6d91d 100644 Binary files a/.storybook/snapshots/__snapshots__/mapboxmap--custom-pin.png and b/.storybook/snapshots/__snapshots__/mapboxmap--custom-pin.png differ diff --git a/.storybook/snapshots/__snapshots__/mapboxmap--custom-render-pin.png b/.storybook/snapshots/__snapshots__/mapboxmap--custom-render-pin.png index 6beaafc6..65f120e5 100644 Binary files a/.storybook/snapshots/__snapshots__/mapboxmap--custom-render-pin.png and b/.storybook/snapshots/__snapshots__/mapboxmap--custom-render-pin.png differ diff --git a/.storybook/snapshots/__snapshots__/mapboxmap--multiple-pins.png b/.storybook/snapshots/__snapshots__/mapboxmap--multiple-pins.png index affb5f2e..2635ba46 100644 Binary files a/.storybook/snapshots/__snapshots__/mapboxmap--multiple-pins.png and b/.storybook/snapshots/__snapshots__/mapboxmap--multiple-pins.png differ diff --git a/.storybook/snapshots/__snapshots__/mapboxmap--primary.png b/.storybook/snapshots/__snapshots__/mapboxmap--primary.png index 46278513..422cb92a 100644 Binary files a/.storybook/snapshots/__snapshots__/mapboxmap--primary.png and b/.storybook/snapshots/__snapshots__/mapboxmap--primary.png differ diff --git a/docs/search-ui-react.aisignposticon.md b/docs/search-ui-react.aisignposticon.md new file mode 100644 index 00000000..f86b30cc --- /dev/null +++ b/docs/search-ui-react.aisignposticon.md @@ -0,0 +1,68 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [AISignpostIcon](./search-ui-react.aisignposticon.md) + +## AISignpostIcon() function + +Default icon for the AI signpost. + +**Signature:** + +```typescript +declare function AISignpostIcon(input: { + className?: string; +}): React.JSX.Element; +``` + +## Parameters + + + + +
+ +Parameter + + + + +Type + + + + +Description + + +
+ +{ className } + + + + +(not declared) + + + + + +
+ +input + + + + +{ className?: string; } + + + + + +
+ +**Returns:** + +React.JSX.Element + diff --git a/docs/search-ui-react.aisignpostprops.icon.md b/docs/search-ui-react.aisignpostprops.icon.md new file mode 100644 index 00000000..90bc216b --- /dev/null +++ b/docs/search-ui-react.aisignpostprops.icon.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [AISignpostProps](./search-ui-react.aisignpostprops.md) > [icon](./search-ui-react.aisignpostprops.icon.md) + +## AISignpostProps.icon property + +Icon displayed before the signpost label. Defaults to the SDK's AI signpost icon. + +**Signature:** + +```typescript +icon?: React.JSX.Element; +``` diff --git a/docs/search-ui-react.aisignpostprops.label.md b/docs/search-ui-react.aisignpostprops.label.md new file mode 100644 index 00000000..8b271783 --- /dev/null +++ b/docs/search-ui-react.aisignpostprops.label.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [AISignpostProps](./search-ui-react.aisignpostprops.md) > [label](./search-ui-react.aisignpostprops.label.md) + +## AISignpostProps.label property + +Label displayed in the signpost button. Defaults to "AI-Generated". + +**Signature:** + +```typescript +label?: string; +``` diff --git a/docs/search-ui-react.aisignpostprops.md b/docs/search-ui-react.aisignpostprops.md new file mode 100644 index 00000000..3894c588 --- /dev/null +++ b/docs/search-ui-react.aisignpostprops.md @@ -0,0 +1,115 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [AISignpostProps](./search-ui-react.aisignpostprops.md) + +## AISignpostProps interface + +Props for the built-in AI signpost component. + +**Signature:** + +```typescript +interface AISignpostProps +``` + +## Properties + + + + + + +
+ +Property + + + + +Modifiers + + + + +Type + + + + +Description + + +
+ +[icon?](./search-ui-react.aisignpostprops.icon.md) + + + + + + + +React.JSX.Element + + + + +_(Optional)_ Icon displayed before the signpost label. Defaults to the SDK's AI signpost icon. + + +
+ +[label?](./search-ui-react.aisignpostprops.label.md) + + + + + + + +string + + + + +_(Optional)_ Label displayed in the signpost button. Defaults to "AI-Generated". + + +
+ +[popoverBody?](./search-ui-react.aisignpostprops.popoverbody.md) + + + + + + + +string + + + + +_(Optional)_ Body displayed in the signpost popover. + + +
+ +[popoverHeader?](./search-ui-react.aisignpostprops.popoverheader.md) + + + + + + + +string + + + + +_(Optional)_ Header displayed in the signpost popover. Defaults to "AI-Generated Content". + + +
+ diff --git a/docs/search-ui-react.aisignpostprops.popoverbody.md b/docs/search-ui-react.aisignpostprops.popoverbody.md new file mode 100644 index 00000000..de781ca9 --- /dev/null +++ b/docs/search-ui-react.aisignpostprops.popoverbody.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [AISignpostProps](./search-ui-react.aisignpostprops.md) > [popoverBody](./search-ui-react.aisignpostprops.popoverbody.md) + +## AISignpostProps.popoverBody property + +Body displayed in the signpost popover. + +**Signature:** + +```typescript +popoverBody?: string; +``` diff --git a/docs/search-ui-react.aisignpostprops.popoverheader.md b/docs/search-ui-react.aisignpostprops.popoverheader.md new file mode 100644 index 00000000..fe7ba236 --- /dev/null +++ b/docs/search-ui-react.aisignpostprops.popoverheader.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [AISignpostProps](./search-ui-react.aisignpostprops.md) > [popoverHeader](./search-ui-react.aisignpostprops.popoverheader.md) + +## AISignpostProps.popoverHeader property + +Header displayed in the signpost popover. Defaults to "AI-Generated Content". + +**Signature:** + +```typescript +popoverHeader?: string; +``` diff --git a/docs/search-ui-react.generativedirectanswer.md b/docs/search-ui-react.generativedirectanswer.md index 7c65ba92..0b8e9665 100644 --- a/docs/search-ui-react.generativedirectanswer.md +++ b/docs/search-ui-react.generativedirectanswer.md @@ -32,7 +32,7 @@ Description -{ customCssClasses, answerHeader, citationsHeader, CitationCard, CitationsContainer, } +{ customCssClasses, answerHeader, hideAISignpost, aiSignpostProps, citationsHeader, CitationCard, CitationsContainer, } diff --git a/docs/search-ui-react.generativedirectanswerprops.aisignpostprops.md b/docs/search-ui-react.generativedirectanswerprops.aisignpostprops.md new file mode 100644 index 00000000..99093690 --- /dev/null +++ b/docs/search-ui-react.generativedirectanswerprops.aisignpostprops.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [GenerativeDirectAnswerProps](./search-ui-react.generativedirectanswerprops.md) > [aiSignpostProps](./search-ui-react.generativedirectanswerprops.aisignpostprops.md) + +## GenerativeDirectAnswerProps.aiSignpostProps property + +The props to pass to the AI signpost component. + +**Signature:** + +```typescript +aiSignpostProps?: AISignpostProps; +``` diff --git a/docs/search-ui-react.generativedirectanswerprops.hideaisignpost.md b/docs/search-ui-react.generativedirectanswerprops.hideaisignpost.md new file mode 100644 index 00000000..a894f9f4 --- /dev/null +++ b/docs/search-ui-react.generativedirectanswerprops.hideaisignpost.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/search-ui-react](./search-ui-react.md) > [GenerativeDirectAnswerProps](./search-ui-react.generativedirectanswerprops.md) > [hideAISignpost](./search-ui-react.generativedirectanswerprops.hideaisignpost.md) + +## GenerativeDirectAnswerProps.hideAISignpost property + +Whether to hide the AI signpost for the generative direct answer. + +**Signature:** + +```typescript +hideAISignpost?: boolean; +``` diff --git a/docs/search-ui-react.generativedirectanswerprops.md b/docs/search-ui-react.generativedirectanswerprops.md index 716ecb2e..0501ba3d 100644 --- a/docs/search-ui-react.generativedirectanswerprops.md +++ b/docs/search-ui-react.generativedirectanswerprops.md @@ -37,6 +37,25 @@ Description +[aiSignpostProps?](./search-ui-react.generativedirectanswerprops.aisignpostprops.md) + + + + + + + +[AISignpostProps](./search-ui-react.aisignpostprops.md) + + + + +_(Optional)_ The props to pass to the AI signpost component. + + + + + [answerHeader?](./search-ui-react.generativedirectanswerprops.answerheader.md) @@ -129,6 +148,25 @@ _(Optional)_ The header for the citations section of the generative direct answe _(Optional)_ CSS classes for customizing the component styling. + + + +[hideAISignpost?](./search-ui-react.generativedirectanswerprops.hideaisignpost.md) + + + + + + + +boolean + + + + +_(Optional)_ Whether to hide the AI signpost for the generative direct answer. + + diff --git a/docs/search-ui-react.md b/docs/search-ui-react.md index 969df69f..e2b3f468 100644 --- a/docs/search-ui-react.md +++ b/docs/search-ui-react.md @@ -19,6 +19,17 @@ Description +[AISignpostIcon({ className }, input)](./search-ui-react.aisignposticon.md) + + + + +Default icon for the AI signpost. + + + + + [AlternativeVerticals({ currentVerticalLabel, verticalConfigMap, displayAllOnNoResults, customCssClasses }, input)](./search-ui-react.alternativeverticals.md) @@ -151,7 +162,7 @@ A component which allows a user to search for filters associated with specific e -[GenerativeDirectAnswer({ customCssClasses, answerHeader, citationsHeader, CitationCard, CitationsContainer, }, input)](./search-ui-react.generativedirectanswer.md) +[GenerativeDirectAnswer({ customCssClasses, answerHeader, hideAISignpost, aiSignpostProps, citationsHeader, CitationCard, CitationsContainer, }, input)](./search-ui-react.generativedirectanswer.md) @@ -504,6 +515,17 @@ Description The parameters that are passed into [FilterSearchProps.afterDropdownInputFocus](./search-ui-react.filtersearchprops.afterdropdowninputfocus.md). + + + +[AISignpostProps](./search-ui-react.aisignpostprops.md) + + + + +Props for the built-in AI signpost component. + + diff --git a/etc/search-ui-react.api.md b/etc/search-ui-react.api.md index 00e0fb86..0ba595fb 100644 --- a/etc/search-ui-react.api.md +++ b/etc/search-ui-react.api.md @@ -35,6 +35,19 @@ export interface AfterDropdownInputFocusProps { value: string; } +// @public +export function AISignpostIcon(input: { + className?: string; +}): React_2.JSX.Element; + +// @public +export interface AISignpostProps { + icon?: React_2.JSX.Element; + label?: string; + popoverBody?: string; + popoverHeader?: string; +} + // @public export function AlternativeVerticals(input: AlternativeVerticalsProps): React_2.JSX.Element | null; @@ -377,11 +390,13 @@ export interface GenerativeDirectAnswerCssClasses { // @public export interface GenerativeDirectAnswerProps { + aiSignpostProps?: AISignpostProps; answerHeader?: string | React_2.JSX.Element; CitationCard?: (props: CitationProps) => React_2.JSX.Element | null; CitationsContainer?: (props: CitationsProps) => React_2.JSX.Element | null; citationsHeader?: string | React_2.JSX.Element; customCssClasses?: GenerativeDirectAnswerCssClasses; + hideAISignpost?: boolean; } // @public @@ -1158,7 +1173,7 @@ export interface VisualAutocompleteConfig { // Warnings were encountered during analysis: // -// dist/index.d.ts:1891:5 - (ae-forgotten-export) The symbol "translations" needs to be exported by the entry point index.d.ts +// dist/index.d.ts:1919:5 - (ae-forgotten-export) The symbol "translations" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/locales/en/search-ui-react.json b/locales/en/search-ui-react.json index b5d2d958..2230b0fd 100644 --- a/locales/en/search-ui-react.json +++ b/locales/en/search-ui-react.json @@ -1,5 +1,8 @@ { "aiGeneratedAnswer": "AI Generated Answer", + "aiGeneratedAnswerSignpostLabel": "AI-Generated", + "aiGeneratedAnswerSignpostPopoverHeader": "AI-Generated Content", + "aiGeneratedAnswerSignpostPopoverBody": "This answer was generated by AI from matching search results and may be incomplete or inaccurate. Review the matching results for full context.", "allCategories": "All Categories", "appliedFiltersToCurrentSearch": "Applied filters to current search", "apply": "Apply", @@ -20,6 +23,7 @@ "conductASearch": "Conduct a search", "currentLocation": "Current Location", "didYouMean": "Did you mean ", + "dismiss": "Dismiss", "dropDownScreenReaderInstructions": "When autocomplete results are available, use up and down arrows to review and enter to select.", "feedback": "Feedback", "filterGroupSearchInputLabel": "Search {{title}} Options", diff --git a/package-lock.json b/package-lock.json index 055c6c39..51587948 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@yext/search-ui-react", - "version": "3.0.6", + "version": "3.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@yext/search-ui-react", - "version": "3.0.6", + "version": "3.1.0", "license": "BSD-3-Clause", "dependencies": { "@restart/ui": "^1.0.1", @@ -59,6 +59,7 @@ "@types/jest": "^29.5.12", "@types/jest-image-snapshot": "^6.2.3", "@types/lodash": "^4.14.199", + "@types/mapbox-gl": "^2.7.5", "@types/prop-types": "^15.7.15", "@types/react-dom": "^19.2.3", "@typescript-eslint/eslint-plugin": "^5.16.0", @@ -6745,6 +6746,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/mapbox-gl": { + "version": "2.7.21", + "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-2.7.21.tgz", + "integrity": "sha512-Dx9MuF2kKgT/N22LsMUB4b3acFZh9clVqz9zv1fomoiPoBrJolwYxpWA/9LPO/2N0xWbKi4V+pkjTaFkkx/4wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/mdast": { "version": "3.0.15", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", diff --git a/package.json b/package.json index 7a263365..ac55ae84 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@yext/search-ui-react", - "version": "3.0.6", + "version": "3.1.0", "description": "A library of React Components for powering Yext Search integrations", "author": "watson@yext.com", "license": "BSD-3-Clause", @@ -88,6 +88,7 @@ "@types/jest": "^29.5.12", "@types/jest-image-snapshot": "^6.2.3", "@types/lodash": "^4.14.199", + "@types/mapbox-gl": "^2.7.5", "@types/prop-types": "^15.7.15", "@types/react-dom": "^19.2.3", "@typescript-eslint/eslint-plugin": "^5.16.0", diff --git a/src/components/GenerativeDirectAnswer.tsx b/src/components/GenerativeDirectAnswer.tsx index f9aa893e..99d2cf15 100644 --- a/src/components/GenerativeDirectAnswer.tsx +++ b/src/components/GenerativeDirectAnswer.tsx @@ -10,7 +10,10 @@ import { useComposedCssClasses } from '../hooks'; import { useCardAnalytics } from '../hooks/useCardAnalytics'; import { DefaultRawDataType } from '../models/index'; import { executeGenerativeDirectAnswer } from '../utils/search-operations'; +import { AISignpostIcon } from '../icons/AISignpostIcon'; +import { CloseIcon } from '../icons/CloseIcon'; import { Markdown, MarkdownCssClasses } from './Markdown'; +import { useId } from '../hooks/useId'; import React, { useCallback, useMemo, useRef } from 'react'; /** @@ -50,6 +53,10 @@ export interface GenerativeDirectAnswerProps { customCssClasses?: GenerativeDirectAnswerCssClasses, /** The header for the answer section of the generative direct answer. */ answerHeader?: string | React.JSX.Element, + /** Whether to hide the AI signpost for the generative direct answer. */ + hideAISignpost?: boolean, + /** The props to pass to the AI signpost component. */ + aiSignpostProps?: AISignpostProps, /** The header for the citations section of the generative direct answer. */ citationsHeader?: string | React.JSX.Element, /** @@ -74,6 +81,8 @@ export interface GenerativeDirectAnswerProps { export function GenerativeDirectAnswer({ customCssClasses, answerHeader, + hideAISignpost = false, + aiSignpostProps, citationsHeader, CitationCard, CitationsContainer = Citations, @@ -117,6 +126,8 @@ export function GenerativeDirectAnswer({ gdaResponse={gdaResponse} cssClasses={cssClasses} answerHeader={answerHeader} + hideAISignpost={hideAISignpost} + aiSignpostProps={aiSignpostProps} linkClickHandler={handleClickEvent} /> void } +/** + * Props for the built-in AI signpost component. + * + * @public + */ +export interface AISignpostProps { + /** Icon displayed before the signpost label. Defaults to the SDK's AI signpost icon. */ + icon?: React.JSX.Element, + /** Label displayed in the signpost button. Defaults to "AI-Generated". */ + label?: string, + /** Header displayed in the signpost popover. Defaults to "AI-Generated Content". */ + popoverHeader?: string, + /** Body displayed in the signpost popover. */ + popoverBody?: string +} + +/** + * Displays AI signpost content for the generative direct answer. + */ +function AISignpost({ + icon, + label, + popoverHeader, + popoverBody +}: AISignpostProps): React.JSX.Element { + const { t } = useTranslation(); + const [isOpen, setIsOpen] = React.useState(false); + const popoverId = useId('ai-signpost-popover'); + const popoverHeaderId = useId('ai-signpost-popover-header'); + const popoverDescriptionId = useId('ai-signpost-popover-description'); + const handleSignpostClick = useCallback(() => { + setIsOpen(current => !current); + }, []); + const onSignpostClose = useCallback(() => { + setIsOpen(false); + }, []); + + return ( +
+ + {isOpen && ( + + )} +
+ ); +} + /** * The answer section of the Generative Direct Answer. */ @@ -146,6 +240,8 @@ function Answer(props: AnswerProps) { gdaResponse, cssClasses, answerHeader, + hideAISignpost, + aiSignpostProps, linkClickHandler } = props; const { t } = useTranslation(); @@ -166,6 +262,7 @@ function Answer(props: AnswerProps) {
{answerHeader ?? t('aiGeneratedAnswer')}
+ {!hideAISignpost && }