Tour feature (#716)

* setup tour page and implemented react-tutorial

* completd tour-feature implementation
This commit is contained in:
Shailesh Parmar 2021-10-11 08:56:20 +05:30 committed by GitHub
parent be7437c820
commit ec51fadf19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 213 additions and 6 deletions

View File

@ -17600,6 +17600,17 @@
"prop-types": "^15.6.2"
}
},
"react-tutorial": {
"version": "https://github.com/deuex-solutions/react-tutorial/tarball/master",
"integrity": "sha512-QFHGepStGMR15zt3lRTqZB3NT6AmpBEHkxM6hi3fW6R86Jgu5y2ZYVY0fu93KDsAou/1saTUtkSJjs0rzwDKRA==",
"requires": {
"classnames": "^2.2.6",
"lodash": "^4.17.15",
"prop-types": "^15.7.2",
"scroll-smooth": "^1.1.0",
"scrollparent": "^2.0.1"
}
},
"reactjs-localstorage": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/reactjs-localstorage/-/reactjs-localstorage-1.0.1.tgz",
@ -18594,6 +18605,16 @@
"compute-scroll-into-view": "^1.0.17"
}
},
"scroll-smooth": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/scroll-smooth/-/scroll-smooth-1.1.0.tgz",
"integrity": "sha512-68OUOXKN/ykM/Dbp4Lhza3O9QQUuW/c01WTsZzDOUyVgb1I5QjT/awOHCCbuYTSV1QnExUQ9w+KcxmVxlXIiAg=="
},
"scrollparent": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/scrollparent/-/scrollparent-2.0.1.tgz",
"integrity": "sha1-cV1bnMV3YPsivczDvvtb/gaxoxc="
},
"select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",

View File

@ -80,7 +80,8 @@
"stream-http": "^3.2.0",
"styled-components": "^5.2.3",
"tailwindcss": "^2.1.4",
"to-arraybuffer": "^1.0.1"
"to-arraybuffer": "^1.0.1",
"react-tutorial": "https://github.com/deuex-solutions/react-tutorial/tarball/master"
},
"scripts": {
"start": "NODE_ENV=development BABEL_ENV=development webpack serve --config ./webpack.config.dev.js --env development",

View File

@ -36,6 +36,8 @@ class AppState {
inPageSearchText = '';
explorePageTab = 'tables';
isTourOpen = false;
constructor() {
makeAutoObservable(this);
}

View File

@ -126,6 +126,7 @@ export const FirstTimeUserModal: FunctionComponent<Props> = ({
) : (
<Button
className="tw-text-primary-active"
id="next"
size="regular"
theme="primary"
variant="text"

View File

@ -47,6 +47,7 @@ import SVGIcons, { Icons } from '../../utils/SvgUtils';
import DropDown from '../dropdown/DropDown';
import { WhatsNewModal } from '../Modals/WhatsNewModal';
import { COOKIE_VERSION } from '../Modals/WhatsNewModal/whatsNewData';
import Tour from '../tour/Tour';
import { ReactComponent as IconDefaultUserProfile } from './../../assets/svg/ic-default-profile.svg';
import SearchOptions from './SearchOptions';
import Suggestions from './Suggestions';
@ -69,6 +70,7 @@ const Appbar: React.FC = (): JSX.Element => {
});
const [version, setVersion] = useState<string>('');
const navStyle = (value: boolean) => {
if (value) return { color: activeLink };
@ -167,7 +169,7 @@ const Appbar: React.FC = (): JSX.Element => {
<div className="tw-h-14 tw-py-2 tw-px-5 tw-border-b-2 tw-border-separator">
<div className="tw-flex tw-items-center tw-flex-row tw-justify-between tw-flex-nowrap">
<div className="tw-flex tw-items-center tw-flex-row tw-justify-between tw-flex-nowrap tw-mr-auto">
<NavLink to="/">
<NavLink id="openmetadata_logo" to="/">
<SVGIcons
alt="OpenMetadata Logo"
icon={Icons.LOGO_SMALL}
@ -180,6 +182,7 @@ const Appbar: React.FC = (): JSX.Element => {
<span className="fa fa-search tw-absolute tw-block tw-z-10 tw-w-9 tw-h-8 tw-leading-8 tw-text-center tw-pointer-events-none tw-text-gray-400" />
<input
className="tw-relative search-grey tw-rounded tw-border tw-border-main tw-bg-body-main focus:tw-outline-none tw-pl-8 tw-py-1"
id="searchBox"
type="text"
value={searchValue || ''}
onChange={(e) => {
@ -224,6 +227,7 @@ const Appbar: React.FC = (): JSX.Element => {
<NavLink
className="tw-nav focus:tw-no-underline"
data-testid="appbar-item"
id="explore"
style={navStyle(location.pathname.startsWith('/explore'))}
to={{
pathname: '/explore',
@ -249,6 +253,20 @@ const Appbar: React.FC = (): JSX.Element => {
/>
<span>What&#39;s new</span>
</button>
<NavLink
className="tw-nav focus:tw-no-underline hover:tw-underline"
style={navStyle(location.pathname.startsWith('/explore'))}
to={{
pathname: '/tour',
}}>
<SVGIcons
alt="Doc icon"
className="tw-align-middle tw--mt-0.5 tw-mr-1"
icon={Icons.WHATS_NEW}
width="16"
/>
<span>Tour</span>
</NavLink>
<div>
<DropDown
dropDownList={supportLinks}
@ -319,6 +337,7 @@ const Appbar: React.FC = (): JSX.Element => {
)}
</div>
) : null}
<Tour />
</>
);
};

View File

@ -149,6 +149,7 @@ const Suggestions = ({ searchText, isOpen, setIsOpen }: SuggestionProp) => {
<Link
className="tw-block tw-px-4 tw-py-2 tw-text-sm"
data-testid="data-name"
id={fqdn.replace(/\./g, '')}
to={getEntityLink(index, fqdn)}
onClick={() => setIsOpen(false)}>
{name}
@ -265,7 +266,7 @@ const Suggestions = ({ searchText, isOpen, setIsOpen }: SuggestionProp) => {
aria-labelledby="menu-button"
aria-orientation="vertical"
className="tw-origin-top-right tw-absolute tw-z-10
tw-w-60 tw-mt-1 tw-rounded-md tw-shadow-lg
tw-w-60 tw-mt-1 tw-rounded-md tw-shadow-lg
tw-bg-white tw-ring-1 tw-ring-black tw-ring-opacity-5 focus:tw-outline-none"
role="menu">
{getEntitiesSuggestions()}

View File

@ -25,7 +25,9 @@ const TabsPane = ({ activeTab, setActiveTab, tabs }: Props) => {
return (
<div className="tw-bg-transparent tw--mx-4">
<nav className="tw-flex tw-flex-row tw-gh-tabs-container tw-px-4">
<nav
className="tw-flex tw-flex-row tw-gh-tabs-container tw-px-4"
id="tabs">
{tabs.map((tab) =>
tab.isProtected ? (
<NonAdminAction

View File

@ -383,7 +383,7 @@ const EntityTable = ({
}, []);
return (
<div className="tw-table-responsive">
<div className="tw-table-responsive" id="schemaTable">
<table className="tw-w-full" {...getTableProps()}>
<thead>
{/* eslint-disable-next-line */}

View File

@ -0,0 +1,106 @@
import { AxiosResponse } from 'axios';
import { observer } from 'mobx-react';
import React, { useEffect, useState } from 'react';
import ReactTutorial from 'react-tutorial';
import { searchData } from '../../axiosAPIs/miscAPI';
import { PAGE_SIZE } from '../../constants/constants';
import { SearchIndex } from '../../enums/search.enum';
import { useTour } from '../../hooks/useTour';
type Steps = {
content: string;
actionType: string;
position: string;
selector: string;
userTypeText?: string;
waitTimer?: number;
};
const getSteps = (value: string) => {
return [
{
content: 'Click on the next.',
actionType: 'click',
position: 'bottom',
selector: '#next',
},
{
content: 'Click on the next.',
actionType: 'click',
position: 'bottom',
selector: '#next',
},
{
content: 'Click on Explore OpenMetadata.',
actionType: 'click',
position: 'bottom',
selector: '#take-tour',
},
{
content: 'Click on explore.',
actionType: 'click',
position: 'bottom',
selector: '#explore',
},
{
content: `Type "${value}" in search box.`,
actionType: 'typing',
userTypeText: value,
position: 'bottom',
selector: '#searchBox',
},
{
content: 'Click on the table.',
actionType: 'click',
position: 'bottom',
selector: '#bigqueryshopifydim_address',
},
{
content:
'Understand the schema of the table and add description, Claim ownership. Add tags etc..',
position: 'bottom',
selector: '#tabs',
actionType: 'wait',
waitTimer: 10000,
},
{
content: 'Click here to explore more',
actionType: 'click',
position: 'bottom',
selector: '#openmetadata_logo',
},
];
};
const Tour = () => {
const { isTourOpen, handleIsTourOpen } = useTour();
const [steps, setSteps] = useState<Steps[]>([]);
useEffect(() => {
searchData('', 1, PAGE_SIZE, '', '', '', SearchIndex.TABLE).then(
(res: AxiosResponse) => {
const table = res.data.hits.hits[0];
setSteps(getSteps(table._source.table_name));
}
);
}, []);
return (
<div>
{isTourOpen ? (
<ReactTutorial
disableKeyboardNavigation
showNumber
maskColor="#302E36"
playTour={isTourOpen}
showButtons={false}
showNavigation={false}
steps={steps}
onRequestClose={() => handleIsTourOpen(false)}
/>
) : null}
</div>
);
};
export default observer(Tour);

View File

@ -100,6 +100,7 @@ export const ROUTES = {
CALLBACK: '/callback',
NOT_FOUND: '/404',
MY_DATA: '/my-data',
TOUR: '/tour',
REPORTS: '/reports',
EXPLORE: '/explore',
EXPLORE_WITH_SEARCH: `/explore/${PLACEHOLDER_ROUTE_TAB}/${PLACEHOLDER_ROUTE_SEARCHQUERY}`,

View File

@ -0,0 +1,19 @@
import { useEffect, useState } from 'react';
import AppState from '../AppState';
export const useTour = () => {
const [isTourOpen, setIsTourOpen] = useState<boolean>();
useEffect(() => {
setIsTourOpen(AppState.isTourOpen);
}, [AppState.isTourOpen]);
const handleIsTourOpen = (value: boolean) => {
AppState.isTourOpen = value;
};
return {
isTourOpen,
handleIsTourOpen,
};
};

View File

@ -0,0 +1,31 @@
import React, { useEffect, useState } from 'react';
import { FirstTimeUserModal } from '../../components/Modals/FirstTimeUserModal/FirstTimeUserModal';
import { useTour } from '../../hooks/useTour';
import MyDataPage from '../my-data';
const TourPage = () => {
const [showFirstTimeUserModal, setShowFirstTimeUserModal] = useState(true);
const { handleIsTourOpen } = useTour();
useEffect(() => {
handleIsTourOpen(true);
}, []);
const handleFirstTimeUser = () => {
setShowFirstTimeUserModal(false);
};
return (
<div>
<MyDataPage />
{showFirstTimeUserModal && (
<FirstTimeUserModal
onCancel={() => setShowFirstTimeUserModal(true)}
onSave={handleFirstTimeUser}
/>
)}
</div>
);
};
export default TourPage;

View File

@ -29,3 +29,4 @@ declare module 'react-slick';
declare module 'slick-carousel';
declare module 'react-table';
declare module 'recharts';
declare module 'react-tutorial';

View File

@ -39,12 +39,14 @@ import SwaggerPage from '../pages/swagger';
import TagsPage from '../pages/tags';
import TeamsPage from '../pages/teams';
import MyTopicDetailPage from '../pages/topic-details';
import TourPage from '../pages/tour-page';
import UsersPage from '../pages/users';
import WorkflowsPage from '../pages/workflows';
const AuthenticatedAppRouter: FunctionComponent = () => {
return (
<Switch>
<Route exact component={MyDataPage} path={ROUTES.MY_DATA} />
<Route exact component={TourPage} path={ROUTES.TOUR} />
<Route exact component={ReportsPage} path={ROUTES.REPORTS} />
<Route exact component={ExplorePage} path={ROUTES.EXPLORE} />
<Route component={ExplorePage} path={ROUTES.EXPLORE_WITH_SEARCH} />

View File

@ -31,7 +31,7 @@ module.exports = {
mode: 'development',
// Input configuration
entry: path.join(__dirname, 'src/index.js'),
entry: ['@babel/polyfill', path.join(__dirname, 'src/index.js')],
// Output configuration
output: {