Skip to content

Commit

Permalink
feat : add studio tour for first time user (#1120)
Browse files Browse the repository at this point in the history
Co-authored-by: asyncapi-bot <[email protected]>
  • Loading branch information
aialok and asyncapi-bot authored Sep 23, 2024
1 parent 1608159 commit fa714db
Show file tree
Hide file tree
Showing 27 changed files with 4,831 additions and 4,431 deletions.
2 changes: 1 addition & 1 deletion .sonarcloud.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Disable specific file since it would introduce more complexity to reduce it - mainly code complexity and complex template literals
sonar.exclusions=apps/studio/public/js/monaco/**,apps/studio/src/tailwind.css,apps/studio/src/components/SplitPane/**,apps/studio-next/cypress/**,apps/studio-next/Dockerfile
# Disable duplicate code in tests since it would introduce more complexity to reduce it.
sonar.cpd.exclusions=apps/studio/**,apps/studio-next/src/components/Navigationv3.tsx,apps/studio-next/src/components/Navigation.tsx,apps/studio-next/cypress/**
sonar.cpd.exclusions=apps/studio/**,apps/studio-next/src/components/Navigationv3.tsx,apps/studio-next/src/components/Navigation.tsx,apps/studio-next/cypress/**,apps/studio-next/src/helpers/driver.ts
2 changes: 2 additions & 0 deletions apps/studio-next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
"@types/react": "18.2.18",
"@types/react-dom": "18.2.7",
"autoprefixer": "10.4.14",
"codemirror": "^6.0.1",
"driver.js": "^1.3.1",
"eslint-config-next": "13.4.12",
"js-base64": "^3.7.3",
"js-file-download": "^0.4.12",
Expand Down
63 changes: 63 additions & 0 deletions apps/studio-next/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,66 @@
.tippy-box[data-placement^="right"] > .tippy-arrow:before {
border-right-color: rgba(17, 24, 39) !important;
}
.driver-popover.driverjs-theme {
background-color: #1a1a1a;
border: 1px solid #333333;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
}

.driver-popover.driverjs-theme .driver-popover-title {
font-size: 18px;
font-weight: bold;
color: #db2777;
}

.driver-popover.driverjs-theme .driver-popover-description {
font-size: 14px;
color: #cccccc;
font-family: sans-serif;
}

.driver-popover.driverjs-theme button {
background-color: #db2777;
color: #ffffff !important;
border: none !important;
font-size: 14px;
padding: 8px 20px;
border-radius: 4px;
margin-left: 10px;
text-shadow: none;
font-weight: bold;
text-align: center;
transition: background-color 0.3s ease;
}

.driver-popover.driverjs-theme button:not(.driver-popover-close-btn):hover {
background-color: #c81c67;
cursor: pointer;
}

.driver-popover.driverjs-theme .driver-popover-close-btn {
color: #999999;
font-size: 20px;
background-color: transparent;
top: 13px;
right: 5px;
padding: 0%;
cursor: pointer;
}

.driver-popover.driverjs-theme .driver-popover-arrow-side-left.driver-popover-arrow {
border-left-color: #1a1a1a;
}

.driver-popover.driverjs-theme .driver-popover-arrow-side-right.driver-popover-arrow {
border-right-color: #1a1a1a;
}

.driver-popover.driverjs-theme .driver-popover-arrow-side-top.driver-popover-arrow {
border-top-color: #1a1a1a;
}

.driver-popover.driverjs-theme .driver-popover-arrow-side-bottom.driver-popover-arrow {
border-bottom-color: #1a1a1a;
}
2 changes: 1 addition & 1 deletion apps/studio-next/src/components/Editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const Editor: React.FunctionComponent<EditorProps> = () => {
size={editorHeight}
defaultSize={editorHeight}
>
<div className="flex flex-1 flex-col h-full">
<div className="flex flex-1 flex-col h-full overflow-hidden" id="editor">
<EditorSidebar />
<MonacoWrapper />
</div>
Expand Down
2 changes: 1 addition & 1 deletion apps/studio-next/src/components/Editor/EditorSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const EditorSidebar: React.FunctionComponent<
className="flex flex-row items-center"
style={{ height: '30px', lineHeight: '30px' }}
>
<div>
<div id="editor-dropdown">
<ul className="flex">
<li>
<ShareButton />
Expand Down
2 changes: 1 addition & 1 deletion apps/studio-next/src/components/Navigationv3.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ export const Navigationv3: React.FunctionComponent<NavigationProps> = ({

const components = document.components();
return (
<div className={`flex flex-none flex-col overflow-y-auto overflow-x-hidden bg-gray-800 h-full ${className}`}>
<div className={`flex flex-none flex-col overflow-y-auto overflow-x-hidden bg-gray-800 h-full ${className}`} id="navigation-panel">
<ul>
<li className="mb-4">
<div
Expand Down
23 changes: 20 additions & 3 deletions apps/studio-next/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { VscListSelection, VscCode, VscOpenPreview, VscGraph, VscNewFile, VscSettingsGear } from 'react-icons/vsc';
import { VscListSelection, VscCode, VscOpenPreview, VscGraph, VscNewFile, VscSettingsGear, VscPlayCircle } from 'react-icons/vsc';
import { show as showModal } from '@ebay/nice-modal-react';

import { Tooltip } from './common';
Expand All @@ -8,6 +8,7 @@ import { usePanelsState, panelsState, useDocumentsState } from '@/state';

import type { FunctionComponent, ReactNode } from 'react';
import type { PanelsState } from '@/state/panels.state';
import { driverObj } from '@/helpers/driver';

function updateState(panelName: keyof PanelsState['show'], type?: PanelsState['secondaryPanelType']) {
const settingsState = panelsState.getState();
Expand Down Expand Up @@ -120,8 +121,13 @@ export const Sidebar: FunctionComponent<SidebarProps> = () => {

navigation = navigation.filter(item => item.enabled);

const driverTourHandler = () => {
const getCurrentTourStep = localStorage.getItem('currentTourStep');
driverObj.drive(parseInt(getCurrentTourStep ?? '0', 10));
};

return (
<div className="flex flex-col bg-gray-800 shadow-lg border-r border-gray-700 justify-between">
<div className="flex flex-col bg-gray-800 shadow-lg border-r border-gray-700 justify-between" id="sidebar">
<div className="flex flex-col">
{navigation.map(item => (
<Tooltip content={item.tooltip} placement='right' hideOnClick={true} key={item.name}>
Expand All @@ -139,14 +145,25 @@ export const Sidebar: FunctionComponent<SidebarProps> = () => {
))}
</div>
<div className="flex flex-col">
<Tooltip content='Start Tour' placement='right' hideOnClick={true}>
<button
title="Start Tour"
className='flex text-gray-500 hover:text-white focus:outline-none border-box p-4'
type="button"
onClick={driverTourHandler}
>
<VscPlayCircle className="w-6 h-6" />
</button>
</Tooltip>
<Tooltip content='Studio settings' placement='right' hideOnClick={true}>
<button
className='flex text-gray-500 hover:text-white focus:outline-none border-box p-4'
type="button"
onClick={() => showModal(SettingsModal)}
id="studio-setting"
data-test="button-settings"
>
<VscSettingsGear className="w-5 h-5" />
<VscSettingsGear className="w-5 h-5" />
</button>
</Tooltip>
</div>
Expand Down
10 changes: 9 additions & 1 deletion apps/studio-next/src/components/StudioWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { createServices, Services, ServicesProvider } from '@/services';

import { AsyncAPIStudio } from './CodeEditor';
import Preloader from './Preloader';
import { driverObj } from '@/helpers/driver';

function configureMonacoEnvironment() {
if (typeof window !== 'undefined') {
Expand Down Expand Up @@ -34,8 +35,15 @@ export default function StudioWrapper() {
useEffect(() => {
const fetchData = async () => {
const servicess = await createServices();
setServices(servicess)
setServices(servicess);
configureMonacoEnvironment();
const alreadyVisitedSession = sessionStorage.getItem('alreadyVisited');
const alreadyVisitedLocal = localStorage.getItem('alreadyVisited');
if (!alreadyVisitedSession && !alreadyVisitedLocal) {
sessionStorage.setItem('alreadyVisited', 'true');
localStorage.setItem('alreadyVisited', 'true');
driverObj.drive();
}
};

fetchData();
Expand Down
2 changes: 1 addition & 1 deletion apps/studio-next/src/components/Template/HTMLWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const HTMLWrapper: React.FunctionComponent<HTMLWrapperProps> = () => {
return (
parsedSpec && (
<div className="flex flex-1 flex-col h-full overflow-hidden">
<div className="overflow-auto">
<div className="overflow-auto" id="html-preview">
<AsyncApiComponentWP
schema={parsedSpec}
config={{
Expand Down
2 changes: 1 addition & 1 deletion apps/studio-next/src/components/Terminal/Terminal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const Terminal: React.FunctionComponent<TerminalProps> = () => {
];

return (
<div className="bg-gray-800 border-t border-gray-700 flex-grow relative h-full overflow-hidden">
<div className="bg-gray-800 border-t border-gray-700 flex-grow relative h-full overflow-hidden" id="terminal">
<TerminalTabs tabs={tabs} active="problems" />
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion apps/studio-next/src/components/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function Toolbar() {
</span>
</div>
</div>
<ul className="flex items-center text-pink-500 mr-2">
<ul className="flex items-center text-pink-500 mr-2" id='communicate'>
<li className="text-xl opacity-75 hover:opacity-100" data-test="button-website">
<a href='https://asyncapi.com' title='AsyncAPI Website' target='_blank' rel="noreferrer">
<IoGlobeOutline />
Expand Down
153 changes: 153 additions & 0 deletions apps/studio-next/src/helpers/driver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { driver } from 'driver.js';
import 'driver.js/dist/driver.css';

export const driverObj = driver({
popoverClass: 'driverjs-theme',
showProgress: true,
showButtons: ['next', 'previous', 'close'],
nextBtnText: 'Next',
prevBtnText: 'Back',

steps: [
{
element: '#tour-start',
popover: {
title: 'Welcome to AsyncAPI Studio',
description:
'Discover a powerful tool for designing, documenting, and managing AsyncAPI-based applications. This tour will guide you through key features to enhance your AsyncAPI development workflow.',
side: 'left',
align: 'start',
onPopoverRender: () => {
const {activeIndex} = driverObj.getState();
localStorage.setItem('currentTourStep', activeIndex?.toString() || '0');
},
},
},
{
element: '#navbar',
popover: {
title: 'Control Center',
description:
'This control center allows you to toggle the editor, navigation panel, and HTML preview on or off. It\'s also your gateway to creating new AsyncAPI templates for various protocols like Apache Kafka, WebSocket, HTTP, and more. Customize your workspace and jumpstart your AsyncAPI design process from here.',
side: 'left',
align: 'start',
onPopoverRender: () => {
const {activeIndex} = driverObj.getState();
localStorage.setItem('currentTourStep', activeIndex?.toString() || '0');
},
},
},
{
element: '#navigation-panel',
popover: {
title: 'Navigation Panel',
description:
'Explore your API structure using this navigation panel. Quickly access Servers, Channels, Operations, Messages, and Schemas - the building blocks of your AsyncAPI document.',
side: 'left',
align: 'start',
onPopoverRender: () => {
const {activeIndex} = driverObj.getState();
localStorage.setItem('currentTourStep', activeIndex?.toString() || '0');
},
},
},
{
element: '#editor',
popover: {
title: 'The Powerful Editor',
description:
'Create and edit your AsyncAPI documents with ease. Enjoy features like syntax highlighting, auto-completion, and real-time validation to streamline your API design process.',
side: 'bottom',
align: 'start',
onPopoverRender: () => {
const {activeIndex} = driverObj.getState();
localStorage.setItem('currentTourStep', activeIndex?.toString() || '0');
},
},
},
{
element: '#editor-dropdown',
popover: {
title: 'Share and Editor Options',
description:
'Collaborate on your work and access document management tools. Import, export, and convert your API specifications with just a few clicks.',
side: 'top',
align: 'start',
onPopoverRender: () => {
const {activeIndex} = driverObj.getState();
localStorage.setItem('currentTourStep', activeIndex?.toString() || '0');
},
},
},
{
element: '#terminal',
popover: {
title: 'Terminal',
description:
'Quickly identify and resolve issues in your specification. View errors, warnings, and helpful messages to ensure your API documentation is error-free.',
side: 'bottom',
align: 'start',
onPopoverRender: () => {
const {activeIndex} = driverObj.getState();
localStorage.setItem('currentTourStep', activeIndex?.toString() || '0');
},
},
},
{
element: '#html-preview',
popover: {
title: 'Instant HTML Preview',
description:
'See your API documentation come to life in real-time. This panel renders a human-readable version of your specification as you make changes.',
side: 'top',
align: 'start',
onPopoverRender: () => {
const {activeIndex} = driverObj.getState();
localStorage.setItem('currentTourStep', activeIndex?.toString() || '0');
},
},
},
{
element: '#studio-setting',
popover: {
title: 'Studio Settings',
description:
'Customize your AsyncAPI Studio experience. Adjust preferences and settings to tailor the tool to your workflow.',
side: 'top',
align: 'start',
onPopoverRender: () => {
const {activeIndex} = driverObj.getState();
localStorage.setItem('currentTourStep', activeIndex?.toString() || '0');
},
},
},
{
element: '#communicate',
popover: {
title: 'Join AsyncAPI Community',
description:
'Connect with fellow AsyncAPI developers. Join our Slack community to share ideas, get help, and stay updated on AsyncAPI news and events.',
side: 'top',
align: 'start',
onPopoverRender: () => {
const {activeIndex} = driverObj.getState();
localStorage.setItem('currentTourStep', activeIndex?.toString() || '0');
},
},
},
{
element: '#Thank-you',
popover: {
title: 'Thank You',
description:
'Thanks for exploring AsyncAPI Studio. We hope you find it valuable for your API projects. Feel free to reach out with any questions or feedback. Happy coding!',
side: 'top',
align: 'start',
onPopoverRender: () => {
localStorage.setItem('currentTourStep', '0');
},
},
},
],
});

2 changes: 1 addition & 1 deletion apps/studio-next/src/state/other.state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export type OtherState = {
}

export const otherState = create<OtherState>(() => ({
editorHeight: 'calc(100% - 36px)',
editorHeight: 'calc(100% - 161px)',
templateRerender: false,
}));

Expand Down
Loading

0 comments on commit fa714db

Please sign in to comment.