diff --git a/src/components/ActionBar/ActionBar.js b/src/components/ActionBar/ActionBar.js index 636043f55..89fd898dc 100644 --- a/src/components/ActionBar/ActionBar.js +++ b/src/components/ActionBar/ActionBar.js @@ -1,8 +1,9 @@ -import React, { useContext } from 'react'; +import React, { lazy, useContext } from 'react'; import PropTypes from 'prop-types'; import { cx } from '@leafygreen-ui/emotion'; import Icon from '@leafygreen-ui/icon'; import { Overline } from '@leafygreen-ui/typography'; +import { useSiteMetadata } from '../../hooks/use-site-metadata'; import { isBrowser } from '../../utils/is-browser'; import { getPlaintext } from '../../utils/get-plaintext'; import { getNestedValue } from '../../utils/get-nested-value'; @@ -16,6 +17,7 @@ import { useFeedbackData, FeedbackContainer, } from '../Widgets/FeedbackWidget'; +import { SuspenseHelper } from '../SuspenseHelper'; import DarkModeDropdown from './DarkModeDropdown'; import SearchInput from './SearchInput'; import { @@ -27,11 +29,15 @@ import { overlineStyling, } from './styles'; +const Chatbot = lazy(() => import('mongodb-chatbot-ui')); +const ChatbotButton = lazy(() => import('./ChatbotButton')); + export const DEPRECATED_PROJECTS = ['atlas-app-services', 'datalake', 'realm']; const ActionBar = ({ template, slug, sidenav, ...props }) => { const url = isBrowser ? window.location.href : null; const metadata = useSnootyMetadata(); + const { snootyEnv } = useSiteMetadata(); const feedbackData = useFeedbackData({ slug, url, @@ -43,6 +49,10 @@ const ActionBar = ({ template, slug, sidenav, ...props }) => { const { hideMobile, setHideMobile } = useContext(SidenavContext); + const CHATBOT_SERVER_BASE_URL = ['dotcomprd', 'production'].includes(snootyEnv) + ? 'https://knowledge.mongodb.com/api/v1' + : 'https://knowledge.staging.corp.mongodb.com/api/v1'; + return ( <div className={cx(props.className, actionBarStyling, containerClassname, isOfflineDocsBuild ? offlineStyling : '')} @@ -60,6 +70,12 @@ const ActionBar = ({ template, slug, sidenav, ...props }) => { {!isOfflineDocsBuild && ( <ActionsBox> {template !== 'openapi' && <DarkModeDropdown />} + <SuspenseHelper> + <Chatbot serverBaseUrl={CHATBOT_SERVER_BASE_URL}> + <ChatbotButton></ChatbotButton> + </Chatbot> + </SuspenseHelper> + {template !== 'errorpage' && !DEPRECATED_PROJECTS.includes(metadata.project) && ( <FeedbackProvider page={feedbackData}> <FeedbackContainer> diff --git a/src/components/ActionBar/ChatbotButton.js b/src/components/ActionBar/ChatbotButton.js new file mode 100644 index 000000000..cb79bd1c7 --- /dev/null +++ b/src/components/ActionBar/ChatbotButton.js @@ -0,0 +1,44 @@ +import React from 'react'; +import { useChatbotContext } from 'mongodb-chatbot-ui'; +import { css, cx } from '@leafygreen-ui/emotion'; +import Button from '@leafygreen-ui/button'; +import Icon from '@leafygreen-ui/icon'; +import IconButton from '@leafygreen-ui/icon-button'; +import { reportAnalytics } from '../../utils/report-analytics'; +import { displayNone } from '../../utils/display-none'; +import { SparkleIcon } from './SparkIcon'; + +const buttonStyling = css` + text-wrap-mode: nowrap; + ${displayNone.onMobileAndTablet} +`; + +const iconStyling = css` + ${displayNone.onLargerThanTablet} +`; + +const ChatbotButton = () => { + const { openChat } = useChatbotContext(); + const onClick = () => { + reportAnalytics('Chatbot button clicked'); + openChat(); + }; + return ( + <> + <Button + className={cx(buttonStyling)} + leftGlyph={<Icon glyph="Sparkle" />} + aria-label={'Ask MongoDB AI'} + variant={'primaryOutline'} + onClick={onClick} + > + Ask Mongodb AI + </Button> + <IconButton className={iconStyling} variant={'primaryOutline'} aria-label="Ask MongoDB AI" onClick={onClick}> + <SparkleIcon /> + </IconButton> + </> + ); +}; + +export default ChatbotButton; diff --git a/src/components/ActionBar/ChatbotControls.js b/src/components/ActionBar/ChatbotControls.js deleted file mode 100644 index 9ae07352a..000000000 --- a/src/components/ActionBar/ChatbotControls.js +++ /dev/null @@ -1,28 +0,0 @@ -import { forwardRef, useCallback, useImperativeHandle } from 'react'; -import { useChatbotContext } from 'mongodb-chatbot-ui'; -import PropTypes from 'prop-types'; - -// Using a forward ref and imperative handle -// to expose lazy loaded child (chatbot) behaviors to parent (SearchInput) -// https://react.dev/reference/react/useImperativeHandle -const ChatbotControls = forwardRef(function ChatbotControls({ searchValue }, ref) { - const { setInputText, handleSubmit, openChat } = useChatbotContext(); - - const onClick = useCallback(async () => { - await openChat(); - setInputText(searchValue); - handleSubmit(searchValue); - }, [handleSubmit, openChat, searchValue, setInputText]); - - useImperativeHandle(ref, () => { - return { - onClick, - }; - }); -}); - -export default ChatbotControls; - -ChatbotControls.propTypes = { - searchValue: PropTypes.string.isRequired, -}; diff --git a/src/components/ActionBar/SearchInput.js b/src/components/ActionBar/SearchInput.js index 691066520..c7edb92e6 100644 --- a/src/components/ActionBar/SearchInput.js +++ b/src/components/ActionBar/SearchInput.js @@ -117,10 +117,6 @@ const SearchInput = ({ className, slug }) => { return (window.location.href = `${fullSearchUrl}/?q=${searchValue}`); }; - // const CHATBOT_SERVER_BASE_URL = ['dotcomprd', 'production'].includes(snootyEnv) - // ? 'https://knowledge.mongodb.com/api/v1' - // : 'https://knowledge.staging.corp.mongodb.com/api/v1'; - return ( <StyledInputContainer className={cx(className)} mobileSearchActive={mobileSearchActive}> <StyledSearchBoxRef ref={searchBoxRef}> @@ -155,22 +151,6 @@ const SearchInput = ({ className, slug }) => { Cancel </Link> )} - {/* <SuspenseHelper> - <Chatbot serverBaseUrl={CHATBOT_SERVER_BASE_URL} darkMode={darkMode}> - <SearchMenu - isOpen={!!searchValue.length && isOpen} - searchBoxRef={searchBoxRef} - searchValue={searchValue} - ref={menuRef} - selectedOption={selectedOption} - slug={slug} - isFocused={isFocused} - selectedResult={selectedResult} - setSelectedResult={setSelectedResult} - setChatbotAvail={setChatbotAvail} - ></SearchMenu> - </Chatbot> - </SuspenseHelper> */} {!mobileSearchActive && ( <IconButton aria-label="Search MongoDB Docs" diff --git a/src/components/ActionBar/styles.js b/src/components/ActionBar/styles.js index 1881e5c15..3a93e7471 100644 --- a/src/components/ActionBar/styles.js +++ b/src/components/ActionBar/styles.js @@ -2,10 +2,9 @@ import styled from '@emotion/styled'; import { palette } from '@leafygreen-ui/palette'; import { css } from '@leafygreen-ui/emotion'; import { theme } from '../../theme/docsTheme'; -import { CONTENT_MAX_WIDTH } from '../../templates/product-landing'; import { displayNone } from '../../utils/display-none'; -const DESKTOP_DARK_MODE_AND_FEEDBACK_BUTTONS_WIDTH = '236px'; +const DESKTOP_DARK_MODE_AND_FEEDBACK_BUTTONS_WIDTH = '400px'; // default styling for all Action Bars export const actionBarStyling = css` @@ -41,10 +40,7 @@ export const actionBarStyling = css` // used for :template: options - 'product-landing', 'changelog' const gridStyling = css` display: grid; - grid-template-columns: minmax(${theme.size.xlarge}, 1fr) minmax(0, ${CONTENT_MAX_WIDTH}px) minmax( - ${DESKTOP_DARK_MODE_AND_FEEDBACK_BUTTONS_WIDTH}, - 1fr - ); + grid-template-columns: minmax(${theme.size.xlarge}, 1fr) repeat(2, minmax(0, 600px)) minmax(${theme.size.xlarge}, 1fr); @media ${theme.screenSize.upToLarge} { grid-template-columns: ${theme.size.medium} 1fr fit-content(100%); @@ -219,12 +215,12 @@ export const ActionsBox = styled('div')` grid-column: -2/-1; @media ${theme.screenSize.upToLarge} { + column-gap: 1px; margin-right: ${theme.size.medium}; } @media ${theme.screenSize.upToMedium} { - margin-left: ${theme.size.small}; - column-gap: ${theme.size.small}; + margin-left: 1px; } `; @@ -252,6 +248,7 @@ export const overlineStyling = css` export const searchIconStyling = css` ${displayNone.onLargerThanMedium}; float: right; + justify-content: right; `; // using content before/after to prevent event bubbling up from lg/search-input/search-result