Skip to content

Commit

Permalink
Display Cloud banner on self-hosted instances (#564)
Browse files Browse the repository at this point in the history
* Display banner unless disabled

* add tests

* Update copy

* Make Cloud banner sticky

* Update Header top position when Cloud banner is closed

* Adjust header height based on visible banners
  • Loading branch information
Strift authored Nov 28, 2024
1 parent 412cba0 commit aac90d6
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 72 deletions.
23 changes: 23 additions & 0 deletions cypress/e2e/cloud-banner.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const SELECTOR = '.cloud-banner'

describe('Cloud Banner', () => {
it('should show cloud banner by default', () => {
cy.visit('/')
cy.get(SELECTOR).should('be.visible')
})

it('should hide cloud banner when cloud_banner=false query param is present', () => {
cy.visit('/?cloud_banner=false')
cy.get(SELECTOR).should('not.exist')
})

it('should show cloud banner with other query params when cloud_banner is not false', () => {
cy.visit('/?cloud_banner=true&other_param=123')
cy.get(SELECTOR).should('be.visible')
})

it('should show cloud banner with invalid cloud_banner values', () => {
cy.visit('/?cloud_banner=invalid')
cy.get(SELECTOR).should('be.visible')
})
})
29 changes: 24 additions & 5 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import Modal from 'components/Modal'
import NoMeilisearchRunning from 'components/NoMeilisearchRunning'
import ApiKeyAwarenessBanner from 'components/ApiKeyAwarenessBanner'
import getIndexesListWithStats from 'utils/getIndexesListWithStats'
import shouldDisplayCloudBanner from 'utils/shouldDisplayCloudBanner'
import isCloudBannerEnabled from 'utils/isCloudBannerEnabled'
import shouldDisplayApiKeyModal from 'utils/shouldDisplayApiKeyModal'
import hasAnApiKeySet from 'utils/hasAnApiKeySet'
import clientAgents from './version/client-agents'
Expand Down Expand Up @@ -78,9 +78,8 @@ const App = () => {
}, [])

useEffect(() => {
const shouldCloudBannerBeDisplayed = shouldDisplayCloudBanner()
if (shouldCloudBannerBeDisplayed) {
setShowCloudBanner(shouldCloudBannerBeDisplayed)
if (isCloudBannerEnabled()) {
setShowCloudBanner(true)
}
getApiKeyFromUrl()
}, [])
Expand Down Expand Up @@ -115,6 +114,20 @@ const App = () => {
onClientUpdate()
}, [meilisearchJsClient])

const handleCloudBannerClose = () => {
setShowCloudBanner(false)
localStorage.setItem('bannerVisibility', JSON.stringify(false))
}

// Retrieve the banner visibility state from local storage on component mount
React.useEffect(() => {
const storedVisibility = localStorage.getItem('bannerVisibility')
if (storedVisibility) {
setShowCloudBanner(JSON.parse(storedVisibility))
}
return () => {}
}, [])

return (
<ApiKeyContext.Provider value={{ apiKey, setApiKey }}>
<Wrapper>
Expand All @@ -123,7 +136,12 @@ const App = () => {
onClose={() => setIsApiKeyBannerVisible(false)}
/>
)}
{showCloudBanner && <CloudBanner />}
{showCloudBanner && (
<CloudBanner
handleBannerClose={handleCloudBannerClose}
isBannerVisible={showCloudBanner}
/>
)}
{isMeilisearchRunning ? (
<Body
currentIndex={currentIndex}
Expand All @@ -132,6 +150,7 @@ const App = () => {
requireApiKeyToWork={requireApiKeyToWork}
getIndexesList={getIndexesList}
isApiKeyBannerVisible={isApiKeyBannerVisible}
isCloudBannerVisible={showCloudBanner}
/>
) : (
<NoMeilisearchRunning />
Expand Down
4 changes: 3 additions & 1 deletion src/components/Body.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const Body = ({
setCurrentIndex,
requireApiKeyToWork,
isApiKeyBannerVisible,
isCloudBannerVisible,
}) => {
const { meilisearchJsClient, instantMeilisearchClient } =
useMeilisearchClientContext()
Expand All @@ -50,7 +51,8 @@ const Body = ({
requireApiKeyToWork={requireApiKeyToWork}
client={meilisearchJsClient}
refreshIndexes={getIndexesList}
isBannerVisible={isApiKeyBannerVisible}
isApiKeyBannerVisible={isApiKeyBannerVisible}
isCloudBannerVisible={isCloudBannerVisible}
/>
<BodyWrapper>
{/* <Sidebar /> */}
Expand Down
85 changes: 29 additions & 56 deletions src/components/CloudBanner.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,68 +24,41 @@ const CloudBannerWrapper = styled.div`
top: 0;
height: 74px;
box-shadow: 0px 0px 30px ${(p) => Color(p.theme.colors.gray[0]).alpha(0.15)};
z-index: 3;
z-index: 10;
padding: 4px;
`

const CloudBanner = () => {
const [isBannerVisible, setIsBannerVisible] = React.useState(true)
const CloudBanner = ({ handleBannerClose, isBannerVisible }) => (
<>
{isBannerVisible && (
<CloudBannerWrapper className="cloud-banner">
<Container display="flex" flexDirection="column" alignContent="center">
<Typography variant="typo14" color="white">
Scale up with Meilisearch Cloud 🚀
</Typography>

const handleBannerClose = () => {
setIsBannerVisible(false)
localStorage.setItem('bannerVisibility', JSON.stringify(false))
}

// Retrieve the banner visibility state from local storage on component mount
React.useEffect(() => {
const storedVisibility = localStorage.getItem('bannerVisibility')
if (storedVisibility) {
setIsBannerVisible(JSON.parse(storedVisibility))
}

return () => {}
}, [])

return (
<>
{isBannerVisible && (
<CloudBannerWrapper>
<Container
display="flex"
flexDirection="column"
alignContent="center"
>
<Typography variant="typo14" color="white">
Supercharge your Meilisearch experience
</Typography>

<Typography variant="typo15" color="white">
Say goodbye to server management, and manual updates with{' '}
<Link
href="https://www.meilisearch.com/cloud?utm_campaign=oss&utm_source=integration&utm_medium=minidashboard"
color="white"
>
<Typography variant="typo14" color="white">
Meilisearch Cloud
</Typography>
</Link>
.&nbsp;
<Typography variant="typo15" color="white">
Faster, smarter search—no maintenance needed.{' '}
<Link
href="https://www.meilisearch.com/cloud?utm_campaign=oss&utm_source=integration&utm_medium=minidashboard"
color="white"
>
<Typography variant="typo14" color="white">
Get started with a 14-day free trial! No credit card required.
Start free
</Typography>
</Link>
<Typography variant="typo14" color="white">
{' '}
with no commitment.
</Typography>
<Button
color="gray.9"
aria-label="close"
onClick={handleBannerClose}
>
<Cross style={{ width: 10 }} />
</Button>
</Container>
</CloudBannerWrapper>
)}
</>
)
}
</Typography>
<Button color="gray.9" aria-label="close" onClick={handleBannerClose}>
<Cross style={{ width: 10 }} />
</Button>
</Container>
</CloudBannerWrapper>
)}
</>
)

export default CloudBanner
9 changes: 6 additions & 3 deletions src/components/Header/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const HeaderWrapper = styled('div')(compose(position), {
position: 'sticky',
height: '120px',
boxShadow: `0px 0px 30px ${(p) => Color(p.theme.colors.gray[0]).alpha(0.15)}`,
zIndex: 3,
zIndex: 10,
})

const ApiKey = ({ requireApiKeyToWork }) => {
Expand Down Expand Up @@ -71,7 +71,8 @@ const Header = ({
setCurrentIndex,
refreshIndexes,
requireApiKeyToWork,
isBannerVisible,
isApiKeyBannerVisible,
isCloudBannerVisible,
}) => {
const { meilisearchJsClient } = useMeilisearchClientContext()
const [version, setVersion] = React.useState()
Expand All @@ -89,8 +90,10 @@ const Header = ({
getMeilisearchVersion()
}, [meilisearchJsClient])

const topPosition =
(isCloudBannerVisible ? 74 : 0) + (isApiKeyBannerVisible ? 55 : 0)
return (
<HeaderWrapper top={isBannerVisible ? 55 : 0}>
<HeaderWrapper top={topPosition}>
<Container
p={4}
display="flex"
Expand Down
14 changes: 14 additions & 0 deletions src/utils/isCloudBannerEnabled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/** @const {string} Name of the query parameter that controls banner visibility */
const QUERY_PARAM_NAME = 'cloud_banner'

/**
* Checks if the cloud banner should be enabled based on URL query parameters
* @returns {boolean} True if banner should be shown, false if explicitly disabled via query param
*/
const isBannerEnabled = () => {
const urlParams = new URLSearchParams(window.location.search)
const cloudBannerQueryParam = urlParams.get(QUERY_PARAM_NAME)
return cloudBannerQueryParam !== 'false'
}

export default isBannerEnabled
7 changes: 0 additions & 7 deletions src/utils/shouldDisplayCloudBanner.js

This file was deleted.

0 comments on commit aac90d6

Please sign in to comment.