Skip to content

Commit

Permalink
feat: add banner about cloud offering (#3077)
Browse files Browse the repository at this point in the history
  • Loading branch information
markphelps authored May 13, 2024
1 parent 057191a commit 1cc2ee4
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 16 deletions.
10 changes: 10 additions & 0 deletions ui/src/app/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@ import {
useListNamespacesQuery
} from './namespaces/namespacesSlice';
import CommandDialog from '~/components/command/CommandDialog';
import Banner from '~/components/Banner';
import { selectDismissedBanner } from './events/eventSlice';

function InnerLayout() {
const { session } = useSession();
const [sidebarOpen, setSidebarOpen] = useState(false);

const dismissedBanner = useSelector(selectDismissedBanner);
const dispatch = useAppDispatch();

const { namespaceKey } = useParams();
Expand Down Expand Up @@ -75,6 +78,13 @@ function InnerLayout() {
<Sidebar setSidebarOpen={setSidebarOpen} sidebarOpen={sidebarOpen} />
<div className="bg-white flex min-h-screen flex-col md:pl-64">
<Header setSidebarOpen={setSidebarOpen} />
{!dismissedBanner && (
<Banner
title="Introducing Flipt Hybrid Cloud"
description="Learn about our latest offering that enhances Flipt Open Source with managed security and support"
href="https://docs.flipt.io/cloud/overview"
/>
)}
<main className="flex px-6 py-10">
<div className="w-full overflow-x-auto px-4 sm:px-6 lg:px-8">
<Outlet />
Expand Down
17 changes: 12 additions & 5 deletions ui/src/app/Onboarding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
AcademicCapIcon,
BookOpenIcon,
ChatBubbleLeftIcon,
CloudIcon,
CodeBracketIcon,
CommandLineIcon,
PuzzlePieceIcon,
Expand All @@ -20,28 +21,34 @@ const gettingStartedTiles = [
icon: AcademicCapIcon,
name: 'Get Started',
description: 'Learn how to create your first feature flag',
href: 'https://docs.flipt.io/introduction'
},
{
icon: CloudIcon,
name: 'Introducing Flipt Hybrid Cloud',
className: 'sm:col-span-2',
href: 'https://www.flipt.io/docs/introduction'
description:
'Learn about our managed offering with enhanced security and support',
href: 'https://docs.flipt.io/cloud/overview'
},
{
icon: CommandLineIcon,
name: 'Try the CLI',
description: 'Use the Flipt CLI to manage your feature flags and more',
href: 'https://www.flipt.io/docs/cli/overview'
href: 'https://docs.flipt.io/cli/overview'
},
{
icon: BookOpenIcon,
name: 'Checkout a Guide',
description:
'Use Flipt to its full potential. Read our guides including using Flipt with GitOps',
href: 'https://www.flipt.io/docs/guides'
href: 'https://docs.flipt.io/guides'
},
{
icon: PuzzlePieceIcon,
name: 'Integrate Your Application',
description: 'Use our SDKs to integrate your applications in your language',
className: 'sm:col-span-2',
href: 'https://www.flipt.io/docs/integration/overview'
href: 'https://docs.flipt.io/integration/overview'
}
];

Expand Down
14 changes: 12 additions & 2 deletions ui/src/app/events/eventSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import { RootState } from '~/store';
export const eventKey = 'event';
interface IEventState {
completedOnboarding: boolean;
dismissedBanner: boolean;
}

const initialState: IEventState = {
completedOnboarding: false
completedOnboarding: false,
dismissedBanner: false
};

export const eventSlice = createSlice({
Expand All @@ -16,15 +18,23 @@ export const eventSlice = createSlice({
reducers: {
onboardingCompleted: (state) => {
state.completedOnboarding = true;
},
bannerDismissed: (state) => {
state.dismissedBanner = true;
}
}
});

export const { onboardingCompleted } = eventSlice.actions;
export const { onboardingCompleted, bannerDismissed } = eventSlice.actions;

export const selectCompletedOnboarding = createSelector(
[(state: RootState) => state.user],
(user) => user.completedOnboarding
);

export const selectDismissedBanner = createSelector(
[(state: RootState) => state.user],
(user) => user.dismissedBanner
);

export default eventSlice.reducer;
44 changes: 44 additions & 0 deletions ui/src/components/Banner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { XMarkIcon } from '@heroicons/react/20/solid';
import { useDispatch } from 'react-redux';
import { bannerDismissed } from '~/app/events/eventSlice';

type BannerProps = {
title: string;
description: string;
href: string;
};

export default function Banner(props: BannerProps) {
const { title, description, href } = props;

const dispatch = useDispatch();

return (
<div className="bg-violet-700 flex items-center gap-x-6 px-6 py-2.5 sm:px-3.5 sm:before:flex-1">
<p className="text-white text-sm leading-6">
<a href={href}>
<strong className="font-semibold">{title}</strong>
<svg
viewBox="0 0 2 2"
className="mx-2 inline h-0.5 w-0.5 fill-current"
aria-hidden="true"
>
<circle cx={1} cy={1} r={1} />
</svg>
{description}&nbsp;
<span aria-hidden="true">&rarr;</span>
</a>
</p>
<div className="flex flex-1 justify-end">
<button
type="button"
className="-m-3 p-3 focus-visible:outline-offset-[-4px]"
onClick={() => dispatch(bannerDismissed())}
>
<span className="sr-only">Dismiss</span>
<XMarkIcon className="text-white h-5 w-5" aria-hidden="true" />
</button>
</div>
</div>
);
}
5 changes: 4 additions & 1 deletion ui/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ import { eventSlice, eventKey } from './app/events/eventSlice';
const listenerMiddleware = createListenerMiddleware();

listenerMiddleware.startListening({
matcher: isAnyOf(eventSlice.actions.onboardingCompleted),
matcher: isAnyOf(
eventSlice.actions.onboardingCompleted,
eventSlice.actions.bannerDismissed
),
effect: (_action, api) => {
// save to local storage
localStorage.setItem(
Expand Down
21 changes: 13 additions & 8 deletions ui/tests/onboarding.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,29 @@ test.describe('Onboarding', () => {

await expect(page.locator('h1')).toContainText('Onboarding');
await expect(
page.getByText('Get Started', { exact: true })
page.getByRole('heading', { name: 'Get Started' })
).toBeVisible();
await expect(
page.getByText('Try the CLI', { exact: true })
page.getByRole('heading', { name: 'Introducing Flipt Hybrid Cloud' })
).toBeVisible();
await expect(
page.getByText('Checkout a Guide', { exact: true })
page.getByRole('heading', { name: 'Try the CLI' })
).toBeVisible();
await expect(
page.getByText('Integrate Your Application', { exact: true })
page.getByRole('heading', { name: 'Checkout a Guide' })
).toBeVisible();
await expect(
page.getByText('Join the Community', { exact: true })
page.getByRole('heading', { name: 'Integrate Your Application' })
).toBeVisible();
await expect(
page.getByText('View API Reference', { exact: true })
page.getByRole('heading', { name: 'Join the Community' })
).toBeVisible();
await expect(
page.getByRole('heading', { name: 'View API Reference' })
).toBeVisible();
await expect(
page.getByRole('heading', { name: 'Support Us' })
).toBeVisible();
await expect(page.getByText('Support Us', { exact: true })).toBeVisible();
await expect(
page.getByRole('button', { name: 'Continue to Dashboard' })
).toBeVisible();
Expand All @@ -41,7 +46,7 @@ test.describe('Onboarding', () => {
test.describe('user navigates to the onboarding page', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.getByRole('link', { name: 'Support' }).click();
await page.getByRole('link', { name: 'Support', exact: true }).click();
await page.getByRole('heading', { name: 'Onboarding' }).click();
await page.getByRole('link', { name: "Let's Go" }).click();
});
Expand Down

0 comments on commit 1cc2ee4

Please sign in to comment.